--- a/wp/wp-includes/PHPMailer/PHPMailer.php Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/PHPMailer/PHPMailer.php Fri Sep 05 18:40:08 2025 +0200
@@ -350,14 +350,21 @@
public $Password = '';
/**
- * SMTP auth type.
- * Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2, attempted in that order if not specified.
+ * SMTP authentication type. Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2.
+ * If not specified, the first one from that list that the server supports will be selected.
*
* @var string
*/
public $AuthType = '';
/**
+ * SMTP SMTPXClient command attibutes
+ *
+ * @var array
+ */
+ protected $SMTPXClient = [];
+
+ /**
* An implementation of the PHPMailer OAuthTokenProvider interface.
*
* @var OAuthTokenProvider
@@ -750,7 +757,7 @@
*
* @var string
*/
- const VERSION = '6.6.0';
+ const VERSION = '6.9.1';
/**
* Error severity: message only, continue processing.
@@ -795,7 +802,7 @@
* The maximum line length supported by mail().
*
* Background: mail() will sometimes corrupt messages
- * with headers headers longer than 65 chars, see #818.
+ * with headers longer than 65 chars, see #818.
*
* @var int
*/
@@ -858,7 +865,7 @@
private function mailPassthru($to, $subject, $body, $header, $params)
{
//Check overloading of mail function to avoid double-encoding
- if (ini_get('mbstring.func_overload') & 1) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated
+ if ((int)ini_get('mbstring.func_overload') & 1) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated
$subject = $this->secureHeader($subject);
} else {
$subject = $this->encodeHeader($this->secureHeader($subject));
@@ -1066,8 +1073,8 @@
* Addresses that have been added already return false, but do not throw exceptions.
*
* @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
- * @param string $address The email address to send, resp. to reply to
- * @param string $name
+ * @param string $address The email address
+ * @param string $name An optional username associated with the address
*
* @throws Exception
*
@@ -1075,9 +1082,11 @@
*/
protected function addOrEnqueueAnAddress($kind, $address, $name)
{
- $address = trim($address);
- $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
- $pos = strrpos($address, '@');
+ $pos = false;
+ if ($address !== null) {
+ $address = trim($address);
+ $pos = strrpos($address, '@');
+ }
if (false === $pos) {
//At-sign is missing.
$error_message = sprintf(
@@ -1094,8 +1103,14 @@
return false;
}
+ if ($name !== null && is_string($name)) {
+ $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+ } else {
+ $name = '';
+ }
$params = [$kind, $address, $name];
//Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
+ //Domain is assumed to be whatever is after the last @ symbol in the address
if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) {
if ('Reply-To' !== $kind) {
if (!array_key_exists($address, $this->RecipientsQueue)) {
@@ -1117,6 +1132,22 @@
}
/**
+ * Set the boundaries to use for delimiting MIME parts.
+ * If you override this, ensure you set all 3 boundaries to unique values.
+ * The default boundaries include a "=_" sequence which cannot occur in quoted-printable bodies,
+ * as suggested by https://www.rfc-editor.org/rfc/rfc2045#section-6.7
+ *
+ * @return void
+ */
+ public function setBoundaries()
+ {
+ $this->uniqueid = $this->generateId();
+ $this->boundary[1] = 'b1=_' . $this->uniqueid;
+ $this->boundary[2] = 'b2=_' . $this->uniqueid;
+ $this->boundary[3] = 'b3=_' . $this->uniqueid;
+ }
+
+ /**
* Add an address to one of the recipient arrays or to the ReplyTo array.
* Addresses that have been added already return false, but do not throw exceptions.
*
@@ -1280,7 +1311,7 @@
*/
public function setFrom($address, $name = '', $auto = true)
{
- $address = trim($address);
+ $address = trim((string)$address);
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
//Don't validate now addresses with IDN. Will be done in send().
$pos = strrpos($address, '@');
@@ -1549,17 +1580,21 @@
//Validate From, Sender, and ConfirmReadingTo addresses
foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
- $this->$address_kind = trim($this->$address_kind);
- if (empty($this->$address_kind)) {
+ if ($this->{$address_kind} === null) {
+ $this->{$address_kind} = '';
continue;
}
- $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
- if (!static::validateAddress($this->$address_kind)) {
+ $this->{$address_kind} = trim($this->{$address_kind});
+ if (empty($this->{$address_kind})) {
+ continue;
+ }
+ $this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind});
+ if (!static::validateAddress($this->{$address_kind})) {
$error_message = sprintf(
'%s (%s): %s',
$this->lang('invalid_address'),
$address_kind,
- $this->$address_kind
+ $this->{$address_kind}
);
$this->setError($error_message);
$this->edebug($error_message);
@@ -1659,17 +1694,17 @@
default:
$sendMethod = $this->Mailer . 'Send';
if (method_exists($this, $sendMethod)) {
- return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
+ return $this->{$sendMethod}($this->MIMEHeader, $this->MIMEBody);
}
return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
}
} catch (Exception $exc) {
- if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true) {
+ $this->setError($exc->getMessage());
+ $this->edebug($exc->getMessage());
+ if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true && $this->smtp->connected()) {
$this->smtp->reset();
}
- $this->setError($exc->getMessage());
- $this->edebug($exc->getMessage());
if ($this->exceptions) {
throw $exc;
}
@@ -1857,7 +1892,7 @@
if (!static::isPermittedPath($path)) {
return false;
}
- $readable = file_exists($path);
+ $readable = is_file($path);
//If not a UNC path (expected to start with \\), check read permission, see #2069
if (strpos($path, '\\\\') !== 0) {
$readable = $readable && is_readable($path);
@@ -1885,7 +1920,14 @@
foreach ($this->to as $toaddr) {
$toArr[] = $this->addrFormat($toaddr);
}
- $to = implode(', ', $toArr);
+ $to = trim(implode(', ', $toArr));
+
+ //If there are no To-addresses (e.g. when sending only to BCC-addresses)
+ //the following should be added to get a correct DKIM-signature.
+ //Compare with $this->preSend()
+ if ($to === '') {
+ $to = 'undisclosed-recipients:;';
+ }
$params = null;
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
@@ -1969,6 +2011,38 @@
}
/**
+ * 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.
*
@@ -1996,6 +2070,9 @@
} 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);
@@ -2088,6 +2165,9 @@
$this->smtp->setDebugLevel($this->SMTPDebug);
$this->smtp->setDebugOutput($this->Debugoutput);
$this->smtp->setVerp($this->do_verp);
+ if ($this->Host === null) {
+ $this->Host = 'localhost';
+ }
$hosts = explode(';', $this->Host);
$lastexception = null;
@@ -2155,10 +2235,17 @@
$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) {
@@ -2194,7 +2281,8 @@
//As we've caught all exceptions, just report whatever the last one was
if ($this->exceptions && null !== $lastexception) {
throw $lastexception;
- } elseif ($this->exceptions) {
+ }
+ if ($this->exceptions) {
// no exception was thrown, likely $this->smtp->connect() failed
$message = $this->getSmtpErrorMessage('connect_host');
throw new Exception($message);
@@ -2390,7 +2478,7 @@
*/
public function addrFormat($addr)
{
- if (empty($addr[1])) { //No name provided
+ if (!isset($addr[1]) || ($addr[1] === '')) { //No name provided
return $this->secureHeader($addr[0]);
}
@@ -2777,10 +2865,7 @@
{
$body = '';
//Create unique IDs and preset boundaries
- $this->uniqueid = $this->generateId();
- $this->boundary[1] = 'b1_' . $this->uniqueid;
- $this->boundary[2] = 'b2_' . $this->uniqueid;
- $this->boundary[3] = 'b3_' . $this->uniqueid;
+ $this->setBoundaries();
if ($this->sign_key_file) {
$body .= $this->getMailMIME() . static::$LE;
@@ -2816,7 +2901,7 @@
$altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE;
}
//Use this as a preamble in all multipart message types
- $mimepre = 'This is a multi-part message in MIME format.' . static::$LE . static::$LE;
+ $mimepre = '';
switch ($this->message_type) {
case 'inline':
$body .= $mimepre;
@@ -3053,6 +3138,18 @@
}
/**
+ * Get the boundaries that this message will use
+ * @return array
+ */
+ public function getBoundaries()
+ {
+ if (empty($this->boundary)) {
+ $this->setBoundaries();
+ }
+ return $this->boundary;
+ }
+
+ /**
* Return the start of a message boundary.
*
* @param string $boundary
@@ -3707,20 +3804,21 @@
* These differ from 'regular' attachments in that they are intended to be
* displayed inline with the message, not just attached for download.
* This is used in HTML messages that embed the images
- * the HTML refers to using the $cid value.
+ * the HTML refers to using the `$cid` value in `img` tags, for example `<img src="cid:mylogo">`.
* Never use a user-supplied path to a file!
*
* @param string $path Path to the attachment
* @param string $cid Content ID of the attachment; Use this to reference
* the content when using an embedded image in HTML
- * @param string $name Overrides the attachment name
- * @param string $encoding File encoding (see $Encoding)
- * @param string $type File MIME type
- * @param string $disposition Disposition to use
- *
- * @throws Exception
+ * @param string $name Overrides the attachment filename
+ * @param string $encoding File encoding (see $Encoding) defaults to `base64`
+ * @param string $type File MIME type (by default mapped from the `$path` filename's extension)
+ * @param string $disposition Disposition to use: `inline` (default) or `attachment`
+ * (unlikely you want this – {@see `addAttachment()`} instead)
*
* @return bool True on successfully adding an attachment
+ * @throws Exception
+ *
*/
public function addEmbeddedImage(
$path,
@@ -4005,6 +4103,79 @@
}
/**
+ * 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.
*
* @param string $msg
@@ -4098,12 +4269,8 @@
//Is it a valid IPv4 address?
return filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
}
- if (filter_var('http://' . $host, FILTER_VALIDATE_URL) !== false) {
- //Is it a syntactically valid hostname?
- return true;
- }
-
- return false;
+ //Is it a syntactically valid hostname (when embeded in a URL)?
+ return filter_var('http://' . $host, FILTER_VALIDATE_URL) !== false;
}
/**
@@ -4172,6 +4339,7 @@
* @param string $name Custom header name
* @param string|null $value Header value
*
+ * @return bool True if a header was set successfully
* @throws Exception
*/
public function addCustomHeader($name, $value = null)
@@ -4466,6 +4634,7 @@
'ics' => 'text/calendar',
'xml' => 'text/xml',
'xsl' => 'text/xml',
+ 'csv' => 'text/csv',
'wmv' => 'video/x-ms-wmv',
'mpeg' => 'video/mpeg',
'mpe' => 'video/mpeg',
@@ -4573,7 +4742,7 @@
public function set($name, $value = '')
{
if (property_exists($this, $name)) {
- $this->$name = $value;
+ $this->{$name} = $value;
return true;
}
@@ -4620,15 +4789,27 @@
}
/**
- * Remove trailing breaks from a string.
+ * Remove trailing whitespace from a string.
+ *
+ * @param string $text
+ *
+ * @return string The text to remove whitespace from
+ */
+ public static function stripTrailingWSP($text)
+ {
+ return rtrim($text, " \r\n\t");
+ }
+
+ /**
+ * Strip trailing line breaks from a string.
*
* @param string $text
*
* @return string The text to remove breaks from
*/
- public static function stripTrailingWSP($text)
+ public static function stripTrailingBreaks($text)
{
- return rtrim($text, " \r\n\t");
+ return rtrim($text, "\r\n");
}
/**
@@ -4794,7 +4975,7 @@
$body = static::normalizeBreaks($body, self::CRLF);
//Reduce multiple trailing line breaks to a single one
- return static::stripTrailingWSP($body) . self::CRLF;
+ return static::stripTrailingBreaks($body) . self::CRLF;
}
/**