WordPress.org

Make WordPress Core

Changeset 38058


Ignore:
Timestamp:
07/13/16 18:03:52 (8 months 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.