From 67a24ef8c80b5920077c910eda8a2e0abc139975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=B6nsch?= Date: Thu, 2 Mar 2023 22:53:42 +0100 Subject: [PATCH 1/3] [Mime] support overwriting form encoding The data part on it's own defaults to base64 because charset is null, but in a form it's forced to 8bit, which is a good default, but it may be overwritten. --- src/Symfony/Component/Mime/Part/DataPart.php | 9 ++++-- .../Mime/Part/Multipart/FormDataPart.php | 29 +++++++++++++++---- .../Component/Mime/Tests/EmailTest.php | 1 + .../Tests/Part/Multipart/FormDataPartTest.php | 21 ++++++++++++++ 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Mime/Part/DataPart.php b/src/Symfony/Component/Mime/Part/DataPart.php index b42ecb4da102e..2132385720dd6 100644 --- a/src/Symfony/Component/Mime/Part/DataPart.php +++ b/src/Symfony/Component/Mime/Part/DataPart.php @@ -26,6 +26,9 @@ class DataPart extends TextPart private $mediaType; private $cid; + /** @internal */ + private $formEncoding; + /** * @param resource|string|File $body Use a File instance to defer loading the file until rendering */ @@ -42,6 +45,8 @@ public function __construct($body, string $filename = null, string $contentType parent::__construct($body, null, $subtype, $encoding); + $this->formEncoding = $encoding ?? '8bit'; + if (null !== $filename) { $this->filename = $filename; $this->setName($filename); @@ -49,9 +54,9 @@ public function __construct($body, string $filename = null, string $contentType $this->setDisposition('attachment'); } - public static function fromPath(string $path, string $name = null, string $contentType = null): self + public static function fromPath(string $path, string $name = null, string $contentType = null, string $encoding = null): self { - return new self(new File($path), $name, $contentType); + return new self(new File($path), $name, $contentType, $encoding); } /** diff --git a/src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php b/src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php index 54a7c76a353a0..a0484b17cfc9a 100644 --- a/src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php +++ b/src/Symfony/Component/Mime/Part/Multipart/FormDataPart.php @@ -94,16 +94,35 @@ private function preparePart(string $name, string|TextPart $value): TextPart private function configurePart(string $name, TextPart $part): TextPart { - static $r; - - $r ??= new \ReflectionProperty(TextPart::class, 'encoding'); - $part->setDisposition('form-data'); $part->setName($name); // HTTP does not support \r\n in header values $part->getHeaders()->setMaxLineLength(\PHP_INT_MAX); - $r->setValue($part, '8bit'); + if ($part instanceof DataPart) { + // data part may specify base64 encoding + $this->configureDataPartEncoding($part); + } else { + $this->configureTextPartEncoding($part); + } return $part; } + + private function configureDataPartEncoding(DataPart $part) + { + static $r; + + $r ??= new \ReflectionProperty(DataPart::class, 'formEncoding'); + $formEncoding = $r->getValue($part); + + $this->configureTextPartEncoding($part, $formEncoding); + } + + private function configureTextPartEncoding(TextPart $part, $encoding = '8bit') + { + static $r; + + $r ??= new \ReflectionProperty(TextPart::class, 'encoding'); + $r->setValue($part, $encoding); + } } diff --git a/src/Symfony/Component/Mime/Tests/EmailTest.php b/src/Symfony/Component/Mime/Tests/EmailTest.php index c4f829d7d12de..c47fe23ec5606 100644 --- a/src/Symfony/Component/Mime/Tests/EmailTest.php +++ b/src/Symfony/Component/Mime/Tests/EmailTest.php @@ -530,6 +530,7 @@ public function testSymfonySerialize() { "filename": "test.txt", "mediaType": "application", + "formEncoding": "8bit", "body": "Some Text file", "charset": null, "subtype": "octet-stream", diff --git a/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php b/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php index 7ac3336f90666..e78b1d6a6597b 100644 --- a/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php +++ b/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php @@ -47,6 +47,27 @@ public function testConstructor() $this->assertEquals([$t, $b, $c], $f->getParts()); } + public function testConstructorWithBase64Encoding() + { + $r = new \ReflectionProperty(TextPart::class, 'encoding'); + + $d = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif', null, null, '8bit'); + $e = DataPart::fromPath($file = __DIR__.'/../../Fixtures/mimetypes/test.gif', null, null, 'base64'); + $f = new FormDataPart([ + 'foo' => clone $d, + 'bar' => clone $e + ]); + $d->setDisposition('form-data'); + $d->setName('foo'); + $d->getHeaders()->setMaxLineLength(\PHP_INT_MAX); + $r->setValue($d, '8bit'); + $e->setDisposition('form-data'); + $e->setName('bar'); + $e->getHeaders()->setMaxLineLength(\PHP_INT_MAX); + $r->setValue($e, 'base64'); + $this->assertEquals([$d, $e], $f->getParts()); + } + public function testNestedArrayParts() { $p1 = new TextPart('content', 'utf-8', 'plain', '8bit'); From ef15f4f1a74b3299b18b244dcc5bc9bd45a5bcbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=B6nsch?= Date: Fri, 3 Mar 2023 18:53:03 +0100 Subject: [PATCH 2/3] fix serialization --- src/Symfony/Component/Mime/Part/DataPart.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Mime/Part/DataPart.php b/src/Symfony/Component/Mime/Part/DataPart.php index 2132385720dd6..133c56e8feec1 100644 --- a/src/Symfony/Component/Mime/Part/DataPart.php +++ b/src/Symfony/Component/Mime/Part/DataPart.php @@ -32,7 +32,7 @@ class DataPart extends TextPart /** * @param resource|string|File $body Use a File instance to defer loading the file until rendering */ - public function __construct($body, string $filename = null, string $contentType = null, string $encoding = null) + public function __construct($body, string $filename = null, string $contentType = null, string $encoding = null, string $formEncoding = null) { unset($this->_parent); @@ -45,7 +45,7 @@ public function __construct($body, string $filename = null, string $contentType parent::__construct($body, null, $subtype, $encoding); - $this->formEncoding = $encoding ?? '8bit'; + $this->formEncoding = $formEncoding ?? '8bit'; if (null !== $filename) { $this->filename = $filename; @@ -56,7 +56,7 @@ public function __construct($body, string $filename = null, string $contentType public static function fromPath(string $path, string $name = null, string $contentType = null, string $encoding = null): self { - return new self(new File($path), $name, $contentType, $encoding); + return new self(new File($path), $name, $contentType, $encoding, $encoding); } /** From e1898c6fdc871baa5048acacc99bcaa807547201 Mon Sep 17 00:00:00 2001 From: croensch Date: Sun, 5 Mar 2023 11:41:02 +0100 Subject: [PATCH 3/3] phpdoc internal not needed --- src/Symfony/Component/Mime/Part/DataPart.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Component/Mime/Part/DataPart.php b/src/Symfony/Component/Mime/Part/DataPart.php index 133c56e8feec1..c5ca9410db224 100644 --- a/src/Symfony/Component/Mime/Part/DataPart.php +++ b/src/Symfony/Component/Mime/Part/DataPart.php @@ -26,7 +26,6 @@ class DataPart extends TextPart private $mediaType; private $cid; - /** @internal */ private $formEncoding; /**