Commit b825f104ff for wordpress.org

commit b825f104ff58b069c60a3e367b47e658a7083a9a
Author: Sergey Biryukov <sergeybiryukov.ru@gmail.com>
Date:   Sat Jan 10 23:52:47 2026 +0000

    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:
    * [https://github.com/PHPMailer/PHPMailer/releases/tag/v7.0.2 PHPMailer 7.0.2 release notes]
    * [https://github.com/PHPMailer/PHPMailer/compare/v7.0.0...v7.0.2 Full list of changes in PHPMailer 7.0.2]

    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.
    Built from https://develop.svn.wordpress.org/trunk@61468


    git-svn-id: http://core.svn.wordpress.org/trunk@60780 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/PHPMailer/PHPMailer.php b/wp-includes/PHPMailer/PHPMailer.php
index ff3b84bb26..2bb3578c7e 100644
--- a/wp-includes/PHPMailer/PHPMailer.php
+++ b/wp-includes/PHPMailer/PHPMailer.php
@@ -768,7 +768,7 @@ class PHPMailer
      *
      * @var string
      */
-    const VERSION = '7.0.0';
+    const VERSION = '7.0.2';

     /**
      * Error severity: message only, continue processing.
@@ -876,6 +876,7 @@ class PHPMailer
     private function mailPassthru($to, $subject, $body, $header, $params)
     {
         //Check overloading of mail function to avoid double-encoding
+        // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecatedRemoved
         if ((int)ini_get('mbstring.func_overload') & 1) {
             $subject = $this->secureHeader($subject);
         } else {
@@ -987,6 +988,54 @@ class PHPMailer
         $this->Mailer = 'mail';
     }

+    /**
+     * Extract sendmail path and parse to deal with known parameters.
+     *
+     * @param string $sendmailPath The sendmail path as set in php.ini
+     *
+     * @return string The sendmail path without the known parameters
+     */
+    private function parseSendmailPath($sendmailPath)
+    {
+        $sendmailPath = trim((string)$sendmailPath);
+        if ($sendmailPath === '') {
+            return $sendmailPath;
+        }
+
+        $parts = preg_split('/\s+/', $sendmailPath);
+        if (empty($parts)) {
+            return $sendmailPath;
+        }
+
+        $command = array_shift($parts);
+        $remainder = [];
+
+        // Parse only -t, -i, -oi and -f parameters.
+        for ($i = 0; $i < count($parts); ++$i) {
+            $part = $parts[$i];
+            if (preg_match('/^-(i|oi|t)$/', $part, $matches)) {
+                continue;
+            }
+            if (preg_match('/^-f(.*)$/', $part, $matches)) {
+                $address = $matches[1];
+                if ($address === '' && isset($parts[$i + 1]) && strpos($parts[$i + 1], '-') !== 0) {
+                    $address = $parts[++$i];
+                }
+                $this->Sender = $address;
+                continue;
+            }
+
+            $remainder[] = $part;
+        }
+
+        // The params that are not parsed are added back to the command.
+        if (!empty($remainder)) {
+            $command .= ' ' . implode(' ', $remainder);
+        }
+
+        return $command;
+    }
+
     /**
      * Send messages using $Sendmail.
      */
@@ -995,10 +1044,9 @@ class PHPMailer
         $ini_sendmail_path = ini_get('sendmail_path');

         if (false === stripos($ini_sendmail_path, 'sendmail')) {
-            $this->Sendmail = '/usr/sbin/sendmail';
-        } else {
-            $this->Sendmail = $ini_sendmail_path;
+            $ini_sendmail_path = '/usr/sbin/sendmail';
         }
+        $this->Sendmail = $this->parseSendmailPath($ini_sendmail_path);
         $this->Mailer = 'sendmail';
     }

@@ -1010,10 +1058,9 @@ class PHPMailer
         $ini_sendmail_path = ini_get('sendmail_path');

         if (false === stripos($ini_sendmail_path, 'qmail')) {
-            $this->Sendmail = '/var/qmail/bin/qmail-inject';
-        } else {
-            $this->Sendmail = $ini_sendmail_path;
+            $ini_sendmail_path = '/var/qmail/bin/qmail-inject';
         }
+        $this->Sendmail = $this->parseSendmailPath($ini_sendmail_path);
         $this->Mailer = 'qmail';
     }

