From 9c84dc68fb24743af745e5b06f4362bdcfc8331d Mon Sep 17 00:00:00 2001 From: SirLouen Date: Thu, 7 Aug 2025 11:59:17 +0200 Subject: [PATCH 1/6] Add Tests --- tests/phpunit/tests/pluggable/wpMail.php | 83 ++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/tests/phpunit/tests/pluggable/wpMail.php b/tests/phpunit/tests/pluggable/wpMail.php index 85e3b46f61a5b..74aec391cffa1 100644 --- a/tests/phpunit/tests/pluggable/wpMail.php +++ b/tests/phpunit/tests/pluggable/wpMail.php @@ -555,6 +555,89 @@ public function test_wp_mail_resets_properties() { $this->assertNotSame( 'user1', $phpmailer->AltBody ); } + /** + * Test that multiple headers with the same name are sent correctly. + * + * @ticket 30128 + * @ticket 56779 + */ + public function test_wp_mail_with_multiple_same_name_headers() { + $to = 'test@example.com'; + $subject = 'Testing Multiple Headers'; + $message = 'This is a test message.'; + $headers = array( + 'x-my-things: thing1', + 'x-my-things: thing2', + ); + + wp_mail( $to, $subject, $message, $headers ); + + $mailer = tests_retrieve_phpmailer_instance(); + $sent_header = $mailer->get_sent()->header; + + $this->assertStringContainsString( 'x-my-things: thing1', $sent_header, 'The first header was not sent.' ); + $this->assertStringContainsString( 'x-my-things: thing2', $sent_header, 'The second header was not sent.' ); + } + + /** + * Tests that headers are handled correctly in various formats. + * + * @dataProvider data_wp_mail_header_formats + * + * @ticket 30128 + */ + public function test_wp_mail_header_formats( $headers, $expected_strings ) { + wp_mail( 'test@example.com', 'Test', 'Message', $headers ); + + $mailer = tests_retrieve_phpmailer_instance(); + $sent_header = $mailer->get_sent()->header; + + foreach ( $expected_strings as $expected_string ) { + $this->assertStringContainsString( $expected_string, $sent_header ); + } + } + + /** + * Data provider for test_wp_mail_header_formats. + */ + public function data_wp_mail_header_formats() { + return array( + 'associative array' => array( + 'headers' => array( + 'From' => 'Me Myself ', + 'Cc' => 'Isaaco Harrelson ', + ), + 'expected_strings' => array( + 'From: Me Myself ', + 'Cc: Isaaco Harrelson ', + ), + ), + 'indexed array' => array( + 'headers' => array( + 'From: Me Myself ', + 'Cc: Isaaco Harrelson ', + ), + 'expected_strings' => array( + 'From: Me Myself ', + 'Cc: Isaaco Harrelson ', + ), + ), + 'associative array with multiple CCs' => array( + 'headers' => array( + 'From' => 'Me Myself ', + 'Cc' => array( + 'Isaaco Harrelson ', + 'frederick@wordpress.org', + ), + ), + 'expected_strings' => array( + 'From: Me Myself ', + 'Cc: Isaaco Harrelson , frederick@wordpress.org', + ), + ), + ); + } + /** * Tests that wp_mail() can send embedded images. * From 657f0920b6671a857944f706d216bd8586000545 Mon Sep 17 00:00:00 2001 From: SirLouen Date: Thu, 7 Aug 2025 11:59:29 +0200 Subject: [PATCH 2/6] Add First Code Version, needs more work --- src/wp-includes/formatting.php | 10 +++++--- src/wp-includes/pluggable.php | 47 ++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index e46e5d82ad590..15aac9be53d91 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -6171,13 +6171,15 @@ function wp_staticize_emoji_for_email( $mail ) { } } - foreach ( $headers as $header ) { - if ( ! str_contains( $header, ':' ) ) { - continue; + foreach ( $headers as $key => $header ) { + if ( is_string( $header ) && strpos($header, ':') === false ) { + if ( is_numeric ( $key ) ) { + continue; + } } // Explode them out. - list( $name, $content ) = explode( ':', trim( $header ), 2 ); + list( $name, $content ) = ( is_numeric ( $key ) ) ? explode( ':', trim( $header ), 2 ) : array ( $key, $header ); // Cleanup crew. $name = trim( $name ); diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index 5edd0c760cbb2..ede069cb743f8 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -306,16 +306,18 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() // If it's actually got contents. if ( ! empty( $tempheaders ) ) { // Iterate through the raw headers. - foreach ( (array) $tempheaders as $header ) { - if ( ! str_contains( $header, ':' ) ) { + foreach ( (array) $tempheaders as $key => $header ) { + if ( strpos($header, ':') === false ) { if ( false !== stripos( $header, 'boundary=' ) ) { $parts = preg_split( '/boundary=/i', trim( $header ) ); $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) ); } - continue; + if ( is_numeric( $key ) ) { + continue; + } } // Explode them out. - list( $name, $content ) = explode( ':', trim( $header ), 2 ); + list( $name, $content ) = ( is_numeric ( $key ) ) ? explode( ':', trim( $header ), 2 ) : array ( $key, $header ); // Cleanup crew. $name = trim( $name ); @@ -358,18 +360,35 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() $content_type = trim( $content ); } break; - case 'cc': - $cc = array_merge( (array) $cc, explode( ',', $content ) ); + case 'cc': + $cc = array_merge( + (array) $cc, + ( is_array( $content ) ) ? $content : explode( ',', $content ) + ); break; case 'bcc': - $bcc = array_merge( (array) $bcc, explode( ',', $content ) ); + $bcc = array_merge( + (array) $bcc, + ( is_array( $content ) ) ? $content : explode( ',', $content ) + ); break; case 'reply-to': - $reply_to = array_merge( (array) $reply_to, explode( ',', $content ) ); + $reply_to = array_merge( + (array) $reply_to, + ( is_array( $content ) ) ? $content : explode( ',', $content ) + ); break; default: - // Add it to our grand headers array. - $headers[ trim( $name ) ] = trim( $content ); + $name = trim( $name ); + $content = trim( $content ); + if ( isset( $headers[ $name ] ) ) { + if ( ! is_array( $headers[ $name ] ) ) { + $headers[ $name ] = array( $headers[ $name ] ); + } + $headers[ $name ][] = $content; + } else { + $headers[ $name ] = $content; + } break; } } @@ -532,7 +551,13 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() // Only add custom headers not added automatically by PHPMailer. if ( ! in_array( $name, array( 'MIME-Version', 'X-Mailer' ), true ) ) { try { - $phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) ); + if ( is_array( $content ) ) { + foreach ( $content as $value ) { + $phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $value ) ); + } + } else { + $phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) ); + } } catch ( PHPMailer\PHPMailer\Exception $e ) { continue; } From 1b29a91ad80853cf5610bc172d478e6475342485 Mon Sep 17 00:00:00 2001 From: SirLouen Date: Sun, 21 Sep 2025 00:04:00 +0200 Subject: [PATCH 3/6] Associative Array Case --- src/wp-includes/formatting.php | 11 ++++++++--- src/wp-includes/pluggable.php | 8 +++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 15aac9be53d91..6173ddf601ee6 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -6172,14 +6172,19 @@ function wp_staticize_emoji_for_email( $mail ) { } foreach ( $headers as $key => $header ) { - if ( is_string( $header ) && strpos($header, ':') === false ) { - if ( is_numeric ( $key ) ) { + if ( is_string( $header ) && strpos( $header, ':' ) === false ) { + if ( is_numeric( $key ) ) { continue; } } // Explode them out. - list( $name, $content ) = ( is_numeric ( $key ) ) ? explode( ':', trim( $header ), 2 ) : array ( $key, $header ); + list( $name, $content ) = ( is_numeric( $key ) ) ? explode( ':', trim( $header ), 2 ) : array( $key, $header ); + + // Skip because Content-Type must be an string. + if ( ! is_string( $name ) || ! is_string( $content ) ) { + continue; + } // Cleanup crew. $name = trim( $name ); diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index ede069cb743f8..78263f0ee3175 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -307,7 +307,9 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() if ( ! empty( $tempheaders ) ) { // Iterate through the raw headers. foreach ( (array) $tempheaders as $key => $header ) { - if ( strpos($header, ':') === false ) { + if ( is_array( $header ) ) { + $header = implode( ',', $header ) . ','; + } elseif ( strpos( $header, ':' ) === false ) { if ( false !== stripos( $header, 'boundary=' ) ) { $parts = preg_split( '/boundary=/i', trim( $header ) ); $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) ); @@ -317,7 +319,7 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() } } // Explode them out. - list( $name, $content ) = ( is_numeric ( $key ) ) ? explode( ':', trim( $header ), 2 ) : array ( $key, $header ); + list( $name, $content ) = ( is_numeric( $key ) ) ? explode( ':', trim( $header ), 2 ) : array( $key, $header ); // Cleanup crew. $name = trim( $name ); @@ -360,7 +362,7 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() $content_type = trim( $content ); } break; - case 'cc': + case 'cc': $cc = array_merge( (array) $cc, ( is_array( $content ) ) ? $content : explode( ',', $content ) From 811ff70bf226204582d516d56fdea0b4cca97500 Mon Sep 17 00:00:00 2001 From: SirLouen Date: Sun, 21 Sep 2025 00:04:57 +0200 Subject: [PATCH 4/6] Little fix --- src/wp-includes/pluggable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index 78263f0ee3175..0462539936b64 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -308,7 +308,7 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() // Iterate through the raw headers. foreach ( (array) $tempheaders as $key => $header ) { if ( is_array( $header ) ) { - $header = implode( ',', $header ) . ','; + $header = implode( ',', $header ); } elseif ( strpos( $header, ':' ) === false ) { if ( false !== stripos( $header, 'boundary=' ) ) { $parts = preg_split( '/boundary=/i', trim( $header ) ); From 2c982bba21194093654ffd6323876df02ea3a4cd Mon Sep 17 00:00:00 2001 From: SirLouen Date: Sun, 21 Sep 2025 00:13:00 +0200 Subject: [PATCH 5/6] Merging Conditional --- src/wp-includes/formatting.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 6173ddf601ee6..3bd03090322ec 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -6172,10 +6172,11 @@ function wp_staticize_emoji_for_email( $mail ) { } foreach ( $headers as $key => $header ) { - if ( is_string( $header ) && strpos( $header, ':' ) === false ) { - if ( is_numeric( $key ) ) { - continue; - } + if ( is_string( $header ) && + strpos( $header, ':' ) === false && + ! is_numeric( $key ) + ) { + continue; } // Explode them out. From a08a6ce11d0a4ce74e2cca7c4fd58b857d5827ea Mon Sep 17 00:00:00 2001 From: SirLouen Date: Sun, 21 Sep 2025 00:16:35 +0200 Subject: [PATCH 6/6] Autocomplete mistake --- src/wp-includes/formatting.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 3bd03090322ec..8d9e6656e8718 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -6174,7 +6174,7 @@ function wp_staticize_emoji_for_email( $mail ) { foreach ( $headers as $key => $header ) { if ( is_string( $header ) && strpos( $header, ':' ) === false && - ! is_numeric( $key ) + is_numeric( $key ) ) { continue; }