PHPMailer.php (mrbs-1.9.4) | : | PHPMailer.php (mrbs-1.10.0) | ||
---|---|---|---|---|
skipping to change at line 106 | skipping to change at line 106 | |||
* | * | |||
* @var string | * @var string | |||
*/ | */ | |||
public $ErrorInfo = ''; | public $ErrorInfo = ''; | |||
/** | /** | |||
* The From email address for the message. | * The From email address for the message. | |||
* | * | |||
* @var string | * @var string | |||
*/ | */ | |||
public $From = 'root@localhost'; | public $From = ''; | |||
/** | /** | |||
* The From name of the message. | * The From name of the message. | |||
* | * | |||
* @var string | * @var string | |||
*/ | */ | |||
public $FromName = 'Root User'; | public $FromName = ''; | |||
/** | /** | |||
* The envelope sender of the message. | * The envelope sender of the message. | |||
* This will usually be turned into a Return-Path header by the receiver, | * This will usually be turned into a Return-Path header by the receiver, | |||
* and is the address that bounces will be sent to. | * and is the address that bounces will be sent to. | |||
* If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' v alue over SMTP. | * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' v alue over SMTP. | |||
* | * | |||
* @var string | * @var string | |||
*/ | */ | |||
public $Sender = ''; | public $Sender = ''; | |||
skipping to change at line 431 | skipping to change at line 431 | |||
* $mail->Debugoutput = new myPsr3Logger; | * $mail->Debugoutput = new myPsr3Logger; | |||
* ``` | * ``` | |||
* | * | |||
* @see SMTP::$Debugoutput | * @see SMTP::$Debugoutput | |||
* | * | |||
* @var string|callable|\Psr\Log\LoggerInterface | * @var string|callable|\Psr\Log\LoggerInterface | |||
*/ | */ | |||
public $Debugoutput = 'echo'; | public $Debugoutput = 'echo'; | |||
/** | /** | |||
* Whether to keep SMTP connection open after each message. | * Whether to keep the SMTP connection open after each message. | |||
* If this is set to true then to close the connection | * If this is set to true then the connection will remain open after a send, | |||
* requires an explicit call to smtpClose(). | * 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 r | ||||
educes overhead. | ||||
* See the mailing list example for how to use it. | ||||
* | * | |||
* @var bool | * @var bool | |||
*/ | */ | |||
public $SMTPKeepAlive = false; | public $SMTPKeepAlive = false; | |||
/** | /** | |||
* Whether to split multiple to addresses into multiple messages | * Whether to split multiple to addresses into multiple messages | |||
* or send them all in one message. | * or send them all in one message. | |||
* Only supported in `mail` and `sendmail` transports, not in SMTP. | * Only supported in `mail` and `sendmail` transports, not in SMTP. | |||
* | * | |||
skipping to change at line 690 | skipping to change at line 692 | |||
protected $message_type = ''; | protected $message_type = ''; | |||
/** | /** | |||
* The array of MIME boundary strings. | * The array of MIME boundary strings. | |||
* | * | |||
* @var array | * @var array | |||
*/ | */ | |||
protected $boundary = []; | protected $boundary = []; | |||
/** | /** | |||
* The array of available languages. | * The array of available text strings for the current language. | |||
* | * | |||
* @var array | * @var array | |||
*/ | */ | |||
protected $language = []; | protected $language = []; | |||
/** | /** | |||
* The number of errors encountered. | * The number of errors encountered. | |||
* | * | |||
* @var int | * @var int | |||
*/ | */ | |||
skipping to change at line 751 | skipping to change at line 753 | |||
* | * | |||
* @var string | * @var string | |||
*/ | */ | |||
protected $uniqueid = ''; | protected $uniqueid = ''; | |||
/** | /** | |||
* The PHPMailer Version number. | * The PHPMailer Version number. | |||
* | * | |||
* @var string | * @var string | |||
*/ | */ | |||
const VERSION = '6.4.1'; | const VERSION = '6.5.3'; | |||
/** | /** | |||
* Error severity: message only, continue processing. | * Error severity: message only, continue processing. | |||
* | * | |||
* @var int | * @var int | |||
*/ | */ | |||
const STOP_MESSAGE = 0; | const STOP_MESSAGE = 0; | |||
/** | /** | |||
* Error severity: message, likely ok to continue processing. | * Error severity: message, likely ok to continue processing. | |||
skipping to change at line 1189 | skipping to change at line 1191 | |||
* Uses the imap_rfc822_parse_adrlist function if the IMAP extension is avai lable. | * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is avai lable. | |||
* Note that quotes in the name part are removed. | * Note that quotes in the name part are removed. | |||
* | * | |||
* @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822. php A more careful implementation | * @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822. php A more careful implementation | |||
* | * | |||
* @param string $addrstr The address list string | * @param string $addrstr The address list string | |||
* @param bool $useimap Whether to use the IMAP extension to parse the lis t | * @param bool $useimap Whether to use the IMAP extension to parse the lis t | |||
* | * | |||
* @return array | * @return array | |||
*/ | */ | |||
public static function parseAddresses($addrstr, $useimap = true) | public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591) | |||
{ | { | |||
$addresses = []; | $addresses = []; | |||
if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { | if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { | |||
//Use this built-in parser if it's available | //Use this built-in parser if it's available | |||
$list = imap_rfc822_parse_adrlist($addrstr, ''); | $list = imap_rfc822_parse_adrlist($addrstr, ''); | |||
// Clear any potential IMAP errors to get rid of notices being throw | ||||
n at end of script. | ||||
imap_errors(); | ||||
foreach ($list as $address) { | foreach ($list as $address) { | |||
if ( | if ( | |||
('.SYNTAX-ERROR.' !== $address->host) && static::validateAdd | '.SYNTAX-ERROR.' !== $address->host && | |||
ress( | static::validateAddress($address->mailbox . '@' . $address-> | |||
$address->mailbox . '@' . $address->host | host) | |||
) | ||||
) { | ) { | |||
//Decode the name part if it's present and encoded | //Decode the name part if it's present and encoded | |||
if ( | if ( | |||
property_exists($address, 'personal') && | property_exists($address, 'personal') && | |||
extension_loaded('mbstring') && | //Check for a Mbstring constant rather than using extens | |||
preg_match('/^=\?.*\?=$/', $address->personal) | ion_loaded, which is sometimes disabled | |||
defined('MB_CASE_UPPER') && | ||||
preg_match('/^=\?.*\?=$/s', $address->personal) | ||||
) { | ) { | |||
$origCharset = mb_internal_encoding(); | ||||
mb_internal_encoding($charset); | ||||
//Undo any RFC2047-encoded spaces-as-underscores | ||||
$address->personal = str_replace('_', '=20', $address->p | ||||
ersonal); | ||||
//Decode the name | ||||
$address->personal = mb_decode_mimeheader($address->pers onal); | $address->personal = mb_decode_mimeheader($address->pers onal); | |||
mb_internal_encoding($origCharset); | ||||
} | } | |||
$addresses[] = [ | $addresses[] = [ | |||
'name' => (property_exists($address, 'personal') ? $addr ess->personal : ''), | 'name' => (property_exists($address, 'personal') ? $addr ess->personal : ''), | |||
'address' => $address->mailbox . '@' . $address->host, | 'address' => $address->mailbox . '@' . $address->host, | |||
]; | ]; | |||
} | } | |||
} | } | |||
} else { | } else { | |||
//Use this simpler parser | //Use this simpler parser | |||
skipping to change at line 1235 | skipping to change at line 1245 | |||
$addresses[] = [ | $addresses[] = [ | |||
'name' => '', | 'name' => '', | |||
'address' => $address, | 'address' => $address, | |||
]; | ]; | |||
} | } | |||
} else { | } else { | |||
list($name, $email) = explode('<', $address); | list($name, $email) = explode('<', $address); | |||
$email = trim(str_replace('>', '', $email)); | $email = trim(str_replace('>', '', $email)); | |||
$name = trim($name); | $name = trim($name); | |||
if (static::validateAddress($email)) { | if (static::validateAddress($email)) { | |||
//Check for a Mbstring constant rather than using extens ion_loaded, which is sometimes disabled | ||||
//If this name is encoded, decode it | //If this name is encoded, decode it | |||
if (preg_match('/^=\?.*\?=$/', $name)) { | if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/ | |||
s', $name)) { | ||||
$origCharset = mb_internal_encoding(); | ||||
mb_internal_encoding($charset); | ||||
//Undo any RFC2047-encoded spaces-as-underscores | ||||
$name = str_replace('_', '=20', $name); | ||||
//Decode the name | ||||
$name = mb_decode_mimeheader($name); | $name = mb_decode_mimeheader($name); | |||
mb_internal_encoding($origCharset); | ||||
} | } | |||
$addresses[] = [ | $addresses[] = [ | |||
//Remove any surrounding quotes and spaces from the name | //Remove any surrounding quotes and spaces from the name | |||
'name' => trim($name, '\'" '), | 'name' => trim($name, '\'" '), | |||
'address' => $email, | 'address' => $email, | |||
]; | ]; | |||
} | } | |||
} | } | |||
} | } | |||
} | } | |||
skipping to change at line 1338 | skipping to change at line 1355 | |||
* @param string $address The email address to check | * @param string $address The email address to check | |||
* @param string|callable $patternselect Which pattern to use | * @param string|callable $patternselect Which pattern to use | |||
* | * | |||
* @return bool | * @return bool | |||
*/ | */ | |||
public static function validateAddress($address, $patternselect = null) | public static function validateAddress($address, $patternselect = null) | |||
{ | { | |||
if (null === $patternselect) { | if (null === $patternselect) { | |||
$patternselect = static::$validator; | $patternselect = static::$validator; | |||
} | } | |||
if (is_callable($patternselect)) { | //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); | return call_user_func($patternselect, $address); | |||
} | } | |||
//Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 | //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 | |||
if (strpos($address, "\n") !== false || strpos($address, "\r") !== false ) { | if (strpos($address, "\n") !== false || strpos($address, "\r") !== false ) { | |||
return false; | return false; | |||
} | } | |||
switch ($patternselect) { | switch ($patternselect) { | |||
case 'pcre': //Kept for BC | case 'pcre': //Kept for BC | |||
case 'pcre8': | case 'pcre8': | |||
/* | /* | |||
skipping to change at line 1436 | skipping to change at line 1454 | |||
) { | ) { | |||
$domain = substr($address, ++$pos); | $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, $thi s->CharSet)) { | if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $thi s->CharSet)) { | |||
//Convert the domain from whatever charset it's in to UTF-8 | //Convert the domain from whatever charset it's in to UTF-8 | |||
$domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this ->CharSet); | $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this ->CharSet); | |||
//Ignore IDE complaints about this line - method signature chang ed in PHP 5.4 | //Ignore IDE complaints about this line - method signature chang ed in PHP 5.4 | |||
$errorcode = 0; | $errorcode = 0; | |||
if (defined('INTL_IDNA_VARIANT_UTS46')) { | if (defined('INTL_IDNA_VARIANT_UTS46')) { | |||
//Use the current punycode standard (appeared in PHP 7.2) | //Use the current punycode standard (appeared in PHP 7.2) | |||
$punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VAR | $punycode = idn_to_ascii( | |||
IANT_UTS46); | $domain, | |||
\IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI | ||||
| | ||||
\IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCI | ||||
I, | ||||
\INTL_IDNA_VARIANT_UTS46 | ||||
); | ||||
} elseif (defined('INTL_IDNA_VARIANT_2003')) { | } elseif (defined('INTL_IDNA_VARIANT_2003')) { | |||
//Fall back to this old, deprecated/removed encoding | //Fall back to this old, deprecated/removed encoding | |||
$punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VAR IANT_2003); | $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VAR IANT_2003); | |||
} else { | } else { | |||
//Fall back to a default we don't know about | //Fall back to a default we don't know about | |||
$punycode = idn_to_ascii($domain, $errorcode); | $punycode = idn_to_ascii($domain, $errorcode); | |||
} | } | |||
if (false !== $punycode) { | if (false !== $punycode) { | |||
return substr($address, 0, $pos) . $punycode; | return substr($address, 0, $pos) . $punycode; | |||
} | } | |||
skipping to change at line 1508 | skipping to change at line 1531 | |||
static::setLE(PHP_EOL); | static::setLE(PHP_EOL); | |||
} | } | |||
//Check for buggy PHP versions that add a header with an incorrect line break | //Check for buggy PHP versions that add a header with an incorrect line break | |||
if ( | if ( | |||
'mail' === $this->Mailer | 'mail' === $this->Mailer | |||
&& ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017) | && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017) | |||
|| (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103)) | || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103)) | |||
&& ini_get('mail.add_x_header') === '1' | && ini_get('mail.add_x_header') === '1' | |||
&& stripos(PHP_OS, 'WIN') === 0 | && stripos(PHP_OS, 'WIN') === 0 | |||
) { | ) { | |||
trigger_error( | trigger_error($this->lang('buggy_php'), E_USER_WARNING); | |||
'Your version of PHP is affected by a bug that may result in cor | ||||
rupted messages.' . | ||||
' To fix it, switch to sending using SMTP, disable the mail.add_ | ||||
x_header option in' . | ||||
' your php.ini, switch to MacOS or Linux, or upgrade your PHP to | ||||
version 7.0.17+ or 7.1.3+.', | ||||
E_USER_WARNING | ||||
); | ||||
} | } | |||
try { | try { | |||
$this->error_count = 0; //Reset errors | $this->error_count = 0; //Reset errors | |||
$this->mailHeader = ''; | $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) { | foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { | |||
$params[1] = $this->punyencodeAddress($params[1]); | $params[1] = $this->punyencodeAddress($params[1]); | |||
call_user_func_array([$this, 'addAnAddress'], $params); | call_user_func_array([$this, 'addAnAddress'], $params); | |||
skipping to change at line 1687 | skipping to change at line 1705 | |||
$this->edebug('Sending with sendmail'); | $this->edebug('Sending with sendmail'); | |||
} | } | |||
$header = static::stripTrailingWSP($header) . static::$LE . static::$LE; | $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; | |||
//This sets the SMTP envelope sender which gets turned into a return-pat h header by the receiver | //This sets the SMTP envelope sender which gets turned into a return-pat h header by the receiver | |||
//A space after `-f` is optional, but there is a long history of its pre sence | //A space after `-f` is optional, but there is a long history of its pre sence | |||
//causing problems, so we don't use one | //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 | //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 | //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html | |||
//Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html | //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html | |||
//Example problem: https://www.drupal.org/node/1057954 | //Example problem: https://www.drupal.org/node/1057954 | |||
if (empty($this->Sender) && !empty(ini_get('sendmail_from'))) { | ||||
//PHP 5.6 workaround | ||||
$sendmail_from_value = ini_get('sendmail_from'); | ||||
if (empty($this->Sender) && !empty($sendmail_from_value)) { | ||||
//PHP config has a sender address we can use | //PHP config has a sender address we can use | |||
$this->Sender = ini_get('sendmail_from'); | $this->Sender = ini_get('sendmail_from'); | |||
} | } | |||
//CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be es caped. | //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be es caped. | |||
if (!empty($this->Sender) && static::validateAddress($this->Sender) && s elf::isShellSafe($this->Sender)) { | if (!empty($this->Sender) && static::validateAddress($this->Sender) && s elf::isShellSafe($this->Sender)) { | |||
if ($this->Mailer === 'qmail') { | if ($this->Mailer === 'qmail') { | |||
$sendmailFmt = '%s -f%s'; | $sendmailFmt = '%s -f%s'; | |||
} else { | } else { | |||
$sendmailFmt = '%s -oi -f%s -t'; | $sendmailFmt = '%s -oi -f%s -t'; | |||
} | } | |||
skipping to change at line 1724 | skipping to change at line 1745 | |||
foreach ($this->SingleToArray as $toAddr) { | foreach ($this->SingleToArray as $toAddr) { | |||
$mail = @popen($sendmail, 'w'); | $mail = @popen($sendmail, 'w'); | |||
if (!$mail) { | if (!$mail) { | |||
throw new Exception($this->lang('execute') . $this->Sendmail , self::STOP_CRITICAL); | throw new Exception($this->lang('execute') . $this->Sendmail , self::STOP_CRITICAL); | |||
} | } | |||
$this->edebug("To: {$toAddr}"); | $this->edebug("To: {$toAddr}"); | |||
fwrite($mail, 'To: ' . $toAddr . "\n"); | fwrite($mail, 'To: ' . $toAddr . "\n"); | |||
fwrite($mail, $header); | fwrite($mail, $header); | |||
fwrite($mail, $body); | fwrite($mail, $body); | |||
$result = pclose($mail); | $result = pclose($mail); | |||
$addrinfo = static::parseAddresses($toAddr); | $addrinfo = static::parseAddresses($toAddr, true, $this->charSet ); | |||
$this->doCallback( | $this->doCallback( | |||
($result === 0), | ($result === 0), | |||
[[$addrinfo['address'], $addrinfo['name']]], | [[$addrinfo['address'], $addrinfo['name']]], | |||
$this->cc, | $this->cc, | |||
$this->bcc, | $this->bcc, | |||
$this->Subject, | $this->Subject, | |||
$body, | $body, | |||
$this->From, | $this->From, | |||
[] | [] | |||
); | ); | |||
skipping to change at line 1869 | skipping to change at line 1890 | |||
$params = null; | $params = null; | |||
//This sets the SMTP envelope sender which gets turned into a return-pat h header by the receiver | //This sets the SMTP envelope sender which gets turned into a return-pat h header by the receiver | |||
//A space after `-f` is optional, but there is a long history of its pre sence | //A space after `-f` is optional, but there is a long history of its pre sence | |||
//causing problems, so we don't use one | //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 | //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 | //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html | |||
//Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html | //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html | |||
//Example problem: https://www.drupal.org/node/1057954 | //Example problem: https://www.drupal.org/node/1057954 | |||
//CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be es caped. | //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be es caped. | |||
if (empty($this->Sender) && !empty(ini_get('sendmail_from'))) { | ||||
//PHP 5.6 workaround | ||||
$sendmail_from_value = ini_get('sendmail_from'); | ||||
if (empty($this->Sender) && !empty($sendmail_from_value)) { | ||||
//PHP config has a sender address we can use | //PHP config has a sender address we can use | |||
$this->Sender = ini_get('sendmail_from'); | $this->Sender = ini_get('sendmail_from'); | |||
} | } | |||
if (!empty($this->Sender) && static::validateAddress($this->Sender)) { | if (!empty($this->Sender) && static::validateAddress($this->Sender)) { | |||
if (self::isShellSafe($this->Sender)) { | if (self::isShellSafe($this->Sender)) { | |||
$params = sprintf('-f%s', $this->Sender); | $params = sprintf('-f%s', $this->Sender); | |||
} | } | |||
$old_from = ini_get('sendmail_from'); | $old_from = ini_get('sendmail_from'); | |||
ini_set('sendmail_from', $this->Sender); | ini_set('sendmail_from', $this->Sender); | |||
} | } | |||
$result = false; | $result = false; | |||
if ($this->SingleTo && count($toArr) > 1) { | if ($this->SingleTo && count($toArr) > 1) { | |||
foreach ($toArr as $toAddr) { | foreach ($toArr as $toAddr) { | |||
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $h eader, $params); | $result = $this->mailPassthru($toAddr, $this->Subject, $body, $h eader, $params); | |||
$addrinfo = static::parseAddresses($toAddr); | $addrinfo = static::parseAddresses($toAddr, true, $this->charSet ); | |||
$this->doCallback( | $this->doCallback( | |||
$result, | $result, | |||
[[$addrinfo['address'], $addrinfo['name']]], | [[$addrinfo['address'], $addrinfo['name']]], | |||
$this->cc, | $this->cc, | |||
$this->bcc, | $this->bcc, | |||
$this->Subject, | $this->Subject, | |||
$body, | $body, | |||
$this->From, | $this->From, | |||
[] | [] | |||
); | ); | |||
skipping to change at line 2181 | skipping to change at line 2205 | |||
public function smtpClose() | public function smtpClose() | |||
{ | { | |||
if ((null !== $this->smtp) && $this->smtp->connected()) { | if ((null !== $this->smtp) && $this->smtp->connected()) { | |||
$this->smtp->quit(); | $this->smtp->quit(); | |||
$this->smtp->close(); | $this->smtp->close(); | |||
} | } | |||
} | } | |||
/** | /** | |||
* Set the language for error messages. | * Set the language for error messages. | |||
* Returns false if it cannot load the language file. | ||||
* The default language is English. | * The default language is English. | |||
* | * | |||
* @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") | * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") | |||
* Optionally, the language code can be enhanced wi | ||||
th a 4-character | ||||
* script annotation and/or a 2-character country a | ||||
nnotation. | ||||
* @param string $lang_path Path to the language file directory, with traili ng separator (slash) | * @param string $lang_path Path to the language file directory, with traili ng separator (slash) | |||
* Do not set this from user input! | ||||
* | * | |||
* @return bool | * @return bool Returns true if the requested language was loaded, false oth erwise. | |||
*/ | */ | |||
public function setLanguage($langcode = 'en', $lang_path = '') | public function setLanguage($langcode = 'en', $lang_path = '') | |||
{ | { | |||
//Backwards compatibility for renamed language codes | //Backwards compatibility for renamed language codes | |||
$renamed_langcodes = [ | $renamed_langcodes = [ | |||
'br' => 'pt_br', | 'br' => 'pt_br', | |||
'cz' => 'cs', | 'cz' => 'cs', | |||
'dk' => 'da', | 'dk' => 'da', | |||
'no' => 'nb', | 'no' => 'nb', | |||
'se' => 'sv', | 'se' => 'sv', | |||
skipping to change at line 2210 | skipping to change at line 2236 | |||
'am' => 'hy', | 'am' => 'hy', | |||
]; | ]; | |||
if (array_key_exists($langcode, $renamed_langcodes)) { | if (array_key_exists($langcode, $renamed_langcodes)) { | |||
$langcode = $renamed_langcodes[$langcode]; | $langcode = $renamed_langcodes[$langcode]; | |||
} | } | |||
//Define full set of translatable strings in English | //Define full set of translatable strings in English | |||
$PHPMAILER_LANG = [ | $PHPMAILER_LANG = [ | |||
'authenticate' => 'SMTP Error: Could not authenticate.', | 'authenticate' => 'SMTP Error: Could not authenticate.', | |||
'buggy_php' => 'Your version of PHP is affected by a bug that may re | ||||
sult in corrupted messages.' . | ||||
' To fix it, switch to sending using SMTP, disable the mail.add_ | ||||
x_header option in' . | ||||
' your php.ini, switch to MacOS or Linux, or upgrade your PHP to | ||||
version 7.0.17+ or 7.1.3+.', | ||||
'connect_host' => 'SMTP Error: Could not connect to SMTP host.', | 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', | |||
'data_not_accepted' => 'SMTP Error: data not accepted.', | 'data_not_accepted' => 'SMTP Error: data not accepted.', | |||
'empty_message' => 'Message body empty', | 'empty_message' => 'Message body empty', | |||
'encoding' => 'Unknown encoding: ', | 'encoding' => 'Unknown encoding: ', | |||
'execute' => 'Could not execute: ', | 'execute' => 'Could not execute: ', | |||
'extension_missing' => 'Extension missing: ', | ||||
'file_access' => 'Could not access file: ', | 'file_access' => 'Could not access file: ', | |||
'file_open' => 'File Error: Could not open file: ', | 'file_open' => 'File Error: Could not open file: ', | |||
'from_failed' => 'The following From address failed: ', | 'from_failed' => 'The following From address failed: ', | |||
'instantiate' => 'Could not instantiate mail function.', | 'instantiate' => 'Could not instantiate mail function.', | |||
'invalid_address' => 'Invalid address: ', | 'invalid_address' => 'Invalid address: ', | |||
'invalid_header' => 'Invalid header name or value', | ||||
'invalid_hostentry' => 'Invalid hostentry: ', | 'invalid_hostentry' => 'Invalid hostentry: ', | |||
'invalid_host' => 'Invalid host: ', | 'invalid_host' => 'Invalid host: ', | |||
'mailer_not_supported' => ' mailer is not supported.', | 'mailer_not_supported' => ' mailer is not supported.', | |||
'provide_address' => 'You must provide at least one recipient email address.', | 'provide_address' => 'You must provide at least one recipient email address.', | |||
'recipients_failed' => 'SMTP Error: The following recipients failed: ', | 'recipients_failed' => 'SMTP Error: The following recipients failed: ', | |||
'signing' => 'Signing Error: ', | 'signing' => 'Signing Error: ', | |||
'smtp_code' => 'SMTP code: ', | ||||
'smtp_code_ex' => 'Additional SMTP info: ', | ||||
'smtp_connect_failed' => 'SMTP connect() failed.', | 'smtp_connect_failed' => 'SMTP connect() failed.', | |||
'smtp_detail' => 'Detail: ', | ||||
'smtp_error' => 'SMTP server error: ', | 'smtp_error' => 'SMTP server error: ', | |||
'variable_set' => 'Cannot set or reset variable: ', | 'variable_set' => 'Cannot set or reset variable: ', | |||
'extension_missing' => 'Extension missing: ', | ||||
]; | ]; | |||
if (empty($lang_path)) { | 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' . D IRECTORY_SEPARATOR; | $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . D IRECTORY_SEPARATOR; | |||
} | } | |||
//Validate $langcode | //Validate $langcode | |||
if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { | $foundlang = true; | |||
$langcode = strtolower($langcode); | ||||
if ( | ||||
!preg_match('/^(?P<lang>[a-z]{2})(?P<script>_[a-z]{4})?(?P<country>_ | ||||
[a-z]{2})?$/', $langcode, $matches) | ||||
&& $langcode !== 'en' | ||||
) { | ||||
$foundlang = false; | ||||
$langcode = 'en'; | $langcode = 'en'; | |||
} | } | |||
$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) { | if ('en' !== $langcode) { | |||
//Make sure language file path is readable | $langcodes = []; | |||
if (!static::fileIsAccessible($lang_file)) { | if (!empty($matches['script']) && !empty($matches['country'])) { | |||
$langcodes[] = $matches['lang'] . $matches['script'] . $matches[ | ||||
'country']; | ||||
} | ||||
if (!empty($matches['country'])) { | ||||
$langcodes[] = $matches['lang'] . $matches['country']; | ||||
} | ||||
if (!empty($matches['script'])) { | ||||
$langcodes[] = $matches['lang'] . $matches['script']; | ||||
} | ||||
$langcodes[] = $matches['lang']; | ||||
//Try and find a readable language file for the requested language. | ||||
$foundFile = false; | ||||
foreach ($langcodes as $code) { | ||||
$lang_file = $lang_path . 'phpmailer.lang-' . $code . '.php'; | ||||
if (static::fileIsAccessible($lang_file)) { | ||||
$foundFile = true; | ||||
break; | ||||
} | ||||
} | ||||
if ($foundFile === false) { | ||||
$foundlang = false; | $foundlang = false; | |||
} else { | } else { | |||
//Overwrite language-specific strings. | $lines = file($lang_file); | |||
//This way we'll never have missing translation keys. | foreach ($lines as $line) { | |||
$foundlang = include $lang_file; | //Translation file lines look like this: | |||
//$PHPMAILER_LANG['authenticate'] = 'SMTP-Fehler: Authentifi | ||||
zierung 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 hav | ||||
e missing translation keys. | ||||
$PHPMAILER_LANG[$matches[1]] = (string)$matches[3]; | ||||
} | ||||
} | ||||
} | } | |||
} | } | |||
$this->language = $PHPMAILER_LANG; | $this->language = $PHPMAILER_LANG; | |||
return (bool) $foundlang; //Returns false if language not found | return $foundlang; //Returns false if language not found | |||
} | } | |||
/** | /** | |||
* Get the array of strings for the current language. | * Get the array of strings for the current language. | |||
* | * | |||
* @return array | * @return array | |||
*/ | */ | |||
public function getTranslations() | public function getTranslations() | |||
{ | { | |||
if (empty($this->language)) { | ||||
$this->setLanguage(); // Set the default language. | ||||
} | ||||
return $this->language; | return $this->language; | |||
} | } | |||
/** | /** | |||
* Create recipient headers. | * Create recipient headers. | |||
* | * | |||
* @param string $type | * @param string $type | |||
* @param array $addr An array of recipients, | * @param array $addr An array of recipients, | |||
* where each recipient is a 2-element indexed array wit h element 0 containing an address | * where each recipient is a 2-element indexed array wit h element 0 containing an address | |||
* and element 1 containing a name, like: | * and element 1 containing a name, like: | |||
skipping to change at line 2532 | skipping to change at line 2613 | |||
$result .= $this->addrAppend('Reply-To', $this->ReplyTo); | $result .= $this->addrAppend('Reply-To', $this->ReplyTo); | |||
} | } | |||
//mail() sets the subject itself | //mail() sets the subject itself | |||
if ('mail' !== $this->Mailer) { | if ('mail' !== $this->Mailer) { | |||
$result .= $this->headerLine('Subject', $this->encodeHeader($this->s ecureHeader($this->Subject))); | $result .= $this->headerLine('Subject', $this->encodeHeader($this->s ecureHeader($this->Subject))); | |||
} | } | |||
//Only allow a custom message ID if it conforms to RFC 5322 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 | //https://tools.ietf.org/html/rfc5322#section-3.6.4 | |||
if ('' !== $this->MessageID && preg_match('/^<.*@.*>$/', $this->MessageI | if ( | |||
D)) { | '' !== $this->MessageID && | |||
preg_match( | ||||
'/^<((([a-z\d!#$%&\'*+\/=?^_`{|}~-]+(\.[a-z\d!#$%&\'*+\/=?^_`{|} | ||||
~-]+)*)' . | ||||
'|("(([\x01-\x08\x0B\x0C\x0E-\x1F\x7F]|[\x21\x23-\x5B\x5D-\x7E]) | ||||
' . | ||||
'|(\\[\x01-\x09\x0B\x0C\x0E-\x7F]))*"))@(([a-z\d!#$%&\'*+\/=?^_` | ||||
{|}~-]+' . | ||||
'(\.[a-z\d!#$%&\'*+\/=?^_`{|}~-]+)*)|(\[(([\x01-\x08\x0B\x0C\x0E | ||||
-\x1F\x7F]' . | ||||
'|[\x21-\x5A\x5E-\x7E])|(\\[\x01-\x09\x0B\x0C\x0E-\x7F]))*\])))> | ||||
$/Di', | ||||
$this->MessageID | ||||
) | ||||
) { | ||||
$this->lastMessageID = $this->MessageID; | $this->lastMessageID = $this->MessageID; | |||
} else { | } else { | |||
$this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->se rverHostname()); | $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->se rverHostname()); | |||
} | } | |||
$result .= $this->headerLine('Message-ID', $this->lastMessageID); | $result .= $this->headerLine('Message-ID', $this->lastMessageID); | |||
if (null !== $this->Priority) { | if (null !== $this->Priority) { | |||
$result .= $this->headerLine('X-Priority', $this->Priority); | $result .= $this->headerLine('X-Priority', $this->Priority); | |||
} | } | |||
if ('' === $this->XMailer) { | if ('' === $this->XMailer) { | |||
$result .= $this->headerLine( | $result .= $this->headerLine( | |||
skipping to change at line 3916 | skipping to change at line 4007 | |||
* @param string $msg | * @param string $msg | |||
*/ | */ | |||
protected function setError($msg) | protected function setError($msg) | |||
{ | { | |||
++$this->error_count; | ++$this->error_count; | |||
if ('smtp' === $this->Mailer && null !== $this->smtp) { | if ('smtp' === $this->Mailer && null !== $this->smtp) { | |||
$lasterror = $this->smtp->getError(); | $lasterror = $this->smtp->getError(); | |||
if (!empty($lasterror['error'])) { | if (!empty($lasterror['error'])) { | |||
$msg .= $this->lang('smtp_error') . $lasterror['error']; | $msg .= $this->lang('smtp_error') . $lasterror['error']; | |||
if (!empty($lasterror['detail'])) { | if (!empty($lasterror['detail'])) { | |||
$msg .= ' Detail: ' . $lasterror['detail']; | $msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detai l']; | |||
} | } | |||
if (!empty($lasterror['smtp_code'])) { | if (!empty($lasterror['smtp_code'])) { | |||
$msg .= ' SMTP code: ' . $lasterror['smtp_code']; | $msg .= ' ' . $this->lang('smtp_code') . $lasterror['smtp_co de']; | |||
} | } | |||
if (!empty($lasterror['smtp_code_ex'])) { | if (!empty($lasterror['smtp_code_ex'])) { | |||
$msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex ']; | $msg .= ' ' . $this->lang('smtp_code_ex') . $lasterror['smtp _code_ex']; | |||
} | } | |||
} | } | |||
} | } | |||
$this->ErrorInfo = $msg; | $this->ErrorInfo = $msg; | |||
} | } | |||
/** | /** | |||
* Return an RFC 822 formatted date. | * Return an RFC 822 formatted date. | |||
* | * | |||
* @return string | * @return string | |||
skipping to change at line 3983 | skipping to change at line 4074 | |||
* | * | |||
* @return bool | * @return bool | |||
*/ | */ | |||
public static function isValidHost($host) | public static function isValidHost($host) | |||
{ | { | |||
//Simple syntax limits | //Simple syntax limits | |||
if ( | if ( | |||
empty($host) | empty($host) | |||
|| !is_string($host) | || !is_string($host) | |||
|| strlen($host) > 256 | || strlen($host) > 256 | |||
|| !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+])$/', $host) | || !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+\])$/', $host) | |||
) { | ) { | |||
return false; | return false; | |||
} | } | |||
//Looks like a bracketed IPv6 address | //Looks like a bracketed IPv6 address | |||
if (strlen($host) > 2 && substr($host, 0, 1) === '[' && substr($host, -1 , 1) === ']') { | if (strlen($host) > 2 && substr($host, 0, 1) === '[' && substr($host, -1 , 1) === ']') { | |||
return filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP, FILTER_F LAG_IPV6) !== false; | return filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP, FILTER_F LAG_IPV6) !== false; | |||
} | } | |||
//If removing all the dots results in a numeric string, it must be an IP v4 address. | //If removing all the dots results in a numeric string, it must be an IP v4 address. | |||
//Need to check this first because otherwise things like `999.0.0.0` are considered valid host names | //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names | |||
if (is_numeric(str_replace('.', '', $host))) { | if (is_numeric(str_replace('.', '', $host))) { | |||
skipping to change at line 4060 | skipping to change at line 4151 | |||
* | * | |||
* @throws Exception | * @throws Exception | |||
*/ | */ | |||
public function addCustomHeader($name, $value = null) | public function addCustomHeader($name, $value = null) | |||
{ | { | |||
if (null === $value && strpos($name, ':') !== false) { | if (null === $value && strpos($name, ':') !== false) { | |||
//Value passed in as name:value | //Value passed in as name:value | |||
list($name, $value) = explode(':', $name, 2); | list($name, $value) = explode(':', $name, 2); | |||
} | } | |||
$name = trim($name); | $name = trim($name); | |||
$value = trim($value); | $value = (null === $value) ? '' : trim($value); | |||
//Ensure name is not empty, and that neither name nor value contain line breaks | //Ensure name is not empty, and that neither name nor value contain line breaks | |||
if (empty($name) || strpbrk($name . $value, "\r\n") !== false) { | if (empty($name) || strpbrk($name . $value, "\r\n") !== false) { | |||
if ($this->exceptions) { | if ($this->exceptions) { | |||
throw new Exception('Invalid header name or value'); | throw new Exception($this->lang('invalid_header')); | |||
} | } | |||
return false; | return false; | |||
} | } | |||
$this->CustomHeader[] = [$name, $value]; | $this->CustomHeader[] = [$name, $value]; | |||
return true; | return true; | |||
} | } | |||
/** | /** | |||
skipping to change at line 4218 | skipping to change at line 4309 | |||
* $plain = $mail->html2text($html); | * $plain = $mail->html2text($html); | |||
* //Use your own custom converter | * //Use your own custom converter | |||
* $plain = $mail->html2text($html, function($html) { | * $plain = $mail->html2text($html, function($html) { | |||
* $converter = new MyHtml2text($html); | * $converter = new MyHtml2text($html); | |||
* return $converter->get_text(); | * return $converter->get_text(); | |||
* }); | * }); | |||
* ``` | * ``` | |||
* | * | |||
* @param string $html The HTML text to convert | * @param string $html The HTML text to convert | |||
* @param bool|callable $advanced Any boolean value to use the internal conv erter, | * @param bool|callable $advanced Any boolean value to use the internal conv erter, | |||
* or provide your own callable for custom co | * or provide your own callable for custom co | |||
nversion | nversion. | |||
* *Never* pass user-supplied data into this | ||||
parameter | ||||
* | * | |||
* @return string | * @return string | |||
*/ | */ | |||
public function html2text($html, $advanced = false) | public function html2text($html, $advanced = false) | |||
{ | { | |||
if (is_callable($advanced)) { | if (is_callable($advanced)) { | |||
return call_user_func($advanced, $html); | return call_user_func($advanced, $html); | |||
} | } | |||
return html_entity_decode( | return html_entity_decode( | |||
End of changes. 46 change blocks. | ||||
53 lines changed or deleted | 165 lines changed or added |