@@ -1242,7 +1289,9 @@ class PHPMailer
      * @see https://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
      *
      * @param string $addrstr The address list string
-     * @param null   $useimap Deprecated argument since 6.11.0.
+     * @param null   $useimap Unused. Argument has been deprecated in PHPMailer 6.11.0.
+     *                        Previously this argument determined whether to use
+     *                        the IMAP extension to parse the list and accepted a boolean value.
      * @param string $charset The charset to use when decoding the address list string.
      *
      * @return array
@@ -1250,13 +1299,15 @@ class PHPMailer
     public static function parseAddresses($addrstr, $useimap = null, $charset = self::CHARSET_ISO88591)
     {
         if ($useimap !== null) {
-            trigger_error(self::lang('deprecated_argument'), E_USER_DEPRECATED);
+            trigger_error(self::lang('deprecated_argument') . '$useimap', E_USER_DEPRECATED);
         }
         $addresses = [];
         if (function_exists('imap_rfc822_parse_adrlist')) {
             //Use this built-in parser if it's available
+            // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.imap_rfc822_parse_adrlistRemoved -- wrapped in function_exists()
             $list = imap_rfc822_parse_adrlist($addrstr, '');
             // Clear any potential IMAP errors to get rid of notices being thrown at end of script.
+            // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.imap_errorsRemoved -- wrapped in function_exists()
             imap_errors();
             foreach ($list as $address) {
                 if (
@@ -1583,9 +1634,11 @@ class PHPMailer
                     );
                 } elseif (defined('INTL_IDNA_VARIANT_2003')) {
                     //Fall back to this old, deprecated/removed encoding
+                    // phpcs:ignore PHPCompatibility.Constants.RemovedConstants.intl_idna_variant_2003DeprecatedRemoved
                     $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003);
                 } else {
                     //Fall back to a default we don't know about
+                    // phpcs:ignore PHPCompatibility.ParameterValues.NewIDNVariantDefault.NotSet
                     $punycode = idn_to_ascii($domain, $errorcode);
                 }
                 if (false !== $punycode) {
@@ -1853,25 +1906,27 @@ class PHPMailer
             //PHP config has a sender address we can use
             $this->Sender = ini_get('sendmail_from');
         }
-        //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
+
+        $sendmailArgs = [];
+
+        // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
+        // Also don't add the -f automatically unless it has been set either via Sender
+        // or sendmail_path. Otherwise it can introduce new problems.
+        // @see http://github.com/PHPMailer/PHPMailer/issues/2298
         if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) {
-            if ($this->Mailer === 'qmail') {
-                $sendmailFmt = '%s -f%s';
-            } else {
-                $sendmailFmt = '%s -oi -f%s -t';
-            }
-        } elseif ($this->Mailer === 'qmail') {
-            $sendmailFmt = '%s';
-        } else {
-            //Allow sendmail to choose a default envelope sender. It may
-            //seem preferable to force it to use the From header as with
-            //SMTP, but that introduces new problems (see
-            //<https://github.com/PHPMailer/PHPMailer/issues/2298>), and
-            //it has historically worked this way.
-            $sendmailFmt = '%s -oi -t';
+            $sendmailArgs[] = '-f' . $this->Sender;
+        }
+
+        // Qmail doesn't accept all the sendmail parameters
+        // @see https://github.com/PHPMailer/PHPMailer/issues/3189
+        if ($this->Mailer !== 'qmail') {
+            $sendmailArgs[] = '-i';
+            $sendmailArgs[] = '-t';
         }

-        $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
+        $resultArgs = (empty($sendmailArgs) ? '' : ' ' . implode(' ', $sendmailArgs));
+
+        $sendmail = trim(escapeshellcmd($this->Sendmail) . $resultArgs);
         $this->edebug('Sendmail path: ' . $this->Sendmail);
         $this->edebug('Sendmail command: ' . $sendmail);
         $this->edebug('Envelope sender: ' . $this->Sender);
