diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index e46e5d82ad590..8d9e6656e8718 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -6171,13 +6171,21 @@ function wp_staticize_emoji_for_email( $mail ) { } } - foreach ( $headers as $header ) { - if ( ! str_contains( $header, ':' ) ) { + foreach ( $headers as $key => $header ) { + if ( is_string( $header ) && + strpos( $header, ':' ) === false && + 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 ); + + // 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 5edd0c760cbb2..0462539936b64 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -306,16 +306,20 @@ 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 ( 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] ) ); } - 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 ); @@ -359,17 +363,34 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() } break; case 'cc': - $cc = array_merge( (array) $cc, explode( ',', $content ) ); + $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 +553,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; } 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. *