--- a/wp/wp-includes/PHPMailer/PHPMailer.php Tue Dec 15 15:52:01 2020 +0100
+++ b/wp/wp-includes/PHPMailer/PHPMailer.php Wed Sep 21 18:19:35 2022 +0200
@@ -1,4 +1,5 @@
<?php
+
/**
* PHPMailer - PHP email creation and transport class.
* PHP Version 5.5.
@@ -9,7 +10,7 @@
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
- * @copyright 2012 - 2019 Marcus Bointon
+ * @copyright 2012 - 2020 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
@@ -388,11 +389,11 @@
* SMTP class debug output mode.
* Debug output level.
* Options:
- * * SMTP::DEBUG_OFF: No output
- * * SMTP::DEBUG_CLIENT: Client messages
- * * SMTP::DEBUG_SERVER: Client and server messages
- * * SMTP::DEBUG_CONNECTION: As SERVER plus connection status
- * * SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed
+ * @see SMTP::DEBUG_OFF: No output
+ * @see SMTP::DEBUG_CLIENT: Client messages
+ * @see SMTP::DEBUG_SERVER: Client and server messages
+ * @see SMTP::DEBUG_CONNECTION: As SERVER plus connection status
+ * @see SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed
*
* @see SMTP::$do_debug
*
@@ -427,9 +428,11 @@
public $Debugoutput = 'echo';
/**
- * Whether to keep SMTP connection open after each message.
- * If this is set to true then to close the connection
- * requires an explicit call to smtpClose().
+ * Whether to keep the SMTP connection open after each message.
+ * If this is set to true then the connection will remain open after a send,
+ * and closing the connection will require an explicit call to smtpClose().
+ * It's a good idea to use this if you are sending multiple messages as it reduces overhead.
+ * See the mailing list example for how to use it.
*
* @var bool
*/
@@ -441,6 +444,8 @@
* Only supported in `mail` and `sendmail` transports, not in SMTP.
*
* @var bool
+ *
+ * @deprecated 6.0.0 PHPMailer isn't a mailing list manager!
*/
public $SingleTo = false;
@@ -745,7 +750,7 @@
*
* @var string
*/
- const VERSION = '6.1.6';
+ const VERSION = '6.5.0';
/**
* Error severity: message only, continue processing.
@@ -859,18 +864,25 @@
$subject = $this->encodeHeader($this->secureHeader($subject));
}
//Calling mail() with null params breaks
+ $this->edebug('Sending with mail()');
+ $this->edebug('Sendmail path: ' . ini_get('sendmail_path'));
+ $this->edebug("Envelope sender: {$this->Sender}");
+ $this->edebug("To: {$to}");
+ $this->edebug("Subject: {$subject}");
+ $this->edebug("Headers: {$header}");
if (!$this->UseSendmailOptions || null === $params) {
$result = @mail($to, $subject, $body, $header);
} else {
+ $this->edebug("Additional params: {$params}");
$result = @mail($to, $subject, $body, $header, $params);
}
-
+ $this->edebug('Result: ' . ($result ? 'true' : 'false'));
return $result;
}
/**
- * Output debugging info via user-defined method.
- * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
+ * Output debugging info via a user-defined method.
+ * Only generates output if debug output is enabled.
*
* @see PHPMailer::$Debugoutput
* @see PHPMailer::$SMTPDebug
@@ -897,6 +909,7 @@
switch ($this->Debugoutput) {
case 'error_log':
//Don't output, just log
+ /** @noinspection ForgottenDebugOutputInspection */
error_log($str);
break;
case 'html':
@@ -1066,7 +1079,7 @@
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
$pos = strrpos($address, '@');
if (false === $pos) {
- // At-sign is missing.
+ //At-sign is missing.
$error_message = sprintf(
'%s (%s): %s',
$this->lang('invalid_address'),
@@ -1082,7 +1095,7 @@
return false;
}
$params = [$kind, $address, $name];
- // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
+ //Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) {
if ('Reply-To' !== $kind) {
if (!array_key_exists($address, $this->RecipientsQueue)) {
@@ -1099,7 +1112,7 @@
return false;
}
- // Immediately add standard addresses without IDN.
+ //Immediately add standard addresses without IDN.
return call_user_func_array([$this, 'addAnAddress'], $params);
}
@@ -1182,9 +1195,20 @@
//Use this built-in parser if it's available
$list = imap_rfc822_parse_adrlist($addrstr, '');
foreach ($list as $address) {
- if (('.SYNTAX-ERROR.' !== $address->host) && static::validateAddress(
- $address->mailbox . '@' . $address->host
- )) {
+ if (
+ ('.SYNTAX-ERROR.' !== $address->host) && static::validateAddress(
+ $address->mailbox . '@' . $address->host
+ )
+ ) {
+ //Decode the name part if it's present and encoded
+ if (
+ property_exists($address, 'personal') &&
+ extension_loaded('mbstring') &&
+ preg_match('/^=\?.*\?=$/', $address->personal)
+ ) {
+ $address->personal = mb_decode_mimeheader($address->personal);
+ }
+
$addresses[] = [
'name' => (property_exists($address, 'personal') ? $address->personal : ''),
'address' => $address->mailbox . '@' . $address->host,
@@ -1208,9 +1232,15 @@
} else {
list($name, $email) = explode('<', $address);
$email = trim(str_replace('>', '', $email));
+ $name = trim($name);
if (static::validateAddress($email)) {
+ //If this name is encoded, decode it
+ if (preg_match('/^=\?.*\?=$/', $name)) {
+ $name = mb_decode_mimeheader($name);
+ }
$addresses[] = [
- 'name' => trim(str_replace(['"', "'"], '', $name)),
+ //Remove any surrounding quotes and spaces from the name
+ 'name' => trim($name, '\'" '),
'address' => $email,
];
}
@@ -1236,9 +1266,10 @@
{
$address = trim($address);
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
- // Don't validate now addresses with IDN. Will be done in send().
+ //Don't validate now addresses with IDN. Will be done in send().
$pos = strrpos($address, '@');
- if ((false === $pos)
+ if (
+ (false === $pos)
|| ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported())
&& !static::validateAddress($address))
) {
@@ -1306,8 +1337,9 @@
if (null === $patternselect) {
$patternselect = static::$validator;
}
- if (is_callable($patternselect)) {
- return $patternselect($address);
+ //Don't allow strings as callables, see SECURITY.md and CVE-2021-3603
+ if (is_callable($patternselect) && !is_string($patternselect)) {
+ return call_user_func($patternselect, $address);
}
//Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) {
@@ -1348,7 +1380,7 @@
/*
* This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
*
- * @see http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
+ * @see https://html.spec.whatwg.org/#e-mail-state-(type=email)
*/
return (bool) preg_match(
'/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
@@ -1388,25 +1420,29 @@
*/
public function punyencodeAddress($address)
{
- // Verify we have required functions, CharSet, and at-sign.
+ //Verify we have required functions, CharSet, and at-sign.
$pos = strrpos($address, '@');
- if (!empty($this->CharSet) &&
+ if (
+ !empty($this->CharSet) &&
false !== $pos &&
static::idnSupported()
) {
$domain = substr($address, ++$pos);
- // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
+ //Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) {
- $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
+ //Convert the domain from whatever charset it's in to UTF-8
+ $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet);
//Ignore IDE complaints about this line - method signature changed in PHP 5.4
$errorcode = 0;
if (defined('INTL_IDNA_VARIANT_UTS46')) {
- // phpcs:ignore PHPCompatibility.ParameterValues.NewIDNVariantDefault.NotSet
- $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_UTS46);
+ //Use the current punycode standard (appeared in PHP 7.2)
+ $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_UTS46);
} elseif (defined('INTL_IDNA_VARIANT_2003')) {
+ //Fall back to this old, deprecated/removed encoding
// phpcs:ignore PHPCompatibility.Constants.RemovedConstants.intl_idna_variant_2003Deprecated
- $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_2003);
+ $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);
}
@@ -1455,8 +1491,9 @@
*/
public function preSend()
{
- if ('smtp' === $this->Mailer
- || ('mail' === $this->Mailer && stripos(PHP_OS, 'WIN') === 0)
+ if (
+ 'smtp' === $this->Mailer
+ || ('mail' === $this->Mailer && (\PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0))
) {
//SMTP mandates RFC-compliant line endings
//and it's also used with mail() on Windows
@@ -1466,9 +1503,10 @@
static::setLE(PHP_EOL);
}
//Check for buggy PHP versions that add a header with an incorrect line break
- if ('mail' === $this->Mailer
- && ((PHP_VERSION_ID >= 70000 && PHP_VERSION_ID < 70017)
- || (PHP_VERSION_ID >= 70100 && PHP_VERSION_ID < 70103))
+ if (
+ 'mail' === $this->Mailer
+ && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017)
+ || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103))
&& ini_get('mail.add_x_header') === '1'
&& stripos(PHP_OS, 'WIN') === 0
) {
@@ -1481,10 +1519,10 @@
}
try {
- $this->error_count = 0; // Reset errors
+ $this->error_count = 0; //Reset errors
$this->mailHeader = '';
- // Dequeue recipient and Reply-To addresses with IDN
+ //Dequeue recipient and Reply-To addresses with IDN
foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
$params[1] = $this->punyencodeAddress($params[1]);
call_user_func_array([$this, 'addAnAddress'], $params);
@@ -1493,7 +1531,7 @@
throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
}
- // Validate From, Sender, and ConfirmReadingTo addresses
+ //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)) {
@@ -1517,29 +1555,29 @@
}
}
- // Set whether the message is multipart/alternative
+ //Set whether the message is multipart/alternative
if ($this->alternativeExists()) {
$this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE;
}
$this->setMessageType();
- // Refuse to send an empty message unless we are specifically allowing it
+ //Refuse to send an empty message unless we are specifically allowing it
if (!$this->AllowEmpty && empty($this->Body)) {
throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
}
//Trim subject consistently
$this->Subject = trim($this->Subject);
- // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
+ //Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
$this->MIMEHeader = '';
$this->MIMEBody = $this->createBody();
- // createBody may have added some headers, so retain them
+ //createBody may have added some headers, so retain them
$tempheaders = $this->MIMEHeader;
$this->MIMEHeader = $this->createHeader();
$this->MIMEHeader .= $tempheaders;
- // To capture the complete message when using mail(), create
- // an extra header list which createHeader() doesn't fold in
+ //To capture the complete message when using mail(), create
+ //an extra header list which createHeader() doesn't fold in
if ('mail' === $this->Mailer) {
if (count($this->to) > 0) {
$this->mailHeader .= $this->addrAppend('To', $this->to);
@@ -1552,8 +1590,9 @@
);
}
- // Sign with DKIM if enabled
- if (!empty($this->DKIM_domain)
+ //Sign with DKIM if enabled
+ if (
+ !empty($this->DKIM_domain)
&& !empty($this->DKIM_selector)
&& (!empty($this->DKIM_private_string)
|| (!empty($this->DKIM_private)
@@ -1592,7 +1631,7 @@
public function postSend()
{
try {
- // Choose the mailer and send through it
+ //Choose the mailer and send through it
switch ($this->Mailer) {
case 'sendmail':
case 'qmail':
@@ -1610,6 +1649,9 @@
return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
}
} catch (Exception $exc) {
+ if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true) {
+ $this->smtp->reset();
+ }
$this->setError($exc->getMessage());
$this->edebug($exc->getMessage());
if ($this->exceptions) {
@@ -1634,22 +1676,44 @@
*/
protected function sendmailSend($header, $body)
{
+ if ($this->Mailer === 'qmail') {
+ $this->edebug('Sending with qmail');
+ } else {
+ $this->edebug('Sending with sendmail');
+ }
$header = static::stripTrailingWSP($header) . static::$LE . static::$LE;
-
- // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
- if (!empty($this->Sender) && self::isShellSafe($this->Sender)) {
- if ('qmail' === $this->Mailer) {
+ //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
+ //A space after `-f` is optional, but there is a long history of its presence
+ //causing problems, so we don't use one
+ //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
+ //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
+ //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
+ //Example problem: https://www.drupal.org/node/1057954
+ if (empty($this->Sender) && !empty(ini_get('sendmail_from'))) {
+ //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.
+ 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 ('qmail' === $this->Mailer) {
- $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';
}
$sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
+ $this->edebug('Sendmail path: ' . $this->Sendmail);
+ $this->edebug('Sendmail command: ' . $sendmail);
+ $this->edebug('Envelope sender: ' . $this->Sender);
+ $this->edebug("Headers: {$header}");
if ($this->SingleTo) {
foreach ($this->SingleToArray as $toAddr) {
@@ -1657,13 +1721,15 @@
if (!$mail) {
throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
}
+ $this->edebug("To: {$toAddr}");
fwrite($mail, 'To: ' . $toAddr . "\n");
fwrite($mail, $header);
fwrite($mail, $body);
$result = pclose($mail);
+ $addrinfo = static::parseAddresses($toAddr);
$this->doCallback(
($result === 0),
- [$toAddr],
+ [[$addrinfo['address'], $addrinfo['name']]],
$this->cc,
$this->bcc,
$this->Subject,
@@ -1671,6 +1737,7 @@
$this->From,
[]
);
+ $this->edebug("Result: " . ($result === 0 ? 'true' : 'false'));
if (0 !== $result) {
throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
}
@@ -1693,6 +1760,7 @@
$this->From,
[]
);
+ $this->edebug("Result: " . ($result === 0 ? 'true' : 'false'));
if (0 !== $result) {
throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
}
@@ -1713,8 +1781,9 @@
*/
protected static function isShellSafe($string)
{
- // Future-proof
- if (escapeshellcmd($string) !== $string
+ //Future-proof
+ if (
+ escapeshellcmd($string) !== $string
|| !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
) {
return false;
@@ -1725,9 +1794,9 @@
for ($i = 0; $i < $length; ++$i) {
$c = $string[$i];
- // All other characters have a special meaning in at least one common shell, including = and +.
- // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
- // Note that this does permit non-Latin alphanumeric characters based on the current locale.
+ //All other characters have a special meaning in at least one common shell, including = and +.
+ //Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
+ //Note that this does permit non-Latin alphanumeric characters based on the current locale.
if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
return false;
}
@@ -1747,7 +1816,28 @@
*/
protected static function isPermittedPath($path)
{
- return !preg_match('#^[a-z]+://#i', $path);
+ //Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1
+ return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path);
+ }
+
+ /**
+ * Check whether a file path is safe, accessible, and readable.
+ *
+ * @param string $path A relative or absolute path to a file
+ *
+ * @return bool
+ */
+ protected static function fileIsAccessible($path)
+ {
+ if (!static::isPermittedPath($path)) {
+ return false;
+ }
+ $readable = file_exists($path);
+ //If not a UNC path (expected to start with \\), check read permission, see #2069
+ if (strpos($path, '\\\\') !== 0) {
+ $readable = $readable && is_readable($path);
+ }
+ return $readable;
}
/**
@@ -1780,11 +1870,15 @@
//Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
//Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
//Example problem: https://www.drupal.org/node/1057954
- // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
- if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) {
- $params = sprintf('-f%s', $this->Sender);
+ //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
+ if (empty($this->Sender) && !empty(ini_get('sendmail_from'))) {
+ //PHP config has a sender address we can use
+ $this->Sender = ini_get('sendmail_from');
}
if (!empty($this->Sender) && static::validateAddress($this->Sender)) {
+ if (self::isShellSafe($this->Sender)) {
+ $params = sprintf('-f%s', $this->Sender);
+ }
$old_from = ini_get('sendmail_from');
ini_set('sendmail_from', $this->Sender);
}
@@ -1792,7 +1886,17 @@
if ($this->SingleTo && count($toArr) > 1) {
foreach ($toArr as $toAddr) {
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
- $this->doCallback($result, [$toAddr], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
+ $addrinfo = static::parseAddresses($toAddr);
+ $this->doCallback(
+ $result,
+ [[$addrinfo['address'], $addrinfo['name']]],
+ $this->cc,
+ $this->bcc,
+ $this->Subject,
+ $body,
+ $this->From,
+ []
+ );
}
} else {
$result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
@@ -1870,7 +1974,7 @@
}
$callbacks = [];
- // Attempt to send to all recipients
+ //Attempt to send to all recipients
foreach ([$this->to, $this->cc, $this->bcc] as $togroup) {
foreach ($togroup as $to) {
if (!$this->smtp->recipient($to[0], $this->dsn)) {
@@ -1881,11 +1985,11 @@
$isSent = true;
}
- $callbacks[] = ['issent'=>$isSent, 'to'=>$to[0]];
+ $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]];
}
}
- // Only send the DATA command if we have viable recipients
+ //Only send the DATA command if we have viable recipients
if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) {
throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL);
}
@@ -1902,7 +2006,7 @@
foreach ($callbacks as $cb) {
$this->doCallback(
$cb['issent'],
- [$cb['to']],
+ [[$cb['to'], $cb['name']]],
[],
[],
$this->Subject,
@@ -1947,7 +2051,7 @@
$options = $this->SMTPOptions;
}
- // Already connected?
+ //Already connected?
if ($this->smtp->connected()) {
return true;
}
@@ -1961,20 +2065,22 @@
foreach ($hosts as $hostentry) {
$hostinfo = [];
- if (!preg_match(
- '/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/',
- trim($hostentry),
- $hostinfo
- )) {
+ if (
+ !preg_match(
+ '/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/',
+ trim($hostentry),
+ $hostinfo
+ )
+ ) {
$this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry));
- // Not a valid host entry
+ //Not a valid host entry
continue;
}
- // $hostinfo[1]: optional ssl or tls prefix
- // $hostinfo[2]: the hostname
- // $hostinfo[3]: optional port number
- // The host string prefix can temporarily override the current setting for SMTPSecure
- // If it's not specified, the default value is used
+ //$hostinfo[1]: optional ssl or tls prefix
+ //$hostinfo[2]: the hostname
+ //$hostinfo[3]: optional port number
+ //The host string prefix can temporarily override the current setting for SMTPSecure
+ //If it's not specified, the default value is used
//Check the host name is a valid name or IP address before trying to use it
if (!static::isValidHost($hostinfo[2])) {
@@ -1986,11 +2092,11 @@
$tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure);
if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) {
$prefix = 'ssl://';
- $tls = false; // Can't have SSL and TLS at the same time
+ $tls = false; //Can't have SSL and TLS at the same time
$secure = static::ENCRYPTION_SMTPS;
} elseif ('tls' === $hostinfo[1]) {
$tls = true;
- // tls doesn't use a prefix
+ //TLS doesn't use a prefix
$secure = static::ENCRYPTION_STARTTLS;
}
//Do we need the OpenSSL extension?
@@ -2003,7 +2109,12 @@
}
$host = $hostinfo[2];
$port = $this->Port;
- if (array_key_exists(3, $hostinfo) && is_numeric($hostinfo[3]) && $hostinfo[3] > 0 && $hostinfo[3] < 65536) {
+ if (
+ array_key_exists(3, $hostinfo) &&
+ is_numeric($hostinfo[3]) &&
+ $hostinfo[3] > 0 &&
+ $hostinfo[3] < 65536
+ ) {
$port = (int) $hostinfo[3];
}
if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
@@ -2015,10 +2126,10 @@
}
$this->smtp->hello($hello);
//Automatically enable TLS encryption if:
- // * it's not disabled
- // * we have openssl extension
- // * we are not already using SSL
- // * the server offers STARTTLS
+ //* it's not disabled
+ //* we have openssl extension
+ //* we are not already using SSL
+ //* the server offers STARTTLS
if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) {
$tls = true;
}
@@ -2026,15 +2137,17 @@
if (!$this->smtp->startTLS()) {
throw new Exception($this->lang('connect_host'));
}
- // We must resend EHLO after TLS negotiation
+ //We must resend EHLO after TLS negotiation
$this->smtp->hello($hello);
}
- if ($this->SMTPAuth && !$this->smtp->authenticate(
- $this->Username,
- $this->Password,
- $this->AuthType,
- $this->oauth
- )) {
+ if (
+ $this->SMTPAuth && !$this->smtp->authenticate(
+ $this->Username,
+ $this->Password,
+ $this->AuthType,
+ $this->oauth
+ )
+ ) {
throw new Exception($this->lang('authenticate'));
}
@@ -2042,14 +2155,14 @@
} catch (Exception $exc) {
$lastexception = $exc;
$this->edebug($exc->getMessage());
- // We must have connected, but then failed TLS or Auth, so close connection nicely
+ //We must have connected, but then failed TLS or Auth, so close connection nicely
$this->smtp->quit();
}
}
}
- // If we get here, all connection attempts have failed, so close connection hard
+ //If we get here, all connection attempts have failed, so close connection hard
$this->smtp->close();
- // As we've caught all exceptions, just report whatever the last one was
+ //As we've caught all exceptions, just report whatever the last one was
if ($this->exceptions && null !== $lastexception) {
throw $lastexception;
}
@@ -2074,13 +2187,14 @@
* The default language is English.
*
* @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
- * @param string $lang_path Path to the language file directory, with trailing separator (slash)
+ * @param string $lang_path Path to the language file directory, with trailing separator (slash).D
+ * Do not set this from user input!
*
* @return bool
*/
public function setLanguage($langcode = 'en', $lang_path = '')
{
- // Backwards compatibility for renamed language codes
+ //Backwards compatibility for renamed language codes
$renamed_langcodes = [
'br' => 'pt_br',
'cz' => 'cs',
@@ -2092,11 +2206,11 @@
'am' => 'hy',
];
- if (isset($renamed_langcodes[$langcode])) {
+ if (array_key_exists($langcode, $renamed_langcodes)) {
$langcode = $renamed_langcodes[$langcode];
}
- // Define full set of translatable strings in English
+ //Define full set of translatable strings in English
$PHPMAILER_LANG = [
'authenticate' => 'SMTP Error: Could not authenticate.',
'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
@@ -2121,7 +2235,7 @@
'extension_missing' => 'Extension missing: ',
];
if (empty($lang_path)) {
- // Calculate an absolute path so it can work if CWD is not here
+ //Calculate an absolute path so it can work if CWD is not here
$lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
}
//Validate $langcode
@@ -2130,20 +2244,38 @@
}
$foundlang = true;
$lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
- // There is no English translation file
+ //There is no English translation file
if ('en' !== $langcode) {
- // Make sure language file path is readable
- if (!static::isPermittedPath($lang_file) || !file_exists($lang_file)) {
+ //Make sure language file path is readable
+ if (!static::fileIsAccessible($lang_file)) {
$foundlang = false;
} else {
- // Overwrite language-specific strings.
- // This way we'll never have missing translation keys.
- $foundlang = include $lang_file;
+ //$foundlang = include $lang_file;
+ $lines = file($lang_file);
+ foreach ($lines as $line) {
+ //Translation file lines look like this:
+ //$PHPMAILER_LANG['authenticate'] = 'SMTP-Fehler: Authentifizierung fehlgeschlagen.';
+ //These files are parsed as text and not PHP so as to avoid the possibility of code injection
+ //See https://blog.stevenlevithan.com/archives/match-quoted-string
+ $matches = [];
+ if (
+ preg_match(
+ '/^\$PHPMAILER_LANG\[\'([a-z\d_]+)\'\]\s*=\s*(["\'])(.+)*?\2;/',
+ $line,
+ $matches
+ ) &&
+ //Ignore unknown translation keys
+ array_key_exists($matches[1], $PHPMAILER_LANG)
+ ) {
+ //Overwrite language-specific strings so we'll never have missing translation keys.
+ $PHPMAILER_LANG[$matches[1]] = (string)$matches[3];
+ }
+ }
}
}
$this->language = $PHPMAILER_LANG;
- return (bool) $foundlang; // Returns false if language not found
+ return $foundlang; //Returns false if language not found
}
/**
@@ -2187,7 +2319,7 @@
*/
public function addrFormat($addr)
{
- if (empty($addr[1])) { // No name provided
+ if (empty($addr[1])) { //No name provided
return $this->secureHeader($addr[0]);
}
@@ -2214,8 +2346,8 @@
} else {
$soft_break = static::$LE;
}
- // If utf-8 encoding is used, we will need to make sure we don't
- // split multibyte characters when we wrap
+ //If utf-8 encoding is used, we will need to make sure we don't
+ //split multibyte characters when we wrap
$is_utf8 = static::CHARSET_UTF8 === strtolower($this->CharSet);
$lelen = strlen(static::$LE);
$crlflen = strlen(static::$LE);
@@ -2315,29 +2447,29 @@
$lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
$encodedCharPos = strpos($lastChunk, '=');
if (false !== $encodedCharPos) {
- // Found start of encoded character byte within $lookBack block.
- // Check the encoded byte value (the 2 chars after the '=')
+ //Found start of encoded character byte within $lookBack block.
+ //Check the encoded byte value (the 2 chars after the '=')
$hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
$dec = hexdec($hex);
if ($dec < 128) {
- // Single byte character.
- // If the encoded char was found at pos 0, it will fit
- // otherwise reduce maxLength to start of the encoded char
+ //Single byte character.
+ //If the encoded char was found at pos 0, it will fit
+ //otherwise reduce maxLength to start of the encoded char
if ($encodedCharPos > 0) {
$maxLength -= $lookBack - $encodedCharPos;
}
$foundSplitPos = true;
} elseif ($dec >= 192) {
- // First byte of a multi byte character
- // Reduce maxLength to split at start of character
+ //First byte of a multi byte character
+ //Reduce maxLength to split at start of character
$maxLength -= $lookBack - $encodedCharPos;
$foundSplitPos = true;
} elseif ($dec < 192) {
- // Middle byte of a multi byte character, look further back
+ //Middle byte of a multi byte character, look further back
$lookBack += 3;
}
} else {
- // No encoded character found
+ //No encoded character found
$foundSplitPos = true;
}
}
@@ -2381,30 +2513,28 @@
$result .= $this->headerLine('Date', '' === $this->MessageDate ? self::rfcDate() : $this->MessageDate);
- // To be created automatically by mail()
- if ($this->SingleTo) {
- if ('mail' !== $this->Mailer) {
+ //The To header is created automatically by mail(), so needs to be omitted here
+ if ('mail' !== $this->Mailer) {
+ if ($this->SingleTo) {
foreach ($this->to as $toaddr) {
$this->SingleToArray[] = $this->addrFormat($toaddr);
}
- }
- } elseif (count($this->to) > 0) {
- if ('mail' !== $this->Mailer) {
+ } elseif (count($this->to) > 0) {
$result .= $this->addrAppend('To', $this->to);
+ } elseif (count($this->cc) === 0) {
+ $result .= $this->headerLine('To', 'undisclosed-recipients:;');
}
- } elseif (count($this->cc) === 0) {
- $result .= $this->headerLine('To', 'undisclosed-recipients:;');
}
-
$result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]);
- // sendmail and mail() extract Cc from the header before sending
+ //sendmail and mail() extract Cc from the header before sending
if (count($this->cc) > 0) {
$result .= $this->addrAppend('Cc', $this->cc);
}
- // sendmail and mail() extract Bcc from the header before sending
- if ((
+ //sendmail and mail() extract Bcc from the header before sending
+ if (
+ (
'sendmail' === $this->Mailer || 'qmail' === $this->Mailer || 'mail' === $this->Mailer
)
&& count($this->bcc) > 0
@@ -2416,13 +2546,13 @@
$result .= $this->addrAppend('Reply-To', $this->ReplyTo);
}
- // mail() sets the subject itself
+ //mail() sets the subject itself
if ('mail' !== $this->Mailer) {
$result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
}
- // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
- // https://tools.ietf.org/html/rfc5322#section-3.6.4
+ //Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
+ //https://tools.ietf.org/html/rfc5322#section-3.6.4
if ('' !== $this->MessageID && preg_match('/^<.*@.*>$/', $this->MessageID)) {
$this->lastMessageID = $this->MessageID;
} else {
@@ -2448,7 +2578,7 @@
$result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
}
- // Add custom headers
+ //Add custom headers
foreach ($this->CustomHeader as $header) {
$result .= $this->headerLine(
trim($header[0]),
@@ -2490,28 +2620,24 @@
$result .= $this->textLine(' boundary="' . $this->boundary[1] . '"');
break;
default:
- // Catches case 'plain': and case '':
+ //Catches case 'plain': and case '':
$result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
$ismultipart = false;
break;
}
- // RFC1341 part 5 says 7bit is assumed if not specified
+ //RFC1341 part 5 says 7bit is assumed if not specified
if (static::ENCODING_7BIT !== $this->Encoding) {
- // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
+ //RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
if ($ismultipart) {
if (static::ENCODING_8BIT === $this->Encoding) {
$result .= $this->headerLine('Content-Transfer-Encoding', static::ENCODING_8BIT);
}
- // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
+ //The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
} else {
$result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
}
}
- if ('mail' !== $this->Mailer) {
-// $result .= static::$LE;
- }
-
return $result;
}
@@ -2780,7 +2906,7 @@
$body .= $this->attachAll('attachment', $this->boundary[1]);
break;
default:
- // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
+ //Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
//Reset the `Encoding` property in case we changed it for line length reasons
$this->Encoding = $bodyEncoding;
$body .= $this->encodeString($this->Body, $this->Encoding);
@@ -2871,7 +2997,7 @@
$result .= $this->textLine('--' . $boundary);
$result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
$result .= static::$LE;
- // RFC1341 part 5 says 7bit is assumed if not specified
+ //RFC1341 part 5 says 7bit is assumed if not specified
if (static::ENCODING_7BIT !== $encoding) {
$result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
}
@@ -2950,7 +3076,7 @@
* @param string $path Path to the attachment
* @param string $name Overrides the attachment name
* @param string $encoding File encoding (see $Encoding)
- * @param string $type File extension (MIME) type
+ * @param string $type MIME type, e.g. `image/jpeg`; determined automatically from $path if not specified
* @param string $disposition Disposition to use
*
* @throws Exception
@@ -2965,11 +3091,11 @@
$disposition = 'attachment'
) {
try {
- if (!static::isPermittedPath($path) || !@is_file($path) || !is_readable($path)) {
+ if (!static::fileIsAccessible($path)) {
throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
}
- // If a MIME type is not specified, try to work it out from the file name
+ //If a MIME type is not specified, try to work it out from the file name
if ('' === $type) {
$type = static::filenameToType($path);
}
@@ -2978,7 +3104,6 @@
if ('' === $name) {
$name = $filename;
}
-
if (!$this->validateEncoding($encoding)) {
throw new Exception($this->lang('encoding') . $encoding);
}
@@ -2989,7 +3114,7 @@
2 => $name,
3 => $encoding,
4 => $type,
- 5 => false, // isStringAttachment
+ 5 => false, //isStringAttachment
6 => $disposition,
7 => $name,
];
@@ -3029,16 +3154,16 @@
*/
protected function attachAll($disposition_type, $boundary)
{
- // Return text of body
+ //Return text of body
$mime = [];
$cidUniq = [];
$incl = [];
- // Add all attachments
+ //Add all attachments
foreach ($this->attachment as $attachment) {
- // Check if it is a valid disposition_filter
+ //Check if it is a valid disposition_filter
if ($attachment[6] === $disposition_type) {
- // Check for string attachment
+ //Check for string attachment
$string = '';
$path = '';
$bString = $attachment[5];
@@ -3079,7 +3204,7 @@
static::$LE
);
}
- // RFC1341 part 5 says 7bit is assumed if not specified
+ //RFC1341 part 5 says 7bit is assumed if not specified
if (static::ENCODING_7BIT !== $encoding) {
$mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, static::$LE);
}
@@ -3089,7 +3214,7 @@
$mime[] = 'Content-ID: <' . $this->encodeHeader($this->secureHeader($cid)) . '>' . static::$LE;
}
- // Allow for bypassing the Content-Disposition header
+ //Allow for bypassing the Content-Disposition header
if (!empty($disposition)) {
$encoded_name = $this->encodeHeader($this->secureHeader($name));
if (!empty($encoded_name)) {
@@ -3110,7 +3235,7 @@
$mime[] = static::$LE;
}
- // Encode as string attachment
+ //Encode as string attachment
if ($bString) {
$mime[] = $this->encodeString($string, $encoding);
} else {
@@ -3140,7 +3265,7 @@
protected function encodeFile($path, $encoding = self::ENCODING_BASE64)
{
try {
- if (!static::isPermittedPath($path) || !file_exists($path) || !is_readable($path)) {
+ if (!static::fileIsAccessible($path)) {
throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
}
$file_buffer = file_get_contents($path);
@@ -3186,7 +3311,7 @@
case static::ENCODING_7BIT:
case static::ENCODING_8BIT:
$encoded = static::normalizeBreaks($str);
- // Make sure it ends with a line break
+ //Make sure it ends with a line break
if (substr($encoded, -(strlen(static::$LE))) !== static::$LE) {
$encoded .= static::$LE;
}
@@ -3224,7 +3349,7 @@
switch (strtolower($position)) {
case 'phrase':
if (!preg_match('/[\200-\377]/', $str)) {
- // Can't use addslashes as we don't know the value of magic_quotes_sybase
+ //Can't use addslashes as we don't know the value of magic_quotes_sybase
$encoded = addcslashes($str, "\0..\37\177\\\"");
if (($str === $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
return $encoded;
@@ -3250,7 +3375,7 @@
$charset = static::CHARSET_ASCII;
}
- // Q/B encoding adds 8 chars and the charset ("` =?<charset>?[QB]?<content>?=`").
+ //Q/B encoding adds 8 chars and the charset ("` =?<charset>?[QB]?<content>?=`").
$overhead = 8 + strlen($charset);
if ('mail' === $this->Mailer) {
@@ -3259,26 +3384,26 @@
$maxlen = static::MAX_LINE_LENGTH - $overhead;
}
- // Select the encoding that produces the shortest output and/or prevents corruption.
+ //Select the encoding that produces the shortest output and/or prevents corruption.
if ($matchcount > strlen($str) / 3) {
- // More than 1/3 of the content needs encoding, use B-encode.
+ //More than 1/3 of the content needs encoding, use B-encode.
$encoding = 'B';
} elseif ($matchcount > 0) {
- // Less than 1/3 of the content needs encoding, use Q-encode.
+ //Less than 1/3 of the content needs encoding, use Q-encode.
$encoding = 'Q';
} elseif (strlen($str) > $maxlen) {
- // No encoding needed, but value exceeds max line length, use Q-encode to prevent corruption.
+ //No encoding needed, but value exceeds max line length, use Q-encode to prevent corruption.
$encoding = 'Q';
} else {
- // No reformatting needed
+ //No reformatting needed
$encoding = false;
}
switch ($encoding) {
case 'B':
if ($this->hasMultiBytes($str)) {
- // Use a custom function which correctly encodes and wraps long
- // multibyte strings without breaking lines within a character
+ //Use a custom function which correctly encodes and wraps long
+ //multibyte strings without breaking lines within a character
$encoded = $this->base64EncodeWrapMB($str, "\n");
} else {
$encoded = base64_encode($str);
@@ -3313,7 +3438,7 @@
return strlen($str) > mb_strlen($str, $this->CharSet);
}
- // Assume no multibytes (we can't handle without mbstring functions anyway)
+ //Assume no multibytes (we can't handle without mbstring functions anyway)
return false;
}
@@ -3351,11 +3476,11 @@
}
$mb_length = mb_strlen($str, $this->CharSet);
- // Each line must have length <= 75, including $start and $end
+ //Each line must have length <= 75, including $start and $end
$length = 75 - strlen($start) - strlen($end);
- // Average multi-byte ratio
+ //Average multi-byte ratio
$ratio = $mb_length / strlen($str);
- // Base64 has a 4:3 ratio
+ //Base64 has a 4:3 ratio
$avgLength = floor($length * $ratio * .75);
$offset = 0;
@@ -3370,7 +3495,7 @@
$encoded .= $chunk . $linebreak;
}
- // Chomp the last linefeed
+ //Chomp the last linefeed
return substr($encoded, 0, -strlen($linebreak));
}
@@ -3399,12 +3524,12 @@
*/
public function encodeQ($str, $position = 'text')
{
- // There should not be any EOL in the string
+ //There should not be any EOL in the string
$pattern = '';
$encoded = str_replace(["\r", "\n"], '', $str);
switch (strtolower($position)) {
case 'phrase':
- // RFC 2047 section 5.3
+ //RFC 2047 section 5.3
$pattern = '^A-Za-z0-9!*+\/ -';
break;
/*
@@ -3417,15 +3542,15 @@
/* Intentional fall through */
case 'text':
default:
- // RFC 2047 section 5.1
- // Replace every high ascii, control, =, ? and _ characters
+ //RFC 2047 section 5.1
+ //Replace every high ascii, control, =, ? and _ characters
$pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
break;
}
$matches = [];
if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
- // If the string contains an '=', make sure it's the first thing we replace
- // so as to avoid double-encoding
+ //If the string contains an '=', make sure it's the first thing we replace
+ //so as to avoid double-encoding
$eqkey = array_search('=', $matches[0], true);
if (false !== $eqkey) {
unset($matches[0][$eqkey]);
@@ -3435,8 +3560,8 @@
$encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
}
}
- // Replace spaces with _ (more readable than =20)
- // RFC 2047 section 4.2(2)
+ //Replace spaces with _ (more readable than =20)
+ //RFC 2047 section 4.2(2)
return str_replace(' ', '_', $encoded);
}
@@ -3463,7 +3588,7 @@
$disposition = 'attachment'
) {
try {
- // If a MIME type is not specified, try to work it out from the file name
+ //If a MIME type is not specified, try to work it out from the file name
if ('' === $type) {
$type = static::filenameToType($filename);
}
@@ -3472,14 +3597,14 @@
throw new Exception($this->lang('encoding') . $encoding);
}
- // Append to $attachment array
+ //Append to $attachment array
$this->attachment[] = [
0 => $string,
1 => $filename,
2 => static::mb_pathinfo($filename, PATHINFO_BASENAME),
3 => $encoding,
4 => $type,
- 5 => true, // isStringAttachment
+ 5 => true, //isStringAttachment
6 => $disposition,
7 => 0,
];
@@ -3526,11 +3651,11 @@
$disposition = 'inline'
) {
try {
- if (!static::isPermittedPath($path) || !@is_file($path) || !is_readable($path)) {
+ if (!static::fileIsAccessible($path)) {
throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
}
- // If a MIME type is not specified, try to work it out from the file name
+ //If a MIME type is not specified, try to work it out from the file name
if ('' === $type) {
$type = static::filenameToType($path);
}
@@ -3544,14 +3669,14 @@
$name = $filename;
}
- // Append to $attachment array
+ //Append to $attachment array
$this->attachment[] = [
0 => $path,
1 => $filename,
2 => $name,
3 => $encoding,
4 => $type,
- 5 => false, // isStringAttachment
+ 5 => false, //isStringAttachment
6 => $disposition,
7 => $cid,
];
@@ -3596,7 +3721,7 @@
$disposition = 'inline'
) {
try {
- // If a MIME type is not specified, try to work it out from the name
+ //If a MIME type is not specified, try to work it out from the name
if ('' === $type && !empty($name)) {
$type = static::filenameToType($name);
}
@@ -3605,14 +3730,14 @@
throw new Exception($this->lang('encoding') . $encoding);
}
- // Append to $attachment array
+ //Append to $attachment array
$this->attachment[] = [
0 => $string,
1 => $name,
2 => $name,
3 => $encoding,
4 => $type,
- 5 => true, // isStringAttachment
+ 5 => true, //isStringAttachment
6 => $disposition,
7 => $cid,
];
@@ -3832,8 +3957,8 @@
*/
public static function rfcDate()
{
- // Set the time zone to whatever the default is to avoid 500 errors
- // Will default to UTC if it's not set properly in php.ini
+ //Set the time zone to whatever the default is to avoid 500 errors
+ //Will default to UTC if it's not set properly in php.ini
date_default_timezone_set(@date_default_timezone_get());
return date('D, j M Y H:i:s O');
@@ -3875,7 +4000,8 @@
public static function isValidHost($host)
{
//Simple syntax limits
- if (empty($host)
+ if (
+ empty($host)
|| !is_string($host)
|| strlen($host) > 256
|| !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+])$/', $host)
@@ -3910,13 +4036,13 @@
protected function lang($key)
{
if (count($this->language) < 1) {
- $this->setLanguage(); // set the default language
+ $this->setLanguage(); //Set the default language
}
if (array_key_exists($key, $this->language)) {
if ('smtp_connect_failed' === $key) {
- //Include a link to troubleshooting docs on SMTP connection failure
- //this is by far the biggest cause of support questions
+ //Include a link to troubleshooting docs on SMTP connection failure.
+ //This is by far the biggest cause of support questions
//but it's usually not PHPMailer's fault.
return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
}
@@ -3951,7 +4077,7 @@
public function addCustomHeader($name, $value = null)
{
if (null === $value && strpos($name, ':') !== false) {
- // Value passed in as name:value
+ //Value passed in as name:value
list($name, $value) = explode(':', $name, 2);
}
$name = trim($name);
@@ -3993,7 +4119,8 @@
* @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 $message The transformed message Body
+ * or your own custom converter
+ * @return string The transformed message body
*
* @throws Exception
*
@@ -4004,11 +4131,11 @@
preg_match_all('/(?<!-)(src|background)=["\'](.*)["\']/Ui', $message, $images);
if (array_key_exists(2, $images)) {
if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
- // Ensure $basedir has a trailing /
+ //Ensure $basedir has a trailing /
$basedir .= '/';
}
foreach ($images[2] as $imgindex => $url) {
- // Convert data URIs into embedded images
+ //Convert data URIs into embedded images
//e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
$match = [];
if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) {
@@ -4022,7 +4149,7 @@
}
//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) . '@phpmailer.0'; //RFC2392 S 2
if (!$this->cidExists($cid)) {
$this->addStringEmbeddedImage(
@@ -4040,13 +4167,14 @@
);
continue;
}
- if (// Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
+ if (
+ //Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
!empty($basedir)
- // Ignore URLs containing parent dir traversal (..)
+ //Ignore URLs containing parent dir traversal (..)
&& (strpos($url, '..') === false)
- // Do not change urls that are already inline images
+ //Do not change urls that are already inline images
&& 0 !== strpos($url, 'cid:')
- // Do not change absolute URLs, including anonymous protocol
+ //Do not change absolute URLs, including anonymous protocol
&& !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
) {
$filename = static::mb_pathinfo($url, PATHINFO_BASENAME);
@@ -4054,7 +4182,7 @@
if ('.' === $directory) {
$directory = '';
}
- // RFC2392 S 2
+ //RFC2392 S 2
$cid = substr(hash('sha256', $url), 0, 32) . '@phpmailer.0';
if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
$basedir .= '/';
@@ -4062,13 +4190,14 @@
if (strlen($directory) > 1 && '/' !== substr($directory, -1)) {
$directory .= '/';
}
- if ($this->addEmbeddedImage(
- $basedir . $directory . $filename,
- $cid,
- $filename,
- static::ENCODING_BASE64,
- static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION))
- )
+ if (
+ $this->addEmbeddedImage(
+ $basedir . $directory . $filename,
+ $cid,
+ $filename,
+ static::ENCODING_BASE64,
+ static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION))
+ )
) {
$message = preg_replace(
'/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
@@ -4080,7 +4209,7 @@
}
}
$this->isHTML();
- // Convert all message body line breaks to LE, makes quoted-printable encoding work much better
+ //Convert all message body line breaks to LE, makes quoted-printable encoding work much better
$this->Body = static::normalizeBreaks($message);
$this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced));
if (!$this->alternativeExists()) {
@@ -4099,9 +4228,9 @@
* Example usage:
*
* ```php
- * // Use default conversion
+ * //Use default conversion
* $plain = $mail->html2text($html);
- * // Use your own custom converter
+ * //Use your own custom converter
* $plain = $mail->html2text($html, function($html) {
* $converter = new MyHtml2text($html);
* return $converter->get_text();
@@ -4117,7 +4246,7 @@
public function html2text($html, $advanced = false)
{
if (is_callable($advanced)) {
- return $advanced($html);
+ return call_user_func($advanced, $html);
}
return html_entity_decode(
@@ -4216,6 +4345,7 @@
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'webp' => 'image/webp',
+ 'avif' => 'image/avif',
'heif' => 'image/heif',
'heifs' => 'image/heif-sequence',
'heic' => 'image/heic',
@@ -4267,7 +4397,7 @@
*/
public static function filenameToType($filename)
{
- // In case the path is a URL, strip any query string before getting extension
+ //In case the path is a URL, strip any query string before getting extension
$qpos = strpos($filename, '?');
if (false !== $qpos) {
$filename = substr($filename, 0, $qpos);
@@ -4378,9 +4508,9 @@
if (null === $breaktype) {
$breaktype = static::$LE;
}
- // Normalise to \n
+ //Normalise to \n
$text = str_replace([self::CRLF, "\r"], "\n", $text);
- // Now convert LE as needed
+ //Now convert LE as needed
if ("\n" !== $breaktype) {
$text = str_replace("\n", $breaktype, $text);
}
@@ -4486,11 +4616,15 @@
$privKey = openssl_pkey_get_private($privKeyStr);
}
if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
- openssl_pkey_free($privKey);
+ if (\PHP_MAJOR_VERSION < 8) {
+ openssl_pkey_free($privKey);
+ }
return base64_encode($signature);
}
- openssl_pkey_free($privKey);
+ if (\PHP_MAJOR_VERSION < 8) {
+ openssl_pkey_free($privKey);
+ }
return '';
}
@@ -4555,7 +4689,7 @@
if (empty($body)) {
return self::CRLF;
}
- // Normalize line endings to CRLF
+ //Normalize line endings to CRLF
$body = static::normalizeBreaks($body, self::CRLF);
//Reduce multiple trailing line breaks to a single one
@@ -4575,9 +4709,9 @@
*/
public function DKIM_Add($headers_line, $subject, $body)
{
- $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
- $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization methods of header & body
- $DKIMquery = 'dns/txt'; // Query method
+ $DKIMsignatureType = 'rsa-sha256'; //Signature & hash algorithms
+ $DKIMcanonicalization = 'relaxed/simple'; //Canonicalization methods of header & body
+ $DKIMquery = 'dns/txt'; //Query method
$DKIMtime = time();
//Always sign these headers without being asked
//Recommended list from https://tools.ietf.org/html/rfc6376#section-5.4.1
@@ -4678,7 +4812,8 @@
$headerKeys = ' h=' . implode(':', $headersToSignKeys) . ';' . static::$LE;
$headerValues = implode(static::$LE, $headersToSign);
$body = $this->DKIM_BodyC($body);
- $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
+ //Base64 of packed binary SHA-256 hash of body
+ $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body)));
$ident = '';
if ('' !== $this->DKIM_identity) {
$ident = ' i=' . $this->DKIM_identity . ';' . static::$LE;