Make WordPress Core

Changeset 38058


Ignore:
Timestamp:
07/13/2016 06:03:52 PM (10 years ago)
Author:
boonebgorges
Message:

Mail: Improve handling of UTF-8 address headers.

Previously, wp_mail() implemented Reply-To as a generic header, using
PHPMailer's addCustomHeader(). As such, the email address portion of
the header was being incorrectly encoded when the name portion
contained UTF-8 characters. Switching to PHPMailer's more specific
addReplyTo() method fixes the issue.

For greater readability, the handling of all address-related headers
(To, CC, BCC, Reply-To) has been standardized.

Props szepe.viktor, iandunn, bpetty, stephenharris.
Fixes #21659.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/pluggable.php

    r38028 r38058  
    217217
    218218        // Headers
     219        $cc = $bcc = $reply_to = array();
     220
    219221        if ( empty( $headers ) ) {
    220222                $headers = array();
     
    228230                }
    229231                $headers = array();
    230                 $cc = array();
    231                 $bcc = array();
    232232
    233233                // If it's actually got contents
     
    292292                                                $bcc = array_merge( (array) $bcc, explode( ',', $content ) );
    293293                                                break;
     294                                        case 'reply-to':
     295                                                $reply_to = array_merge( (array) $reply_to, explode( ',', $content ) );
     296                                                break;
    294297                                        default:
    295298                                                // Add it to our grand headers array
     
    336339         * @param string $from_email Email address to send from.
    337340         */
    338         $phpmailer->From = apply_filters( 'wp_mail_from', $from_email );
     341        $from_email = apply_filters( 'wp_mail_from', $from_email );
    339342
    340343        /**
     
    345348         * @param string $from_name Name associated with the "from" email address.
    346349         */
    347         $phpmailer->FromName = apply_filters( 'wp_mail_from_name', $from_name );
     350        $from_name = apply_filters( 'wp_mail_from_name', $from_name );
     351
     352        $phpmailer->setFrom( $from_email, $from_name );
    348353
    349354        // Set destination addresses
     
    351356                $to = explode( ',', $to );
    352357
    353         foreach ( (array) $to as $recipient ) {
    354                 try {
    355                         // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
    356                         $recipient_name = '';
    357                         if ( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
    358                                 if ( count( $matches ) == 3 ) {
    359                                         $recipient_name = $matches[1];
    360                                         $recipient = $matches[2];
    361                                 }
    362                         }
    363                         $phpmailer->AddAddress( $recipient, $recipient_name);
    364                 } catch ( phpmailerException $e ) {
    365                         continue;
    366                 }
    367         }
    368 
    369358        // Set mail's subject and body
    370359        $phpmailer->Subject = $subject;
    371360        $phpmailer->Body    = $message;
    372361
    373         // Add any CC and BCC recipients
    374         if ( !empty( $cc ) ) {
    375                 foreach ( (array) $cc as $recipient ) {
     362        // Use appropriate methods for handling addresses, rather than treating them as generic headers
     363        $address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' );
     364
     365        foreach ( $address_headers as $address_header => $addresses ) {
     366                if ( empty( $addresses ) ) {
     367                        continue;
     368                }
     369
     370                foreach ( (array) $addresses as $address ) {
    376371                        try {
    377372                                // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
    378373                                $recipient_name = '';
    379                                 if ( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
     374
     375                                if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) {
    380376                                        if ( count( $matches ) == 3 ) {
    381377                                                $recipient_name = $matches[1];
    382                                                 $recipient = $matches[2];
     378                                                $address        = $matches[2];
    383379                                        }
    384380                                }
    385                                 $phpmailer->AddCc( $recipient, $recipient_name );
    386                         } catch ( phpmailerException $e ) {
    387                                 continue;
    388                         }
    389                 }
    390         }
    391 
    392         if ( !empty( $bcc ) ) {
    393                 foreach ( (array) $bcc as $recipient) {
    394                         try {
    395                                 // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
    396                                 $recipient_name = '';
    397                                 if ( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
    398                                         if ( count( $matches ) == 3 ) {
    399                                                 $recipient_name = $matches[1];
    400                                                 $recipient = $matches[2];
    401                                         }
     381
     382                                switch ( $address_header ) {
     383                                        case 'to':
     384                                                $phpmailer->addAddress( $address, $recipient_name );
     385                                                break;
     386                                        case 'cc':
     387                                                $phpmailer->addCc( $address, $recipient_name );
     388                                                break;
     389                                        case 'bcc':
     390                                                $phpmailer->addBcc( $address, $recipient_name );
     391                                                break;
     392                                        case 'reply_to':
     393                                                $phpmailer->addReplyTo( $address, $recipient_name );
     394                                                break;
    402395                                }
    403                                 $phpmailer->AddBcc( $recipient, $recipient_name );
    404396                        } catch ( phpmailerException $e ) {
    405397                                continue;
  • trunk/tests/phpunit/includes/mock-mailer.php

    r37358 r38058  
    1818                        'cc'      => $this->cc,
    1919                        'bcc'     => $this->bcc,
    20                         'header'  => $this->MIMEHeader,
     20                        'header'  => $this->MIMEHeader . $this->mailHeader,
    2121                        'subject' => $this->Subject,
    2222                        'body'    => $this->MIMEBody,
  • trunk/tests/phpunit/tests/mail.php

    r37358 r38058  
    308308                $this->assertNotContains( 'quoted-printable', $GLOBALS['phpmailer']->mock_sent[0]['header'] );
    309309        }
     310
     311        /**
     312         * @ticket 21659
     313         */
     314        public function test_wp_mail_addresses_arent_encoded() {
     315                $to      = 'Lukáš To <to@example.org>';
     316                $subject = 'Testing #21659';
     317                $message = 'Only the name should be encoded, not the address.';
     318
     319                $headers = array(
     320                        'From'     => 'From: Lukáš From <from@example.org>',
     321                        'Cc'       => 'Cc: Lukáš CC <cc@example.org>',
     322                        'Bcc'      => 'Bcc: Lukáš BCC <bcc@example.org>',
     323                        'Reply-To' => 'Reply-To: Lukáš Reply-To <reply_to@example.org>',
     324                );
     325
     326                $expected = array(
     327                        'To'       => 'To: =?UTF-8?B?THVrw6HFoSBUbw==?= <to@example.org>',
     328                        'From'     => 'From: =?UTF-8?Q?Luk=C3=A1=C5=A1_From?= <from@example.org>',
     329                        'Cc'       => 'Cc: =?UTF-8?B?THVrw6HFoSBDQw==?= <cc@example.org>',
     330                        'Bcc'      => 'Bcc: =?UTF-8?B?THVrw6HFoSBCQ0M=?= <bcc@example.org>',
     331                        'Reply-To' => 'Reply-To: =?UTF-8?Q?Luk=C3=A1=C5=A1_Reply-To?= <reply_to@example.org>',
     332                );
     333
     334                wp_mail( $to, $subject, $message, array_values( $headers ) );
     335
     336                $mailer        = tests_retrieve_phpmailer_instance();
     337                $sent_headers  = preg_split( "/\r\n|\n|\r/", $mailer->get_sent()->header );
     338                $headers['To'] = "To: $to";
     339
     340                foreach ( $headers as $header => $value ) {
     341                        $target_headers = preg_grep( "/^$header:/", $sent_headers );
     342                        $this->assertEquals( $expected[ $header ], array_pop( $target_headers ) );
     343                }
     344        }
    310345}
Note: See TracChangeset for help on using the changeset viewer.