|
|
@@ -561,9 +561,9 @@ class PHPMailer
|
|
|
* string $body the email body
|
|
|
* string $from email address of sender
|
|
|
* string $extra extra information of possible use
|
|
|
- * "smtp_transaction_id' => last smtp transaction id
|
|
|
+ * 'smtp_transaction_id' => last smtp transaction id
|
|
|
*
|
|
|
- * @var string
|
|
|
+ * @var callable|callable-string
|
|
|
*/
|
|
|
public $action_function = '';
|
|
|
|
|
|
@@ -711,7 +711,7 @@ class PHPMailer
|
|
|
*
|
|
|
* @var array
|
|
|
*/
|
|
|
- protected $language = [];
|
|
|
+ protected static $language = [];
|
|
|
|
|
|
/**
|
|
|
* The number of errors encountered.
|
|
|
@@ -768,7 +768,7 @@ class PHPMailer
|
|
|
*
|
|
|
* @var string
|
|
|
*/
|
|
|
- const VERSION = '6.10.0';
|
|
|
+ const VERSION = '6.11.1';
|
|
|
|
|
|
/**
|
|
|
* Error severity: message only, continue processing.
|
|
|
@@ -1102,7 +1102,7 @@ class PHPMailer
|
|
|
//At-sign is missing.
|
|
|
$error_message = sprintf(
|
|
|
'%s (%s): %s',
|
|
|
- $this->lang('invalid_address'),
|
|
|
+ self::lang('invalid_address'),
|
|
|
$kind,
|
|
|
$address
|
|
|
);
|
|
|
@@ -1187,7 +1187,7 @@ class PHPMailer
|
|
|
if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
|
|
|
$error_message = sprintf(
|
|
|
'%s: %s',
|
|
|
- $this->lang('Invalid recipient kind'),
|
|
|
+ self::lang('Invalid recipient kind'),
|
|
|
$kind
|
|
|
);
|
|
|
$this->setError($error_message);
|
|
|
@@ -1201,7 +1201,7 @@ class PHPMailer
|
|
|
if (!static::validateAddress($address)) {
|
|
|
$error_message = sprintf(
|
|
|
'%s (%s): %s',
|
|
|
- $this->lang('invalid_address'),
|
|
|
+ self::lang('invalid_address'),
|
|
|
$kind,
|
|
|
$address
|
|
|
);
|
|
|
@@ -1220,12 +1220,16 @@ class PHPMailer
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
- } elseif (!array_key_exists(strtolower($address), $this->ReplyTo)) {
|
|
|
- $this->ReplyTo[strtolower($address)] = [$address, $name];
|
|
|
+ } else {
|
|
|
+ foreach ($this->ReplyTo as $replyTo) {
|
|
|
+ if (0 === strcasecmp($replyTo[0], $address)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $this->ReplyTo[] = [$address, $name];
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
-
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
@@ -1238,15 +1242,18 @@ class PHPMailer
|
|
|
* @see https://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
|
|
|
*
|
|
|
* @param string $addrstr The address list string
|
|
|
- * @param bool $useimap Whether to use the IMAP extension to parse the list
|
|
|
+ * @param null $useimap Deprecated argument since 6.11.0.
|
|
|
* @param string $charset The charset to use when decoding the address list string.
|
|
|
*
|
|
|
* @return array
|
|
|
*/
|
|
|
- public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591)
|
|
|
+ public static function parseAddresses($addrstr, $useimap = null, $charset = self::CHARSET_ISO88591)
|
|
|
{
|
|
|
+ if ($useimap !== null) {
|
|
|
+ trigger_error(self::lang('deprecated_argument'), E_USER_DEPRECATED);
|
|
|
+ }
|
|
|
$addresses = [];
|
|
|
- if ($useimap && function_exists('imap_rfc822_parse_adrlist')) {
|
|
|
+ if (function_exists('imap_rfc822_parse_adrlist')) {
|
|
|
//Use this built-in parser if it's available
|
|
|
$list = imap_rfc822_parse_adrlist($addrstr, '');
|
|
|
// Clear any potential IMAP errors to get rid of notices being thrown at end of script.
|
|
|
@@ -1256,20 +1263,13 @@ class PHPMailer
|
|
|
'.SYNTAX-ERROR.' !== $address->host &&
|
|
|
static::validateAddress($address->mailbox . '@' . $address->host)
|
|
|
) {
|
|
|
- //Decode the name part if it's present and encoded
|
|
|
+ //Decode the name part if it's present and maybe encoded
|
|
|
if (
|
|
|
- property_exists($address, 'personal') &&
|
|
|
- //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
|
|
|
- defined('MB_CASE_UPPER') &&
|
|
|
- preg_match('/^=\?.*\?=$/s', $address->personal)
|
|
|
+ property_exists($address, 'personal')
|
|
|
+ && is_string($address->personal)
|
|
|
+ && $address->personal !== ''
|
|
|
) {
|
|
|
- $origCharset = mb_internal_encoding();
|
|
|
- mb_internal_encoding($charset);
|
|
|
- //Undo any RFC2047-encoded spaces-as-underscores
|
|
|
- $address->personal = str_replace('_', '=20', $address->personal);
|
|
|
- //Decode the name
|
|
|
- $address->personal = mb_decode_mimeheader($address->personal);
|
|
|
- mb_internal_encoding($origCharset);
|
|
|
+ $address->personal = static::decodeHeader($address->personal, $charset);
|
|
|
}
|
|
|
|
|
|
$addresses[] = [
|
|
|
@@ -1280,40 +1280,51 @@ class PHPMailer
|
|
|
}
|
|
|
} else {
|
|
|
//Use this simpler parser
|
|
|
- $list = explode(',', $addrstr);
|
|
|
- foreach ($list as $address) {
|
|
|
- $address = trim($address);
|
|
|
- //Is there a separate name part?
|
|
|
- if (strpos($address, '<') === false) {
|
|
|
- //No separate name, just use the whole thing
|
|
|
- if (static::validateAddress($address)) {
|
|
|
- $addresses[] = [
|
|
|
- 'name' => '',
|
|
|
- 'address' => $address,
|
|
|
- ];
|
|
|
- }
|
|
|
- } else {
|
|
|
- list($name, $email) = explode('<', $address);
|
|
|
- $email = trim(str_replace('>', '', $email));
|
|
|
- $name = trim($name);
|
|
|
- if (static::validateAddress($email)) {
|
|
|
- //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
|
|
|
- //If this name is encoded, decode it
|
|
|
- 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);
|
|
|
- mb_internal_encoding($origCharset);
|
|
|
- }
|
|
|
- $addresses[] = [
|
|
|
- //Remove any surrounding quotes and spaces from the name
|
|
|
- 'name' => trim($name, '\'" '),
|
|
|
- 'address' => $email,
|
|
|
- ];
|
|
|
- }
|
|
|
+ $addresses = static::parseSimplerAddresses($addrstr, $charset);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $addresses;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Parse a string containing one or more RFC822-style comma-separated email addresses
|
|
|
+ * with the form "display name <address>" into an array of name/address pairs.
|
|
|
+ * Uses a simpler parser that does not require the IMAP extension but doesnt support
|
|
|
+ * the full RFC822 spec. For full RFC822 support, use the PHP IMAP extension.
|
|
|
+ *
|
|
|
+ * @param string $addrstr The address list string
|
|
|
+ * @param string $charset The charset to use when decoding the address list string.
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected static function parseSimplerAddresses($addrstr, $charset)
|
|
|
+ {
|
|
|
+ // Emit a runtime notice to recommend using the IMAP extension for full RFC822 parsing
|
|
|
+ trigger_error(self::lang('imap_recommended'), E_USER_NOTICE);
|
|
|
+
|
|
|
+ $addresses = [];
|
|
|
+ $list = explode(',', $addrstr);
|
|
|
+ foreach ($list as $address) {
|
|
|
+ $address = trim($address);
|
|
|
+ //Is there a separate name part?
|
|
|
+ if (strpos($address, '<') === false) {
|
|
|
+ //No separate name, just use the whole thing
|
|
|
+ if (static::validateAddress($address)) {
|
|
|
+ $addresses[] = [
|
|
|
+ 'name' => '',
|
|
|
+ 'address' => $address,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ $parsed = static::parseEmailString($address);
|
|
|
+ $email = $parsed['email'];
|
|
|
+ if (static::validateAddress($email)) {
|
|
|
+ $name = static::decodeHeader($parsed['name'], $charset);
|
|
|
+ $addresses[] = [
|
|
|
+ //Remove any surrounding quotes and spaces from the name
|
|
|
+ 'name' => trim($name, '\'" '),
|
|
|
+ 'address' => $email,
|
|
|
+ ];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1321,6 +1332,42 @@ class PHPMailer
|
|
|
return $addresses;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Parse a string containing an email address with an optional name
|
|
|
+ * and divide it into a name and email address.
|
|
|
+ *
|
|
|
+ * @param string $input The email with name.
|
|
|
+ *
|
|
|
+ * @return array{name: string, email: string}
|
|
|
+ */
|
|
|
+ private static function parseEmailString($input)
|
|
|
+ {
|
|
|
+ $input = trim((string)$input);
|
|
|
+
|
|
|
+ if ($input === '') {
|
|
|
+ return ['name' => '', 'email' => ''];
|
|
|
+ }
|
|
|
+
|
|
|
+ $pattern = '/^\s*(?:(?:"([^"]*)"|\'([^\']*)\'|([^<]*?))\s*)?<\s*([^>]+)\s*>\s*$/';
|
|
|
+ if (preg_match($pattern, $input, $matches)) {
|
|
|
+ $name = '';
|
|
|
+ // Double quotes including special scenarios.
|
|
|
+ if (isset($matches[1]) && $matches[1] !== '') {
|
|
|
+ $name = $matches[1];
|
|
|
+ // Single quotes including special scenarios.
|
|
|
+ } elseif (isset($matches[2]) && $matches[2] !== '') {
|
|
|
+ $name = $matches[2];
|
|
|
+ // Simplest scenario, name and email are in the format "Name <email>".
|
|
|
+ } elseif (isset($matches[3])) {
|
|
|
+ $name = trim($matches[3]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ['name' => $name, 'email' => trim($matches[4])];
|
|
|
+ }
|
|
|
+
|
|
|
+ return ['name' => '', 'email' => $input];
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Set the From and FromName properties.
|
|
|
*
|
|
|
@@ -1334,6 +1381,10 @@ class PHPMailer
|
|
|
*/
|
|
|
public function setFrom($address, $name = '', $auto = true)
|
|
|
{
|
|
|
+ if (is_null($name)) {
|
|
|
+ //Helps avoid a deprecation warning in the preg_replace() below
|
|
|
+ $name = '';
|
|
|
+ }
|
|
|
$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().
|
|
|
@@ -1345,7 +1396,7 @@ class PHPMailer
|
|
|
) {
|
|
|
$error_message = sprintf(
|
|
|
'%s (From): %s',
|
|
|
- $this->lang('invalid_address'),
|
|
|
+ self::lang('invalid_address'),
|
|
|
$address
|
|
|
);
|
|
|
$this->setError($error_message);
|
|
|
@@ -1601,7 +1652,7 @@ class PHPMailer
|
|
|
&& ini_get('mail.add_x_header') === '1'
|
|
|
&& stripos(PHP_OS, 'WIN') === 0
|
|
|
) {
|
|
|
- trigger_error($this->lang('buggy_php'), E_USER_WARNING);
|
|
|
+ trigger_error(self::lang('buggy_php'), E_USER_WARNING);
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
@@ -1631,7 +1682,7 @@ class PHPMailer
|
|
|
call_user_func_array([$this, 'addAnAddress'], $params);
|
|
|
}
|
|
|
if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
|
|
|
- throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('provide_address'), self::STOP_CRITICAL);
|
|
|
}
|
|
|
|
|
|
//Validate From, Sender, and ConfirmReadingTo addresses
|
|
|
@@ -1648,7 +1699,7 @@ class PHPMailer
|
|
|
if (!static::validateAddress($this->{$address_kind})) {
|
|
|
$error_message = sprintf(
|
|
|
'%s (%s): %s',
|
|
|
- $this->lang('invalid_address'),
|
|
|
+ self::lang('invalid_address'),
|
|
|
$address_kind,
|
|
|
$this->{$address_kind}
|
|
|
);
|
|
|
@@ -1670,7 +1721,7 @@ class PHPMailer
|
|
|
$this->setMessageType();
|
|
|
//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);
|
|
|
+ throw new Exception(self::lang('empty_message'), self::STOP_CRITICAL);
|
|
|
}
|
|
|
|
|
|
//Trim subject consistently
|
|
|
@@ -1809,8 +1860,10 @@ class PHPMailer
|
|
|
} else {
|
|
|
$sendmailFmt = '%s -oi -f%s -t';
|
|
|
}
|
|
|
+ } elseif ($this->Mailer === 'qmail') {
|
|
|
+ $sendmailFmt = '%s';
|
|
|
} else {
|
|
|
- //allow sendmail to choose a default envelope sender. It may
|
|
|
+ //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
|
|
|
@@ -1828,33 +1881,35 @@ class PHPMailer
|
|
|
foreach ($this->SingleToArray as $toAddr) {
|
|
|
$mail = @popen($sendmail, 'w');
|
|
|
if (!$mail) {
|
|
|
- throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::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, true, $this->CharSet);
|
|
|
- $this->doCallback(
|
|
|
- ($result === 0),
|
|
|
- [[$addrinfo['address'], $addrinfo['name']]],
|
|
|
- $this->cc,
|
|
|
- $this->bcc,
|
|
|
- $this->Subject,
|
|
|
- $body,
|
|
|
- $this->From,
|
|
|
- []
|
|
|
- );
|
|
|
+ $addrinfo = static::parseAddresses($toAddr, null, $this->CharSet);
|
|
|
+ foreach ($addrinfo as $addr) {
|
|
|
+ $this->doCallback(
|
|
|
+ ($result === 0),
|
|
|
+ [[$addr['address'], $addr['name']]],
|
|
|
+ $this->cc,
|
|
|
+ $this->bcc,
|
|
|
+ $this->Subject,
|
|
|
+ $body,
|
|
|
+ $this->From,
|
|
|
+ []
|
|
|
+ );
|
|
|
+ }
|
|
|
$this->edebug("Result: " . ($result === 0 ? 'true' : 'false'));
|
|
|
if (0 !== $result) {
|
|
|
- throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
$mail = @popen($sendmail, 'w');
|
|
|
if (!$mail) {
|
|
|
- throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
|
|
|
}
|
|
|
fwrite($mail, $header);
|
|
|
fwrite($mail, $body);
|
|
|
@@ -1871,7 +1926,7 @@ class PHPMailer
|
|
|
);
|
|
|
$this->edebug("Result: " . ($result === 0 ? 'true' : 'false'));
|
|
|
if (0 !== $result) {
|
|
|
- throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2010,17 +2065,19 @@ class PHPMailer
|
|
|
if ($this->SingleTo && count($toArr) > 1) {
|
|
|
foreach ($toArr as $toAddr) {
|
|
|
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
|
|
|
- $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet);
|
|
|
- $this->doCallback(
|
|
|
- $result,
|
|
|
- [[$addrinfo['address'], $addrinfo['name']]],
|
|
|
- $this->cc,
|
|
|
- $this->bcc,
|
|
|
- $this->Subject,
|
|
|
- $body,
|
|
|
- $this->From,
|
|
|
- []
|
|
|
- );
|
|
|
+ $addrinfo = static::parseAddresses($toAddr, null, $this->CharSet);
|
|
|
+ foreach ($addrinfo as $addr) {
|
|
|
+ $this->doCallback(
|
|
|
+ $result,
|
|
|
+ [[$addr['address'], $addr['name']]],
|
|
|
+ $this->cc,
|
|
|
+ $this->bcc,
|
|
|
+ $this->Subject,
|
|
|
+ $body,
|
|
|
+ $this->From,
|
|
|
+ []
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
} else {
|
|
|
$result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
|
|
|
@@ -2030,7 +2087,7 @@ class PHPMailer
|
|
|
ini_set('sendmail_from', $old_from);
|
|
|
}
|
|
|
if (!$result) {
|
|
|
- throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('instantiate'), self::STOP_CRITICAL);
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
@@ -2116,12 +2173,12 @@ class PHPMailer
|
|
|
$header = static::stripTrailingWSP($header) . static::$LE . static::$LE;
|
|
|
$bad_rcpt = [];
|
|
|
if (!$this->smtpConnect($this->SMTPOptions)) {
|
|
|
- throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('smtp_connect_failed'), self::STOP_CRITICAL);
|
|
|
}
|
|
|
//If we have recipient addresses that need Unicode support,
|
|
|
//but the server doesn't support it, stop here
|
|
|
if ($this->UseSMTPUTF8 && !$this->smtp->getServerExt('SMTPUTF8')) {
|
|
|
- throw new Exception($this->lang('no_smtputf8'), self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('no_smtputf8'), self::STOP_CRITICAL);
|
|
|
}
|
|
|
//Sender already validated in preSend()
|
|
|
if ('' === $this->Sender) {
|
|
|
@@ -2133,7 +2190,7 @@ class PHPMailer
|
|
|
$this->smtp->xclient($this->SMTPXClient);
|
|
|
}
|
|
|
if (!$this->smtp->mail($smtp_from)) {
|
|
|
- $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
|
|
|
+ $this->setError(self::lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
|
|
|
throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
|
|
|
}
|
|
|
|
|
|
@@ -2155,7 +2212,7 @@ class PHPMailer
|
|
|
|
|
|
//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);
|
|
|
+ throw new Exception(self::lang('data_not_accepted'), self::STOP_CRITICAL);
|
|
|
}
|
|
|
|
|
|
$smtp_transaction_id = $this->smtp->getLastTransactionID();
|
|
|
@@ -2186,7 +2243,7 @@ class PHPMailer
|
|
|
foreach ($bad_rcpt as $bad) {
|
|
|
$errstr .= $bad['to'] . ': ' . $bad['error'];
|
|
|
}
|
|
|
- throw new Exception($this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE);
|
|
|
+ throw new Exception(self::lang('recipients_failed') . $errstr, self::STOP_CONTINUE);
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
@@ -2240,7 +2297,7 @@ class PHPMailer
|
|
|
$hostinfo
|
|
|
)
|
|
|
) {
|
|
|
- $this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry));
|
|
|
+ $this->edebug(self::lang('invalid_hostentry') . ' ' . trim($hostentry));
|
|
|
//Not a valid host entry
|
|
|
continue;
|
|
|
}
|
|
|
@@ -2252,7 +2309,7 @@ class PHPMailer
|
|
|
|
|
|
//Check the host name is a valid name or IP address before trying to use it
|
|
|
if (!static::isValidHost($hostinfo[2])) {
|
|
|
- $this->edebug($this->lang('invalid_host') . ' ' . $hostinfo[2]);
|
|
|
+ $this->edebug(self::lang('invalid_host') . ' ' . $hostinfo[2]);
|
|
|
continue;
|
|
|
}
|
|
|
$prefix = '';
|
|
|
@@ -2272,7 +2329,7 @@ class PHPMailer
|
|
|
if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) {
|
|
|
//Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
|
|
|
if (!$sslext) {
|
|
|
- throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('extension_missing') . 'openssl', self::STOP_CRITICAL);
|
|
|
}
|
|
|
}
|
|
|
$host = $hostinfo[2];
|
|
|
@@ -2324,7 +2381,7 @@ class PHPMailer
|
|
|
$this->oauth
|
|
|
)
|
|
|
) {
|
|
|
- throw new Exception($this->lang('authenticate'));
|
|
|
+ throw new Exception(self::lang('authenticate'));
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
@@ -2374,7 +2431,7 @@ class PHPMailer
|
|
|
*
|
|
|
* @return bool Returns true if the requested language was loaded, false otherwise.
|
|
|
*/
|
|
|
- public function setLanguage($langcode = 'en', $lang_path = '')
|
|
|
+ public static function setLanguage($langcode = 'en', $lang_path = '')
|
|
|
{
|
|
|
//Backwards compatibility for renamed language codes
|
|
|
$renamed_langcodes = [
|
|
|
@@ -2423,6 +2480,9 @@ class PHPMailer
|
|
|
'smtp_error' => 'SMTP server error: ',
|
|
|
'variable_set' => 'Cannot set or reset variable: ',
|
|
|
'no_smtputf8' => 'Server does not support SMTPUTF8 needed to send to Unicode addresses',
|
|
|
+ 'imap_recommended' => 'Using simplified address parser is not recommended. ' .
|
|
|
+ 'Install the PHP IMAP extension for full RFC822 parsing.',
|
|
|
+ 'deprecated_argument' => 'Argument $useimap is deprecated',
|
|
|
];
|
|
|
if (empty($lang_path)) {
|
|
|
//Calculate an absolute path so it can work if CWD is not here
|
|
|
@@ -2489,7 +2549,7 @@ class PHPMailer
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- $this->language = $PHPMAILER_LANG;
|
|
|
+ self::$language = $PHPMAILER_LANG;
|
|
|
|
|
|
return $foundlang; //Returns false if language not found
|
|
|
}
|
|
|
@@ -2501,11 +2561,11 @@ class PHPMailer
|
|
|
*/
|
|
|
public function getTranslations()
|
|
|
{
|
|
|
- if (empty($this->language)) {
|
|
|
- $this->setLanguage(); // Set the default language.
|
|
|
+ if (empty(self::$language)) {
|
|
|
+ self::setLanguage(); // Set the default language.
|
|
|
}
|
|
|
|
|
|
- return $this->language;
|
|
|
+ return self::$language;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -2928,10 +2988,6 @@ class PHPMailer
|
|
|
//Create unique IDs and preset boundaries
|
|
|
$this->setBoundaries();
|
|
|
|
|
|
- if ($this->sign_key_file) {
|
|
|
- $body .= $this->getMailMIME() . static::$LE;
|
|
|
- }
|
|
|
-
|
|
|
$this->setWordWrap();
|
|
|
|
|
|
$bodyEncoding = $this->Encoding;
|
|
|
@@ -2963,6 +3019,12 @@ class PHPMailer
|
|
|
if (static::ENCODING_BASE64 !== $altBodyEncoding && static::hasLineLongerThanMax($this->AltBody)) {
|
|
|
$altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE;
|
|
|
}
|
|
|
+
|
|
|
+ if ($this->sign_key_file) {
|
|
|
+ $this->Encoding = $bodyEncoding;
|
|
|
+ $body .= $this->getMailMIME() . static::$LE;
|
|
|
+ }
|
|
|
+
|
|
|
//Use this as a preamble in all multipart message types
|
|
|
$mimepre = '';
|
|
|
switch ($this->message_type) {
|
|
|
@@ -3144,12 +3206,12 @@ class PHPMailer
|
|
|
if ($this->isError()) {
|
|
|
$body = '';
|
|
|
if ($this->exceptions) {
|
|
|
- throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
|
|
|
+ throw new Exception(self::lang('empty_message'), self::STOP_CRITICAL);
|
|
|
}
|
|
|
} elseif ($this->sign_key_file) {
|
|
|
try {
|
|
|
if (!defined('PKCS7_TEXT')) {
|
|
|
- throw new Exception($this->lang('extension_missing') . 'openssl');
|
|
|
+ throw new Exception(self::lang('extension_missing') . 'openssl');
|
|
|
}
|
|
|
|
|
|
$file = tempnam(sys_get_temp_dir(), 'srcsign');
|
|
|
@@ -3187,7 +3249,7 @@ class PHPMailer
|
|
|
$body = $parts[1];
|
|
|
} else {
|
|
|
@unlink($signed);
|
|
|
- throw new Exception($this->lang('signing') . openssl_error_string());
|
|
|
+ throw new Exception(self::lang('signing') . openssl_error_string());
|
|
|
}
|
|
|
} catch (Exception $exc) {
|
|
|
$body = '';
|
|
|
@@ -3332,7 +3394,7 @@ class PHPMailer
|
|
|
) {
|
|
|
try {
|
|
|
if (!static::fileIsAccessible($path)) {
|
|
|
- throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
|
|
|
+ throw new Exception(self::lang('file_access') . $path, self::STOP_CONTINUE);
|
|
|
}
|
|
|
|
|
|
//If a MIME type is not specified, try to work it out from the file name
|
|
|
@@ -3345,7 +3407,7 @@ class PHPMailer
|
|
|
$name = $filename;
|
|
|
}
|
|
|
if (!$this->validateEncoding($encoding)) {
|
|
|
- throw new Exception($this->lang('encoding') . $encoding);
|
|
|
+ throw new Exception(self::lang('encoding') . $encoding);
|
|
|
}
|
|
|
|
|
|
$this->attachment[] = [
|
|
|
@@ -3506,11 +3568,11 @@ class PHPMailer
|
|
|
{
|
|
|
try {
|
|
|
if (!static::fileIsAccessible($path)) {
|
|
|
- throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
|
|
|
+ throw new Exception(self::lang('file_open') . $path, self::STOP_CONTINUE);
|
|
|
}
|
|
|
$file_buffer = file_get_contents($path);
|
|
|
if (false === $file_buffer) {
|
|
|
- throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
|
|
|
+ throw new Exception(self::lang('file_open') . $path, self::STOP_CONTINUE);
|
|
|
}
|
|
|
$file_buffer = $this->encodeString($file_buffer, $encoding);
|
|
|
|
|
|
@@ -3563,9 +3625,9 @@ class PHPMailer
|
|
|
$encoded = $this->encodeQP($str);
|
|
|
break;
|
|
|
default:
|
|
|
- $this->setError($this->lang('encoding') . $encoding);
|
|
|
+ $this->setError(self::lang('encoding') . $encoding);
|
|
|
if ($this->exceptions) {
|
|
|
- throw new Exception($this->lang('encoding') . $encoding);
|
|
|
+ throw new Exception(self::lang('encoding') . $encoding);
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
@@ -3671,6 +3733,42 @@ class PHPMailer
|
|
|
return trim(static::normalizeBreaks($encoded));
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Decode an RFC2047-encoded header value
|
|
|
+ * Attempts multiple strategies so it works even when the mbstring extension is disabled.
|
|
|
+ *
|
|
|
+ * @param string $value The header value to decode
|
|
|
+ * @param string $charset The target charset to convert to, defaults to ISO-8859-1 for BC
|
|
|
+ *
|
|
|
+ * @return string The decoded header value
|
|
|
+ */
|
|
|
+ public static function decodeHeader($value, $charset = self::CHARSET_ISO88591)
|
|
|
+ {
|
|
|
+ if (!is_string($value) || $value === '') {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ // Detect the presence of any RFC2047 encoded-words
|
|
|
+ $hasEncodedWord = (bool) preg_match('/=\?.*\?=/s', $value);
|
|
|
+ if ($hasEncodedWord && defined('MB_CASE_UPPER')) {
|
|
|
+ $origCharset = mb_internal_encoding();
|
|
|
+ // Always decode to UTF-8 to provide a consistent, modern output encoding.
|
|
|
+ mb_internal_encoding($charset);
|
|
|
+ if (PHP_VERSION_ID < 80300) {
|
|
|
+ // Undo any RFC2047-encoded spaces-as-underscores.
|
|
|
+ $value = str_replace('_', '=20', $value);
|
|
|
+ } else {
|
|
|
+ // PHP 8.3+ already interprets underscores as spaces. Remove additional
|
|
|
+ // linear whitespace between adjacent encoded words to avoid double spacing.
|
|
|
+ $value = preg_replace('/(\?=)\s+(=\?)/', '$1$2', $value);
|
|
|
+ }
|
|
|
+ // Decode the header value
|
|
|
+ $value = mb_decode_mimeheader($value);
|
|
|
+ mb_internal_encoding($origCharset);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $value;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Check if a string contains multi-byte characters.
|
|
|
*
|
|
|
@@ -3840,7 +3938,7 @@ class PHPMailer
|
|
|
}
|
|
|
|
|
|
if (!$this->validateEncoding($encoding)) {
|
|
|
- throw new Exception($this->lang('encoding') . $encoding);
|
|
|
+ throw new Exception(self::lang('encoding') . $encoding);
|
|
|
}
|
|
|
|
|
|
//Append to $attachment array
|
|
|
@@ -3899,7 +3997,7 @@ class PHPMailer
|
|
|
) {
|
|
|
try {
|
|
|
if (!static::fileIsAccessible($path)) {
|
|
|
- throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
|
|
|
+ throw new Exception(self::lang('file_access') . $path, self::STOP_CONTINUE);
|
|
|
}
|
|
|
|
|
|
//If a MIME type is not specified, try to work it out from the file name
|
|
|
@@ -3908,7 +4006,7 @@ class PHPMailer
|
|
|
}
|
|
|
|
|
|
if (!$this->validateEncoding($encoding)) {
|
|
|
- throw new Exception($this->lang('encoding') . $encoding);
|
|
|
+ throw new Exception(self::lang('encoding') . $encoding);
|
|
|
}
|
|
|
|
|
|
$filename = (string) static::mb_pathinfo($path, PATHINFO_BASENAME);
|
|
|
@@ -3974,7 +4072,7 @@ class PHPMailer
|
|
|
}
|
|
|
|
|
|
if (!$this->validateEncoding($encoding)) {
|
|
|
- throw new Exception($this->lang('encoding') . $encoding);
|
|
|
+ throw new Exception(self::lang('encoding') . $encoding);
|
|
|
}
|
|
|
|
|
|
//Append to $attachment array
|
|
|
@@ -4231,7 +4329,7 @@ class PHPMailer
|
|
|
}
|
|
|
if (strpbrk($name . $value, "\r\n") !== false) {
|
|
|
if ($this->exceptions) {
|
|
|
- throw new Exception($this->lang('invalid_header'));
|
|
|
+ throw new Exception(self::lang('invalid_header'));
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
@@ -4255,15 +4353,15 @@ class PHPMailer
|
|
|
if ('smtp' === $this->Mailer && null !== $this->smtp) {
|
|
|
$lasterror = $this->smtp->getError();
|
|
|
if (!empty($lasterror['error'])) {
|
|
|
- $msg .= ' ' . $this->lang('smtp_error') . $lasterror['error'];
|
|
|
+ $msg .= ' ' . self::lang('smtp_error') . $lasterror['error'];
|
|
|
if (!empty($lasterror['detail'])) {
|
|
|
- $msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detail'];
|
|
|
+ $msg .= ' ' . self::lang('smtp_detail') . $lasterror['detail'];
|
|
|
}
|
|
|
if (!empty($lasterror['smtp_code'])) {
|
|
|
- $msg .= ' ' . $this->lang('smtp_code') . $lasterror['smtp_code'];
|
|
|
+ $msg .= ' ' . self::lang('smtp_code') . $lasterror['smtp_code'];
|
|
|
}
|
|
|
if (!empty($lasterror['smtp_code_ex'])) {
|
|
|
- $msg .= ' ' . $this->lang('smtp_code_ex') . $lasterror['smtp_code_ex'];
|
|
|
+ $msg .= ' ' . self::lang('smtp_code_ex') . $lasterror['smtp_code_ex'];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -4388,21 +4486,21 @@ class PHPMailer
|
|
|
*
|
|
|
* @return string
|
|
|
*/
|
|
|
- protected function lang($key)
|
|
|
+ protected static function lang($key)
|
|
|
{
|
|
|
- if (count($this->language) < 1) {
|
|
|
- $this->setLanguage(); //Set the default language
|
|
|
+ if (count(self::$language) < 1) {
|
|
|
+ self::setLanguage(); //Set the default language
|
|
|
}
|
|
|
|
|
|
- if (array_key_exists($key, $this->language)) {
|
|
|
+ if (array_key_exists($key, self::$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
|
|
|
//but it's usually not PHPMailer's fault.
|
|
|
- return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
|
|
|
+ return self::$language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
|
|
|
}
|
|
|
|
|
|
- return $this->language[$key];
|
|
|
+ return self::$language[$key];
|
|
|
}
|
|
|
|
|
|
//Return the key as a fallback
|
|
|
@@ -4417,7 +4515,7 @@ class PHPMailer
|
|
|
*/
|
|
|
private function getSmtpErrorMessage($base_key)
|
|
|
{
|
|
|
- $message = $this->lang($base_key);
|
|
|
+ $message = self::lang($base_key);
|
|
|
$error = $this->smtp->getError();
|
|
|
if (!empty($error['error'])) {
|
|
|
$message .= ' ' . $error['error'];
|
|
|
@@ -4461,7 +4559,7 @@ class PHPMailer
|
|
|
//Ensure name is not empty, and that neither name nor value contain line breaks
|
|
|
if (empty($name) || strpbrk($name . $value, "\r\n") !== false) {
|
|
|
if ($this->exceptions) {
|
|
|
- throw new Exception($this->lang('invalid_header'));
|
|
|
+ throw new Exception(self::lang('invalid_header'));
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
@@ -4854,7 +4952,7 @@ class PHPMailer
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
- $this->setError($this->lang('variable_set') . $name);
|
|
|
+ $this->setError(self::lang('variable_set') . $name);
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
@@ -4992,7 +5090,7 @@ class PHPMailer
|
|
|
{
|
|
|
if (!defined('PKCS7_TEXT')) {
|
|
|
if ($this->exceptions) {
|
|
|
- throw new Exception($this->lang('extension_missing') . 'openssl');
|
|
|
+ throw new Exception(self::lang('extension_missing') . 'openssl');
|
|
|
}
|
|
|
|
|
|
return '';
|