Make WordPress Core

Changeset 61468


Ignore:
Timestamp:
01/10/2026 11:50:11 PM (5 weeks ago)
Author:
SergeyBiryukov
Message:

External Libraries: Upgrade PHPMailer to version 7.0.2.

The latest version:

  • Includes a fix for sendmail parameter problems in WordPress.
  • Reduces memory consumption when sending large attachments.

References:

Follow-up to [54937], [55557], [56484], [57137], [59246], [59481], [60623], [60813], [60888], [61249].

Props SirLouen, robinvandervliet, desrosj, siliconforks, digitalblanket, studiomondiale, jorbin, westonruter, dmsnell, zoe20, Monarobase, amanandhishoe, SergeyBiyrukov.
Fixes #64491. See #64368.

Location:
trunk/src/wp-includes/PHPMailer
Files:
3 edited

Legend:

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

    r61249 r61468  
    769769     * @var string
    770770     */
    771     const VERSION = '7.0.0';
     771    const VERSION = '7.0.2';
    772772
    773773    /**
     
    877877    {
    878878        //Check overloading of mail function to avoid double-encoding
     879        // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecatedRemoved
    879880        if ((int)ini_get('mbstring.func_overload') & 1) {
    880881            $subject = $this->secureHeader($subject);
     
    989990
    990991    /**
     992     * Extract sendmail path and parse to deal with known parameters.
     993     *
     994     * @param string $sendmailPath The sendmail path as set in php.ini
     995     *
     996     * @return string The sendmail path without the known parameters
     997     */
     998    private function parseSendmailPath($sendmailPath)
     999    {
     1000        $sendmailPath = trim((string)$sendmailPath);
     1001        if ($sendmailPath === '') {
     1002            return $sendmailPath;
     1003        }
     1004
     1005        $parts = preg_split('/\s+/', $sendmailPath);
     1006        if (empty($parts)) {
     1007            return $sendmailPath;
     1008        }
     1009
     1010        $command = array_shift($parts);
     1011        $remainder = [];
     1012
     1013        // Parse only -t, -i, -oi and -f parameters.
     1014        for ($i = 0; $i < count($parts); ++$i) {
     1015            $part = $parts[$i];
     1016            if (preg_match('/^-(i|oi|t)$/', $part, $matches)) {
     1017                continue;
     1018            }
     1019            if (preg_match('/^-f(.*)$/', $part, $matches)) {
     1020                $address = $matches[1];
     1021                if ($address === '' && isset($parts[$i + 1]) && strpos($parts[$i + 1], '-') !== 0) {
     1022                    $address = $parts[++$i];
     1023                }
     1024                $this->Sender = $address;
     1025                continue;
     1026            }
     1027
     1028            $remainder[] = $part;
     1029        }
     1030
     1031        // The params that are not parsed are added back to the command.
     1032        if (!empty($remainder)) {
     1033            $command .= ' ' . implode(' ', $remainder);
     1034        }
     1035
     1036        return $command;
     1037    }
     1038
     1039    /**
    9911040     * Send messages using $Sendmail.
    9921041     */
     
    9961045
    9971046        if (false === stripos($ini_sendmail_path, 'sendmail')) {
    998             $this->Sendmail = '/usr/sbin/sendmail';
    999         } else {
    1000             $this->Sendmail = $ini_sendmail_path;
    1001         }
     1047            $ini_sendmail_path = '/usr/sbin/sendmail';
     1048        }
     1049        $this->Sendmail = $this->parseSendmailPath($ini_sendmail_path);
    10021050        $this->Mailer = 'sendmail';
    10031051    }
     
    10111059
    10121060        if (false === stripos($ini_sendmail_path, 'qmail')) {
    1013             $this->Sendmail = '/var/qmail/bin/qmail-inject';
    1014         } else {
    1015             $this->Sendmail = $ini_sendmail_path;
    1016         }
     1061            $ini_sendmail_path = '/var/qmail/bin/qmail-inject';
     1062        }
     1063        $this->Sendmail = $this->parseSendmailPath($ini_sendmail_path);
    10171064        $this->Mailer = 'qmail';
    10181065    }
     
    12431290     *
    12441291     * @param string $addrstr The address list string
    1245      * @param null   $useimap Deprecated argument since 6.11.0.
     1292     * @param null   $useimap Unused. Argument has been deprecated in PHPMailer 6.11.0.
     1293     *                        Previously this argument determined whether to use
     1294     *                        the IMAP extension to parse the list and accepted a boolean value.
    12461295     * @param string $charset The charset to use when decoding the address list string.
    12471296     *
     
    12511300    {
    12521301        if ($useimap !== null) {
    1253             trigger_error(self::lang('deprecated_argument'), E_USER_DEPRECATED);
     1302            trigger_error(self::lang('deprecated_argument') . '$useimap', E_USER_DEPRECATED);
    12541303        }
    12551304        $addresses = [];
    12561305        if (function_exists('imap_rfc822_parse_adrlist')) {
    12571306            //Use this built-in parser if it's available
     1307            // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.imap_rfc822_parse_adrlistRemoved -- wrapped in function_exists()
    12581308            $list = imap_rfc822_parse_adrlist($addrstr, '');
    12591309            // Clear any potential IMAP errors to get rid of notices being thrown at end of script.
     1310            // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.imap_errorsRemoved -- wrapped in function_exists()
    12601311            imap_errors();
    12611312            foreach ($list as $address) {
     
    15841635                } elseif (defined('INTL_IDNA_VARIANT_2003')) {
    15851636                    //Fall back to this old, deprecated/removed encoding
     1637                    // phpcs:ignore PHPCompatibility.Constants.RemovedConstants.intl_idna_variant_2003DeprecatedRemoved
    15861638                    $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003);
    15871639                } else {
    15881640                    //Fall back to a default we don't know about
     1641                    // phpcs:ignore PHPCompatibility.ParameterValues.NewIDNVariantDefault.NotSet
    15891642                    $punycode = idn_to_ascii($domain, $errorcode);
    15901643                }
     
    18541907            $this->Sender = ini_get('sendmail_from');
    18551908        }
    1856         //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
     1909
     1910        $sendmailArgs = [];
     1911
     1912        // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
     1913        // Also don't add the -f automatically unless it has been set either via Sender
     1914        // or sendmail_path. Otherwise it can introduce new problems.
     1915        // @see http://github.com/PHPMailer/PHPMailer/issues/2298
    18571916        if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) {
    1858             if ($this->Mailer === 'qmail') {
    1859                 $sendmailFmt = '%s -f%s';
    1860             } else {
    1861                 $sendmailFmt = '%s -oi -f%s -t';
    1862             }
    1863         } elseif ($this->Mailer === 'qmail') {
    1864             $sendmailFmt = '%s';
    1865         } else {
    1866             //Allow sendmail to choose a default envelope sender. It may
    1867             //seem preferable to force it to use the From header as with
    1868             //SMTP, but that introduces new problems (see
    1869             //<https://github.com/PHPMailer/PHPMailer/issues/2298>), and
    1870             //it has historically worked this way.
    1871             $sendmailFmt = '%s -oi -t';
    1872         }
    1873 
    1874         $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
     1917            $sendmailArgs[] = '-f' . $this->Sender;
     1918        }
     1919
     1920        // Qmail doesn't accept all the sendmail parameters
     1921        // @see https://github.com/PHPMailer/PHPMailer/issues/3189
     1922        if ($this->Mailer !== 'qmail') {
     1923            $sendmailArgs[] = '-i';
     1924            $sendmailArgs[] = '-t';
     1925        }
     1926
     1927        $resultArgs = (empty($sendmailArgs) ? '' : ' ' . implode(' ', $sendmailArgs));
     1928
     1929        $sendmail = trim(escapeshellcmd($this->Sendmail) . $resultArgs);
    18751930        $this->edebug('Sendmail path: ' . $this->Sendmail);
    18761931        $this->edebug('Sendmail command: ' . $sendmail);
     
    20562111        }
    20572112        if (!empty($this->Sender) && static::validateAddress($this->Sender)) {
    2058             if (self::isShellSafe($this->Sender)) {
     2113            $phpmailer_path = ini_get('sendmail_path');
     2114            if (self::isShellSafe($this->Sender) && strpos($phpmailer_path, ' -f') === false) {
    20592115                $params = sprintf('-f%s', $this->Sender);
    20602116            }
     
    24832539            'imap_recommended' => 'Using simplified address parser is not recommended. ' .
    24842540                'Install the PHP IMAP extension for full RFC822 parsing.',
    2485             'deprecated_argument' => 'Argument $useimap is deprecated',
     2541            'deprecated_argument' => 'Deprecated Argument: ',
    24862542        ];
    24872543        if (empty($lang_path)) {
     
    29573013        if (function_exists('random_bytes')) {
    29583014            try {
     3015                // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.random_bytesFound -- Wrapped in function_exists.
    29593016                $bytes = random_bytes($len);
    29603017            } catch (\Exception $e) {
     
    45914648     * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
    45924649     *
    4593      * @param string        $message  HTML message string
    4594      * @param string        $basedir  Absolute path to a base directory to prepend to relative paths to images
    4595      * @param bool|callable $advanced Whether to use the internal HTML to text converter
    4596      *                                or your own custom converter
     4650     * @param string        $message    HTML message string
     4651     * @param string        $basedir    Absolute path to a base directory to prepend to relative paths to images
     4652     * @param bool|callable $advanced   Whether to use the internal HTML to text converter
     4653     *                                  or your own custom converter
    45974654     * @return string The transformed message body
    45984655     *
     
    46034660    public function msgHTML($message, $basedir = '', $advanced = false)
    46044661    {
     4662        $cid_domain = 'phpmailer.0';
     4663        if (filter_var($this->From, FILTER_VALIDATE_EMAIL)) {
     4664            //prepend with a character to create valid RFC822 string in order to validate
     4665            $cid_domain = substr($this->From, strrpos($this->From, '@') + 1);
     4666        }
     4667
    46054668        preg_match_all('/(?<!-)(src|background)=["\'](.*)["\']/Ui', $message, $images);
    46064669        if (array_key_exists(2, $images)) {
     
    46244687                    //Hash the decoded data, not the URL, so that the same data-URI image used in multiple places
    46254688                    //will only be embedded once, even if it used a different encoding
    4626                     $cid = substr(hash('sha256', $data), 0, 32) . '@phpmailer.0'; //RFC2392 S 2
     4689                    $cid = substr(hash('sha256', $data), 0, 32) . '@' . $cid_domain; //RFC2392 S 2
    46274690
    46284691                    if (!$this->cidExists($cid)) {
     
    46584721                    }
    46594722                    //RFC2392 S 2
    4660                     $cid = substr(hash('sha256', $url), 0, 32) . '@phpmailer.0';
     4723                    $cid = substr(hash('sha256', $url), 0, 32) . '@' . $cid_domain;
    46614724                    if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
    46624725                        $basedir .= '/';
     
    51065169        if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
    51075170            if (\PHP_MAJOR_VERSION < 8) {
     5171                // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.openssl_pkey_freeDeprecated
    51085172                openssl_pkey_free($privKey);
    51095173            }
     
    51125176        }
    51135177        if (\PHP_MAJOR_VERSION < 8) {
     5178            // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.openssl_pkey_freeDeprecated
    51145179            openssl_pkey_free($privKey);
    51155180        }
  • trunk/src/wp-includes/PHPMailer/POP3.php

    r61249 r61468  
    4646     *
    4747     * @var string
    48      */
    49     const VERSION = '7.0.0';
     48     * @deprecated This constant will be removed in PHPMailer 8.0. Use `PHPMailer::VERSION` instead.
     49     */
     50    const VERSION = '7.0.2';
    5051
    5152    /**
  • trunk/src/wp-includes/PHPMailer/SMTP.php

    r61249 r61468  
    3535     *
    3636     * @var string
    37      */
    38     const VERSION = '7.0.0';
     37     * @deprecated This constant will be removed in PHPMailer 8.0. Use `PHPMailer::VERSION` instead.
     38     */
     39    const VERSION = '7.0.2';
    3940
    4041    /**
     
    495496        //so add them back in manually if we can
    496497        if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
     498            // phpcs:ignore PHPCompatibility.Constants.NewConstants.stream_crypto_method_tlsv1_2_clientFound
    497499            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
     500            // phpcs:ignore PHPCompatibility.Constants.NewConstants.stream_crypto_method_tlsv1_1_clientFound
    498501            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
    499502        }
     
    634637                    return false;
    635638                }
    636                 $oauth = $OAuth->getOauth64();
     639                try {
     640                    $oauth = $OAuth->getOauth64();
     641                } catch (\Exception $e) {
     642                    // We catch all exceptions and convert them to PHPMailer exceptions to be able to
     643                    // handle them correctly later
     644                    throw new Exception("SMTP authentication error", 0, $e);
     645                }
    637646                /*
    638647                 * An SMTP command line can have a maximum length of 512 bytes, including the command name,
     
    762771    }
    763772
     773    private function iterateLines($s)
     774    {
     775        $start = 0;
     776        $length = strlen($s);
     777
     778        for ($i = 0; $i < $length; $i++) {
     779            $c = $s[$i];
     780            if ($c === "\n" || $c === "\r") {
     781                yield substr($s, $start, $i - $start);
     782                if ($c === "\r" && $i + 1 < $length && $s[$i + 1] === "\n") {
     783                    $i++;
     784                }
     785                $start = $i + 1;
     786            }
     787        }
     788
     789        yield substr($s, $start);
     790    }
     791
    764792    /**
    765793     * Send an SMTP DATA command.
     
    790818         */
    791819
    792         //Normalize line breaks before exploding
    793         $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
     820        //Iterate over lines with normalized line breaks
     821        $lines = $this->iterateLines($msg_data);
    794822
    795823        /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
     
    798826         */
    799827
    800         $field = substr($lines[0], 0, strpos($lines[0], ':'));
     828        $first_line = $lines->current();
     829        $field = substr($first_line, 0, strpos($first_line, ':'));
    801830        $in_headers = false;
    802831        if (!empty($field) && strpos($field, ' ') === false) {
Note: See TracChangeset for help on using the changeset viewer.