Make WordPress Core


Ignore:
Timestamp:
08/11/2025 04:16:19 PM (3 months ago)
Author:
SergeyBiryukov
Message:

External Libraries: Upgrade PHPMailer to version 6.10.0.

This is a feature and maintenance release introducing full support for RFC 6531 SMTPUTF8, meaning that plugin or theme developers are now free to use Unicode characters in email addresses, such as JøeÜser@example.com, without any complicated encoding schemes. Using this feature requires sending through a mail server that advertises support for SMTPUTF8. For full details see SMTPUTF8.md.

This commit also includes the parts of PHPMailer not previously bundled with core, specifically the DSNConfigurator, OAuth, and POP3 classes, so that plugin developers could use those extended features without including their own versions of the library.

Including the full library aims to make it easier (and faster) for core to update in case of security issues, and to provide more flexibility and security for plugins and (by extension) users of WordPress.

References:

Follow-up to [54937], [55557], [56484], [57137], [59246], [59481].

Props agulbra, Ipstenu, JeffMatson, lukecavanagh, dd32, Otto42, JeffMatson, MattyRob, desrosj, SirLouen, SergeyBiryukov.
Fixes #39714, #63811.

File:
1 edited

Legend:

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

    r59481 r60623  
    581581     * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option.
    582582     *
     583     * If CharSet is UTF8, the validator is left at the default value,
     584     * and you send to addresses that use non-ASCII local parts, then
     585     * PHPMailer automatically changes to the 'eai' validator.
     586     *
    583587     * @see PHPMailer::validateAddress()
    584588     *
     
    661665
    662666    /**
     667     * Whether the need for SMTPUTF8 has been detected. Set by
     668     * preSend() if necessary.
     669     *
     670     * @var bool
     671     */
     672    public $UseSMTPUTF8 = false;
     673
     674    /**
    663675     * The array of attachments.
    664676     *
     
    757769     * @var string
    758770     */
    759     const VERSION = '6.9.3';
     771    const VERSION = '6.10.0';
    760772
    761773    /**
     
    11111123        //Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
    11121124        //Domain is assumed to be whatever is after the last @ symbol in the address
    1113         if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) {
    1114             if ('Reply-To' !== $kind) {
    1115                 if (!array_key_exists($address, $this->RecipientsQueue)) {
    1116                     $this->RecipientsQueue[$address] = $params;
     1125        if ($this->has8bitChars(substr($address, ++$pos))) {
     1126            if (static::idnSupported()) {
     1127                if ('Reply-To' !== $kind) {
     1128                    if (!array_key_exists($address, $this->RecipientsQueue)) {
     1129                        $this->RecipientsQueue[$address] = $params;
     1130
     1131                        return true;
     1132                    }
     1133                } elseif (!array_key_exists($address, $this->ReplyToQueue)) {
     1134                    $this->ReplyToQueue[$address] = $params;
    11171135
    11181136                    return true;
    11191137                }
    1120             } elseif (!array_key_exists($address, $this->ReplyToQueue)) {
    1121                 $this->ReplyToQueue[$address] = $params;
    1122 
    1123                 return true;
    1124             }
    1125 
     1138            }
     1139            //We have an 8-bit domain, but we are missing the necessary extensions to support it
     1140            //Or we are already sending to this address
    11261141            return false;
    11271142        }
     
    11611176    protected function addAnAddress($kind, $address, $name = '')
    11621177    {
     1178        if (
     1179            self::$validator === 'php' &&
     1180            ((bool) preg_match('/[\x80-\xFF]/', $address))
     1181        ) {
     1182            //The caller has not altered the validator and is sending to an address
     1183            //with UTF-8, so assume that they want UTF-8 support instead of failing
     1184            $this->CharSet = self::CHARSET_UTF8;
     1185            self::$validator = 'eai';
     1186        }
    11631187        if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
    11641188            $error_message = sprintf(
     
    13631387     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
    13641388     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
     1389     * * `eai` Use a pattern similar to the HTML5 spec for 'email' and to firefox, extended to support EAI (RFC6530).
    13651390     * * `noregex` Don't use a regex: super fast, really dumb.
    13661391     * Alternatively you may pass in a callable to inject your own validator, for example:
     
    14331458                    $address
    14341459                );
     1460            case 'eai':
     1461                /*
     1462                 * This is the pattern used in the HTML5 spec for validation of 'email' type
     1463                 * form input elements (as above), modified to accept Unicode email addresses.
     1464                 * This is also more lenient than Firefox' html5 spec, in order to make the regex faster.
     1465                 * 'eai' is an acronym for Email Address Internationalization.
     1466                 * This validator is selected automatically if you attempt to use recipient addresses
     1467                 * that contain Unicode characters in the local part.
     1468                 *
     1469                 * @see https://html.spec.whatwg.org/#e-mail-state-(type=email)
     1470                 * @see https://en.wikipedia.org/wiki/International_email
     1471                 */
     1472                return (bool) preg_match(
     1473                    '/^[-\p{L}\p{N}\p{M}.!#$%&\'*+\/=?^_`{|}~]+@[\p{L}\p{N}\p{M}](?:[\p{L}\p{N}\p{M}-]{0,61}' .
     1474                    '[\p{L}\p{N}\p{M}])?(?:\.[\p{L}\p{N}\p{M}]' .
     1475                    '(?:[-\p{L}\p{N}\p{M}]{0,61}[\p{L}\p{N}\p{M}])?)*$/usD',
     1476                    $address
     1477                );
    14351478            case 'php':
    14361479            default:
     
    15681611            $this->mailHeader = '';
    15691612
     1613            //The code below tries to support full use of Unicode,
     1614            //while remaining compatible with legacy SMTP servers to
     1615            //the greatest degree possible: If the message uses
     1616            //Unicode in the local parts of any addresses, it is sent
     1617            //using SMTPUTF8. If not, it it sent using
     1618            //punycode-encoded domains and plain SMTP.
     1619            if (
     1620                static::CHARSET_UTF8 === strtolower($this->CharSet) &&
     1621                ($this->anyAddressHasUnicodeLocalPart($this->RecipientsQueue) ||
     1622                 $this->anyAddressHasUnicodeLocalPart(array_keys($this->all_recipients)) ||
     1623                 $this->anyAddressHasUnicodeLocalPart($this->ReplyToQueue) ||
     1624                 $this->addressHasUnicodeLocalPart($this->From))
     1625            ) {
     1626                $this->UseSMTPUTF8 = true;
     1627            }
    15701628            //Dequeue recipient and Reply-To addresses with IDN
    15711629            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
    1572                 $params[1] = $this->punyencodeAddress($params[1]);
     1630                if (!$this->UseSMTPUTF8) {
     1631                    $params[1] = $this->punyencodeAddress($params[1]);
     1632                }
    15731633                call_user_func_array([$this, 'addAnAddress'], $params);
    15741634            }
     
    20612121            throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
    20622122        }
     2123        //If we have recipient addresses that need Unicode support,
     2124        //but the server doesn't support it, stop here
     2125        if ($this->UseSMTPUTF8 && !$this->smtp->getServerExt('SMTPUTF8')) {
     2126            throw new Exception($this->lang('no_smtputf8'), self::STOP_CRITICAL);
     2127        }
    20632128        //Sender already validated in preSend()
    20642129        if ('' === $this->Sender) {
     
    21622227        $this->smtp->setDebugOutput($this->Debugoutput);
    21632228        $this->smtp->setVerp($this->do_verp);
     2229        $this->smtp->setSMTPUTF8($this->UseSMTPUTF8);
    21642230        if ($this->Host === null) {
    21652231            $this->Host = 'localhost';
     
    23592425            'smtp_error' => 'SMTP server error: ',
    23602426            'variable_set' => 'Cannot set or reset variable: ',
     2427            'no_smtputf8' => 'Server does not support SMTPUTF8 needed to send to Unicode addresses',
    23612428        ];
    23622429        if (empty($lang_path)) {
     
    28732940        $bodyCharSet = $this->CharSet;
    28742941        //Can we do a 7-bit downgrade?
    2875         if (static::ENCODING_8BIT === $bodyEncoding && !$this->has8bitChars($this->Body)) {
     2942        if ($this->UseSMTPUTF8) {
     2943            $bodyEncoding = static::ENCODING_8BIT;
     2944        } elseif (static::ENCODING_8BIT === $bodyEncoding && !$this->has8bitChars($this->Body)) {
    28762945            $bodyEncoding = static::ENCODING_7BIT;
    28772946            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
     
    35103579     * Encode a header value (not including its label) optimally.
    35113580     * Picks shortest of Q, B, or none. Result includes folding if needed.
    3512      * See RFC822 definitions for phrase, comment and text positions.
     3581     * See RFC822 definitions for phrase, comment and text positions,
     3582     * and RFC2047 for inline encodings.
    35133583     *
    35143584     * @param string $str      The header value to encode
     
    35193589    public function encodeHeader($str, $position = 'text')
    35203590    {
     3591        $position = strtolower($position);
     3592        if ($this->UseSMTPUTF8 && !("comment" === $position)) {
     3593            return trim(static::normalizeBreaks($str));
     3594        }
     3595
    35213596        $matchcount = 0;
    35223597        switch (strtolower($position)) {
     
    41834258            $lasterror = $this->smtp->getError();
    41844259            if (!empty($lasterror['error'])) {
    4185                 $msg .= $this->lang('smtp_error') . $lasterror['error'];
     4260                $msg .= ' ' . $this->lang('smtp_error') . $lasterror['error'];
    41864261                if (!empty($lasterror['detail'])) {
    41874262                    $msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detail'];
     
    42694344        return filter_var('https://' . $host, FILTER_VALIDATE_URL) !== false;
    42704345    }
     4346
     4347    /**
     4348     * Check whether the supplied address uses Unicode in the local part.
     4349     *
     4350     * @return bool
     4351     */
     4352    protected function addressHasUnicodeLocalPart($address)
     4353    {
     4354        return (bool) preg_match('/[\x80-\xFF].*@/', $address);
     4355    }
     4356
     4357    /**
     4358     * Check whether any of the supplied addresses use Unicode in the local part.
     4359     *
     4360     * @return bool
     4361     */
     4362    protected function anyAddressHasUnicodeLocalPart($addresses)
     4363    {
     4364        foreach ($addresses as $address) {
     4365            if (is_array($address)) {
     4366                $address = $address[0];
     4367            }
     4368            if ($this->addressHasUnicodeLocalPart($address)) {
     4369                return true;
     4370            }
     4371        }
     4372        return false;
     4373    }
     4374
     4375    /**
     4376     * Check whether the message requires SMTPUTF8 based on what's known so far.
     4377     *
     4378     * @return bool
     4379     */
     4380    public function needsSMTPUTF8()
     4381    {
     4382        return $this->UseSMTPUTF8;
     4383    }
     4384
    42714385
    42724386    /**
Note: See TracChangeset for help on using the changeset viewer.