From 3fb7d346f35dc043e0f2cc5094e62d21e36aa1bc Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Sun, 26 Nov 2023 19:27:25 +0000 Subject: [PATCH] External Libraries: Upgrade PHPMailer to version 6.9.1. This is a maintenance and feature release, adding support for the official release of PHP 8.3, methods for removing and replacing custom headers, XCLIENT support, and links to a new way of implementing XOAUTH2 authentication. The only change likely to have any impact on existing code is that PHPMailer previously attempted to use opportunistic STARTTLS encryption when connecting to `localhost`, which was unlikely to work. The workaround required setting `SMTPAutoTLS = false`, but that's no longer required. You may still need to use this setting when connecting to literal IPs. References: * [https://github.com/PHPMailer/PHPMailer/releases/tag/v6.9.1 PHPMailer 6.9.1 release notes] * [https://github.com/PHPMailer/PHPMailer/compare/v6.8.1...v6.9.1 Full list of changes in PHPMailer 6.9.1] Follow-up to [50628], [50799], [51169], [51634], [51635], [52252], [52749], [52811], [53500], [53535], [53917], [54427], [54937], [55557], [56484]. Props jrf, Synchro. Fixes #59966. Built from https://develop.svn.wordpress.org/trunk@57137 git-svn-id: http://core.svn.wordpress.org/trunk@56648 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/PHPMailer/PHPMailer.php | 130 +++++++++++++++++++++++++++- wp-includes/PHPMailer/SMTP.php | 33 ++++++- wp-includes/version.php | 2 +- 3 files changed, 161 insertions(+), 4 deletions(-) diff --git a/wp-includes/PHPMailer/PHPMailer.php b/wp-includes/PHPMailer/PHPMailer.php index 011fa92068..cf8657234c 100644 --- a/wp-includes/PHPMailer/PHPMailer.php +++ b/wp-includes/PHPMailer/PHPMailer.php @@ -357,6 +357,13 @@ class PHPMailer */ public $AuthType = ''; + /** + * SMTP SMTPXClient command attibutes + * + * @var array + */ + protected $SMTPXClient = []; + /** * An implementation of the PHPMailer OAuthTokenProvider interface. * @@ -750,7 +757,7 @@ class PHPMailer * * @var string */ - const VERSION = '6.8.1'; + const VERSION = '6.9.1'; /** * Error severity: message only, continue processing. @@ -1573,6 +1580,10 @@ class PHPMailer //Validate From, Sender, and ConfirmReadingTo addresses foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) { + if ($this->{$address_kind} === null) { + $this->{$address_kind} = ''; + continue; + } $this->{$address_kind} = trim($this->{$address_kind}); if (empty($this->{$address_kind})) { continue; @@ -1999,6 +2010,38 @@ class PHPMailer return $this->smtp; } + /** + * Provide SMTP XCLIENT attributes + * + * @param string $name Attribute name + * @param ?string $value Attribute value + * + * @return bool + */ + public function setSMTPXclientAttribute($name, $value) + { + if (!in_array($name, SMTP::$xclient_allowed_attributes)) { + return false; + } + if (isset($this->SMTPXClient[$name]) && $value === null) { + unset($this->SMTPXClient[$name]); + } elseif ($value !== null) { + $this->SMTPXClient[$name] = $value; + } + + return true; + } + + /** + * Get SMTP XCLIENT attributes + * + * @return array + */ + public function getSMTPXclientAttributes() + { + return $this->SMTPXClient; + } + /** * Send mail via SMTP. * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. @@ -2027,6 +2070,9 @@ class PHPMailer } else { $smtp_from = $this->Sender; } + if (count($this->SMTPXClient)) { + $this->smtp->xclient($this->SMTPXClient); + } if (!$this->smtp->mail($smtp_from)) { $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); @@ -2189,10 +2235,17 @@ class PHPMailer $this->smtp->hello($hello); //Automatically enable TLS encryption if: //* it's not disabled + //* we are not connecting to localhost //* we have openssl extension //* we are not already using SSL //* the server offers STARTTLS - if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) { + if ( + $this->SMTPAutoTLS && + $this->Host !== 'localhost' && + $sslext && + $secure !== 'ssl' && + $this->smtp->getServerExt('STARTTLS') + ) { $tls = true; } if ($tls) { @@ -4049,6 +4102,79 @@ class PHPMailer $this->CustomHeader = []; } + /** + * Clear a specific custom header by name or name and value. + * $name value can be overloaded to contain + * both header name and value (name:value). + * + * @param string $name Custom header name + * @param string|null $value Header value + * + * @return bool True if a header was replaced successfully + */ + public function clearCustomHeader($name, $value = null) + { + if (null === $value && strpos($name, ':') !== false) { + //Value passed in as name:value + list($name, $value) = explode(':', $name, 2); + } + $name = trim($name); + $value = (null === $value) ? null : trim($value); + + foreach ($this->CustomHeader as $k => $pair) { + if ($pair[0] == $name) { + // We remove the header if the value is not provided or it matches. + if (null === $value || $pair[1] == $value) { + unset($this->CustomHeader[$k]); + } + } + } + + return true; + } + + /** + * Replace a custom header. + * $name value can be overloaded to contain + * both header name and value (name:value). + * + * @param string $name Custom header name + * @param string|null $value Header value + * + * @return bool True if a header was replaced successfully + * @throws Exception + */ + public function replaceCustomHeader($name, $value = null) + { + if (null === $value && strpos($name, ':') !== false) { + //Value passed in as name:value + list($name, $value) = explode(':', $name, 2); + } + $name = trim($name); + $value = (null === $value) ? '' : trim($value); + + $replaced = false; + foreach ($this->CustomHeader as $k => $pair) { + if ($pair[0] == $name) { + if ($replaced) { + unset($this->CustomHeader[$k]); + continue; + } + if (strpbrk($name . $value, "\r\n") !== false) { + if ($this->exceptions) { + throw new Exception($this->lang('invalid_header')); + } + + return false; + } + $this->CustomHeader[$k] = [$name, $value]; + $replaced = true; + } + } + + return true; + } + /** * Add an error message to the error container. * diff --git a/wp-includes/PHPMailer/SMTP.php b/wp-includes/PHPMailer/SMTP.php index 2b63840304..1b5b00771c 100644 --- a/wp-includes/PHPMailer/SMTP.php +++ b/wp-includes/PHPMailer/SMTP.php @@ -35,7 +35,7 @@ class SMTP * * @var string */ - const VERSION = '6.8.1'; + const VERSION = '6.9.1'; /** * SMTP line break constant. @@ -198,6 +198,18 @@ class SMTP 'Mailjet' => '/[\d]{3} OK queued as (.*)/', ]; + /** + * Allowed SMTP XCLIENT attributes. + * Must be allowed by the SMTP server. EHLO response is not checked. + * + * @see https://www.postfix.org/XCLIENT_README.html + * + * @var array + */ + public static $xclient_allowed_attributes = [ + 'NAME', 'ADDR', 'PORT', 'PROTO', 'HELO', 'LOGIN', 'DESTADDR', 'DESTPORT' + ]; + /** * The last transaction ID issued in response to a DATA command, * if one was detected. @@ -971,6 +983,25 @@ class SMTP ); } + /** + * Send SMTP XCLIENT command to server and check its return code. + * + * @return bool True on success + */ + public function xclient(array $vars) + { + $xclient_options = ""; + foreach ($vars as $key => $value) { + if (in_array($key, SMTP::$xclient_allowed_attributes)) { + $xclient_options .= " {$key}={$value}"; + } + } + if (!$xclient_options) { + return true; + } + return $this->sendCommand('XCLIENT', 'XCLIENT' . $xclient_options, 250); + } + /** * Send an SMTP RSET command. * Abort any transaction that is currently in progress. diff --git a/wp-includes/version.php b/wp-includes/version.php index f33e0c0cf9..6df229da93 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.5-alpha-57136'; +$wp_version = '6.5-alpha-57137'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.