From 94041263a017923f0f1167e9839044975640f3b4 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 2 Oct 2026 16:44:53 +0200 Subject: [PATCH] Fold long address headers addrAppend() joined all addresses onto one line, so a To, Cc, Bcc or Reply-To header with many recipients could go past the 998 character limit in RFC 5322. Fold between addresses when the next one would not fit. Short lists are unchanged. Fixes #2690 --- src/PHPMailer.php | 16 +++++++- test/PHPMailer/AddrAppendTest.php | 64 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 test/PHPMailer/AddrAppendTest.php diff --git a/src/PHPMailer.php b/src/PHPMailer.php index 547024e..d0d6c52 100644 --- a/src/PHPMailer.php +++ b/src/PHPMailer.php @@ -2647,7 +2647,21 @@ class PHPMailer $addresses[] = $this->addrFormat($address); } - return $type . ': ' . implode(', ', $addresses) . static::$LE; + //Fold between addresses so that no line exceeds the RFC 5322 limit, + //leaving room for the comma at the end of a folded line + $header = $type . ': ' . array_shift($addresses); + $lineLength = strlen($header); + foreach ($addresses as $address) { + if ($lineLength + strlen($address) + 3 > static::MAX_LINE_LENGTH) { + $header .= ',' . static::$LE . ' ' . $address; + $lineLength = strlen($address) + 1; + } else { + $header .= ', ' . $address; + $lineLength += strlen($address) + 2; + } + } + + return $header . static::$LE; } /** diff --git a/test/PHPMailer/AddrAppendTest.php b/test/PHPMailer/AddrAppendTest.php new file mode 100644 index 0000000..6761d25 --- /dev/null +++ b/test/PHPMailer/AddrAppendTest.php @@ -0,0 +1,64 @@ + + * @author Andy Prevost + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2004 - 2009 Andy Prevost + * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License + */ + +namespace PHPMailer\Test\PHPMailer; + +use PHPMailer\PHPMailer\PHPMailer; +use PHPMailer\Test\TestCase; + +/** + * Test creating address headers. + * + * @covers \PHPMailer\PHPMailer\PHPMailer::addrAppend + */ +final class AddrAppendTest extends TestCase +{ + /** + * A short address list stays on one line. + */ + public function testShortListIsNotFolded() + { + $header = $this->Mail->addrAppend('To', [['joe@example.com', 'Joe'], ['zoe@example.com', '']]); + + self::assertSame('To: Joe , zoe@example.com' . PHPMailer::CRLF, $header); + } + + /** + * A long address list is folded between addresses so no line exceeds the RFC 5322 limit. + */ + public function testLongListIsFolded() + { + $addresses = []; + $formatted = []; + for ($i = 1; $i <= 100; ++$i) { + $addresses[] = ['recipient' . $i . '@example.com', 'Recipient ' . $i]; + $formatted[] = 'Recipient ' . $i . ' '; + } + + $header = $this->Mail->addrAppend('Cc', $addresses); + $lines = explode(PHPMailer::CRLF, substr($header, 0, -strlen(PHPMailer::CRLF))); + + self::assertGreaterThan(1, count($lines), 'The header was not folded'); + foreach ($lines as $index => $line) { + self::assertLessThanOrEqual(PHPMailer::MAX_LINE_LENGTH, strlen($line), "Line $index is too long"); + if ($index > 0) { + self::assertSame(' ', $line[0], "Line $index is not a folded continuation"); + } + } + self::assertSame( + 'Cc: ' . implode(', ', $formatted) . PHPMailer::CRLF, + str_replace(PHPMailer::CRLF . ' ', ' ', $header), + 'Unfolding the header did not give back the address list' + ); + } +}