@@ -2055,7 +2110,8 @@ class PHPMailer
             $this->Sender = ini_get('sendmail_from');
         }
         if (!empty($this->Sender) && static::validateAddress($this->Sender)) {
-            if (self::isShellSafe($this->Sender)) {
+            $phpmailer_path = ini_get('sendmail_path');
+            if (self::isShellSafe($this->Sender) && strpos($phpmailer_path, ' -f') === false) {
                 $params = sprintf('-f%s', $this->Sender);
             }
             $old_from = ini_get('sendmail_from');
@@ -2482,7 +2538,7 @@ class PHPMailer
             'no_smtputf8' => 'Server does not support SMTPUTF8 needed to send to Unicode addresses',
             'imap_recommended' => 'Using simplified address parser is not recommended. ' .
                 'Install the PHP IMAP extension for full RFC822 parsing.',
-            'deprecated_argument' => 'Argument $useimap is deprecated',
+            'deprecated_argument' => 'Deprecated Argument: ',
         ];
         if (empty($lang_path)) {
             //Calculate an absolute path so it can work if CWD is not here
@@ -2956,6 +3012,7 @@ class PHPMailer
         $bytes = '';
         if (function_exists('random_bytes')) {
             try {
+                // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.random_bytesFound -- Wrapped in function_exists.
                 $bytes = random_bytes($len);
             } catch (\Exception $e) {
                 //Do nothing
@@ -4590,10 +4647,10 @@ class PHPMailer
      * Converts data-uri images into embedded attachments.
      * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
      *
-     * @param string        $message  HTML message string
-     * @param string        $basedir  Absolute path to a base directory to prepend to relative paths to images
-     * @param bool|callable $advanced Whether to use the internal HTML to text converter
-     *                                or your own custom converter
+     * @param string        $message    HTML message string
+     * @param string        $basedir    Absolute path to a base directory to prepend to relative paths to images
+     * @param bool|callable $advanced   Whether to use the internal HTML to text converter
+     *                                  or your own custom converter
      * @return string The transformed message body
      *
      * @throws Exception
@@ -4602,6 +4659,12 @@ class PHPMailer
      */
     public function msgHTML($message, $basedir = '', $advanced = false)
     {
+        $cid_domain = 'phpmailer.0';
+        if (filter_var($this->From, FILTER_VALIDATE_EMAIL)) {
+            //prepend with a character to create valid RFC822 string in order to validate
+            $cid_domain = substr($this->From, strrpos($this->From, '@') + 1);
+        }
+
         preg_match_all('/(?<!-)(src|background)=["\'](.*)["\']/Ui', $message, $images);
         if (array_key_exists(2, $images)) {
             if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
@@ -4623,7 +4686,7 @@ class PHPMailer
                     }
                     //Hash the decoded data, not the URL, so that the same data-URI image used in multiple places
                     //will only be embedded once, even if it used a different encoding
-                    $cid = substr(hash('sha256', $data), 0, 32) . '@phpmailer.0'; //RFC2392 S 2
+                    $cid = substr(hash('sha256', $data), 0, 32) . '@' . $cid_domain; //RFC2392 S 2

                     if (!$this->cidExists($cid)) {
                         $this->addStringEmbeddedImage(
@@ -4657,7 +4720,7 @@ class PHPMailer
                         $directory = '';
                     }
                     //RFC2392 S 2
-                    $cid = substr(hash('sha256', $url), 0, 32) . '@phpmailer.0';
+                    $cid = substr(hash('sha256', $url), 0, 32) . '@' . $cid_domain;
                     if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
                         $basedir .= '/';
                     }
@@ -5105,12 +5168,14 @@ class PHPMailer
         }
         if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
             if (\PHP_MAJOR_VERSION < 8) {
+                // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.openssl_pkey_freeDeprecated
                 openssl_pkey_free($privKey);
             }

             return base64_encode($signature);
         }
         if (\PHP_MAJOR_VERSION < 8) {
+            // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.openssl_pkey_freeDeprecated
             openssl_pkey_free($privKey);
         }

diff --git a/wp-includes/PHPMailer/POP3.php b/wp-includes/PHPMailer/POP3.php
index c881a1c3fe..186fe9fe47 100644
--- a/wp-includes/PHPMailer/POP3.php
+++ b/wp-includes/PHPMailer/POP3.php
@@ -45,8 +45,9 @@ class POP3
      * The POP3 PHPMailer Version number.
      *
      * @var string
+     * @deprecated This constant will be removed in PHPMailer 8.0. Use `PHPMailer::VERSION` instead.
      */
-    const VERSION = '7.0.0';
+    const VERSION = '7.0.2';

     /**
      * Default POP3 port number.
diff --git a/wp-includes/PHPMailer/SMTP.php b/wp-includes/PHPMailer/SMTP.php
index a36741bfb9..559b52c45e 100644
--- a/wp-includes/PHPMailer/SMTP.php
+++ b/wp-includes/PHPMailer/SMTP.php
@@ -34,8 +34,9 @@ class SMTP
      * The PHPMailer SMTP version number.
      *
      * @var string
+     * @deprecated This constant will be removed in PHPMailer 8.0. Use `PHPMailer::VERSION` instead.
      */
-    const VERSION = '7.0.0';
+    const VERSION = '7.0.2';

     /**
      * SMTP line break constant.
@@ -494,7 +495,9 @@ class SMTP
         //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
         //so add them back in manually if we can
         if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
+            // phpcs:ignore PHPCompatibility.Constants.NewConstants.stream_crypto_method_tlsv1_2_clientFound
             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
+            // phpcs:ignore PHPCompatibility.Constants.NewConstants.stream_crypto_method_tlsv1_1_clientFound
             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
         }

@@ -633,7 +636,13 @@ class SMTP
                 if (null === $OAuth) {
                     return false;
                 }
-                $oauth = $OAuth->getOauth64();
+                try {
+                    $oauth = $OAuth->getOauth64();
+                } catch (\Exception $e) {
+                    // We catch all exceptions and convert them to PHPMailer exceptions to be able to
+                    // handle them correctly later
+                    throw new Exception("SMTP authentication error", 0, $e);
+                }
                 /*
                  * An SMTP command line can have a maximum length of 512 bytes, including the command name,
                  * so the base64-encoded OAUTH token has a maximum length of:
@@ -761,6 +770,25 @@ class SMTP
         }
     }

+    private function iterateLines($s)
+    {
+        $start = 0;
+        $length = strlen($s);
+
+        for ($i = 0; $i < $length; $i++) {
+            $c = $s[$i];
+            if ($c === "\n" || $c === "\r") {
+                yield substr($s, $start, $i - $start);
+                if ($c === "\r" && $i + 1 < $length && $s[$i + 1] === "\n") {
+                    $i++;
+                }
+                $start = $i + 1;
+            }
+        }
+
+        yield substr($s, $start);
+    }
+
     /**
      * Send an SMTP DATA command.
      * Issues a data command and sends the msg_data to the server,
@@ -789,15 +817,16 @@ class SMTP
          * NOTE: this does not count towards line-length limit.
          */

-        //Normalize line breaks before exploding
-        $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
+        //Iterate over lines with normalized line breaks
+        $lines = $this->iterateLines($msg_data);

         /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
          * of the first line (':' separated) does not contain a space then it _should_ be a header, and we will
          * process all lines before a blank line as headers.
          */

-        $field = substr($lines[0], 0, strpos($lines[0], ':'));
+        $first_line = $lines->current();
+        $field = substr($first_line, 0, strpos($first_line, ':'));
         $in_headers = false;
         if (!empty($field) && strpos($field, ' ') === false) {
             $in_headers = true;
diff --git a/wp-includes/css/dist/index.php b/wp-includes/css/dist/index.php
index 1e931e3a7a..c1720de876 100644
--- a/wp-includes/css/dist/index.php
+++ b/wp-includes/css/dist/index.php
@@ -7,11 +7,6 @@
  */

 return array(
-	array(
-		'handle' => 'wp-list-reusable-blocks',
-		'path' => 'list-reusable-blocks/style',
-		'dependencies' => array('wp-components'),
-	),
 	array(
 		'handle' => 'wp-nux',
 		'path' => 'nux/style',
@@ -22,6 +17,11 @@ return array(
 		'path' => 'preferences/style',
 		'dependencies' => array('wp-components'),
 	),
+	array(
+		'handle' => 'wp-list-reusable-blocks',
+		'path' => 'list-reusable-blocks/style',
+		'dependencies' => array('wp-components'),
+	),
 	array(
 		'handle' => 'wp-commands',
 		'path' => 'commands/style',
@@ -52,6 +52,11 @@ return array(
 		'path' => 'format-library/style',
 		'dependencies' => array('wp-block-editor', 'wp-components'),
 	),
+	array(
+		'handle' => 'wp-media-utils',
+		'path' => 'media-utils/style',
+		'dependencies' => array('wp-components'),
+	),
 	array(
 		'handle' => 'wp-block-directory',
 		'path' => 'block-directory/style',
@@ -63,14 +68,9 @@ return array(
 		'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-components', 'wp-media-utils', 'wp-preferences', 'wp-widgets'),
 	),
 	array(
-		'handle' => 'wp-media-utils',
-		'path' => 'media-utils/style',
-		'dependencies' => array('wp-components'),
-	),
-	array(
-		'handle' => 'wp-edit-post',
-		'path' => 'edit-post/style',
-		'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-commands', 'wp-components', 'wp-editor', 'wp-preferences', 'wp-widgets'),
+		'handle' => 'wp-block-library',
+		'path' => 'block-library/style',
+		'dependencies' => array('wp-block-editor', 'wp-components', 'wp-patterns'),
 	),
 	array(
 		'handle' => 'wp-edit-widgets',
@@ -78,9 +78,9 @@ return array(
 		'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-components', 'wp-media-utils', 'wp-patterns', 'wp-preferences', 'wp-widgets'),
 	),
 	array(
-		'handle' => 'wp-block-library',
-		'path' => 'block-library/style',
-		'dependencies' => array('wp-block-editor', 'wp-components', 'wp-patterns'),
+		'handle' => 'wp-edit-post',
+		'path' => 'edit-post/style',
+		'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-commands', 'wp-components', 'wp-editor', 'wp-preferences', 'wp-widgets'),
 	),
 	array(
 		'handle' => 'wp-editor',
diff --git a/wp-includes/js/dist/script-modules/index.php b/wp-includes/js/dist/script-modules/index.php
index 113805a3fc..1ebf6bd042 100644
--- a/wp-includes/js/dist/script-modules/index.php
+++ b/wp-includes/js/dist/script-modules/index.php
@@ -7,11 +7,6 @@
  */

 return array(
-	array(
-		'id' => '@wordpress/a11y',
-		'path' => 'a11y/index',
-		'asset' => 'a11y/index.min.asset.php',
-	),
 	array(
 		'id' => '@wordpress/interactivity-router',
 		'path' => 'interactivity-router/index',
@@ -27,11 +22,6 @@ return array(
 		'path' => 'interactivity/index',
 		'asset' => 'interactivity/index.min.asset.php',
 	),
-	array(
-		'id' => '@wordpress/core-abilities',
-		'path' => 'core-abilities/index',
-		'asset' => 'core-abilities/index.min.asset.php',
-	),
 	array(
 		'id' => '@wordpress/latex-to-mathml',
 		'path' => 'latex-to-mathml/index',
@@ -42,6 +32,11 @@ return array(
 		'path' => 'latex-to-mathml/loader',
 		'asset' => 'latex-to-mathml/loader.min.asset.php',
 	),
+	array(
+		'id' => '@wordpress/a11y',
+		'path' => 'a11y/index',
+		'asset' => 'a11y/index.min.asset.php',
+	),
 	array(
 		'id' => '@wordpress/abilities',
 		'path' => 'abilities/index',
@@ -57,6 +52,11 @@ return array(
 		'path' => 'edit-site-init/index',
 		'asset' => 'edit-site-init/index.min.asset.php',
 	),
+	array(
+		'id' => '@wordpress/core-abilities',
+		'path' => 'core-abilities/index',
+		'asset' => 'core-abilities/index.min.asset.php',
+	),
 	array(
 		'id' => '@wordpress/lazy-editor',
 		'path' => 'lazy-editor/index',
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 1b8d964a95..2d03a6b1ca 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.0-alpha-61467';
+$wp_version = '7.0-alpha-61468';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.