wp/wp-includes/PHPMailer/PHPMailer.php
changeset 19 3d72ae0968f4
parent 18 be944660c56a
child 21 48c4eec2b7e6
equal deleted inserted replaced
18:be944660c56a 19:3d72ae0968f4
   101     /**
   101     /**
   102      * The From email address for the message.
   102      * The From email address for the message.
   103      *
   103      *
   104      * @var string
   104      * @var string
   105      */
   105      */
   106     public $From = 'root@localhost';
   106     public $From = '';
   107 
   107 
   108     /**
   108     /**
   109      * The From name of the message.
   109      * The From name of the message.
   110      *
   110      *
   111      * @var string
   111      * @var string
   112      */
   112      */
   113     public $FromName = 'Root User';
   113     public $FromName = '';
   114 
   114 
   115     /**
   115     /**
   116      * The envelope sender of the message.
   116      * The envelope sender of the message.
   117      * This will usually be turned into a Return-Path header by the receiver,
   117      * This will usually be turned into a Return-Path header by the receiver,
   118      * and is the address that bounces will be sent to.
   118      * and is the address that bounces will be sent to.
   356      * @var string
   356      * @var string
   357      */
   357      */
   358     public $AuthType = '';
   358     public $AuthType = '';
   359 
   359 
   360     /**
   360     /**
   361      * An instance of the PHPMailer OAuth class.
   361      * An implementation of the PHPMailer OAuthTokenProvider interface.
   362      *
   362      *
   363      * @var OAuth
   363      * @var OAuthTokenProvider
   364      */
   364      */
   365     protected $oauth;
   365     protected $oauth;
   366 
   366 
   367     /**
   367     /**
   368      * The SMTP server timeout in seconds.
   368      * The SMTP server timeout in seconds.
   687      * @var array
   687      * @var array
   688      */
   688      */
   689     protected $boundary = [];
   689     protected $boundary = [];
   690 
   690 
   691     /**
   691     /**
   692      * The array of available languages.
   692      * The array of available text strings for the current language.
   693      *
   693      *
   694      * @var array
   694      * @var array
   695      */
   695      */
   696     protected $language = [];
   696     protected $language = [];
   697 
   697 
   748     /**
   748     /**
   749      * The PHPMailer Version number.
   749      * The PHPMailer Version number.
   750      *
   750      *
   751      * @var string
   751      * @var string
   752      */
   752      */
   753     const VERSION = '6.5.0';
   753     const VERSION = '6.6.0';
   754 
   754 
   755     /**
   755     /**
   756      * Error severity: message only, continue processing.
   756      * Error severity: message only, continue processing.
   757      *
   757      *
   758      * @var int
   758      * @var int
  1183      *
  1183      *
  1184      * @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
  1184      * @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
  1185      *
  1185      *
  1186      * @param string $addrstr The address list string
  1186      * @param string $addrstr The address list string
  1187      * @param bool   $useimap Whether to use the IMAP extension to parse the list
  1187      * @param bool   $useimap Whether to use the IMAP extension to parse the list
       
  1188      * @param string $charset The charset to use when decoding the address list string.
  1188      *
  1189      *
  1189      * @return array
  1190      * @return array
  1190      */
  1191      */
  1191     public static function parseAddresses($addrstr, $useimap = true)
  1192     public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591)
  1192     {
  1193     {
  1193         $addresses = [];
  1194         $addresses = [];
  1194         if ($useimap && function_exists('imap_rfc822_parse_adrlist')) {
  1195         if ($useimap && function_exists('imap_rfc822_parse_adrlist')) {
  1195             //Use this built-in parser if it's available
  1196             //Use this built-in parser if it's available
  1196             $list = imap_rfc822_parse_adrlist($addrstr, '');
  1197             $list = imap_rfc822_parse_adrlist($addrstr, '');
       
  1198             // Clear any potential IMAP errors to get rid of notices being thrown at end of script.
       
  1199             imap_errors();
  1197             foreach ($list as $address) {
  1200             foreach ($list as $address) {
  1198                 if (
  1201                 if (
  1199                     ('.SYNTAX-ERROR.' !== $address->host) && static::validateAddress(
  1202                     '.SYNTAX-ERROR.' !== $address->host &&
  1200                         $address->mailbox . '@' . $address->host
  1203                     static::validateAddress($address->mailbox . '@' . $address->host)
  1201                     )
       
  1202                 ) {
  1204                 ) {
  1203                     //Decode the name part if it's present and encoded
  1205                     //Decode the name part if it's present and encoded
  1204                     if (
  1206                     if (
  1205                         property_exists($address, 'personal') &&
  1207                         property_exists($address, 'personal') &&
  1206                         extension_loaded('mbstring') &&
  1208                         //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
  1207                         preg_match('/^=\?.*\?=$/', $address->personal)
  1209                         defined('MB_CASE_UPPER') &&
       
  1210                         preg_match('/^=\?.*\?=$/s', $address->personal)
  1208                     ) {
  1211                     ) {
       
  1212                         $origCharset = mb_internal_encoding();
       
  1213                         mb_internal_encoding($charset);
       
  1214                         //Undo any RFC2047-encoded spaces-as-underscores
       
  1215                         $address->personal = str_replace('_', '=20', $address->personal);
       
  1216                         //Decode the name
  1209                         $address->personal = mb_decode_mimeheader($address->personal);
  1217                         $address->personal = mb_decode_mimeheader($address->personal);
       
  1218                         mb_internal_encoding($origCharset);
  1210                     }
  1219                     }
  1211 
  1220 
  1212                     $addresses[] = [
  1221                     $addresses[] = [
  1213                         'name' => (property_exists($address, 'personal') ? $address->personal : ''),
  1222                         'name' => (property_exists($address, 'personal') ? $address->personal : ''),
  1214                         'address' => $address->mailbox . '@' . $address->host,
  1223                         'address' => $address->mailbox . '@' . $address->host,
  1232                 } else {
  1241                 } else {
  1233                     list($name, $email) = explode('<', $address);
  1242                     list($name, $email) = explode('<', $address);
  1234                     $email = trim(str_replace('>', '', $email));
  1243                     $email = trim(str_replace('>', '', $email));
  1235                     $name = trim($name);
  1244                     $name = trim($name);
  1236                     if (static::validateAddress($email)) {
  1245                     if (static::validateAddress($email)) {
       
  1246                         //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
  1237                         //If this name is encoded, decode it
  1247                         //If this name is encoded, decode it
  1238                         if (preg_match('/^=\?.*\?=$/', $name)) {
  1248                         if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) {
       
  1249                             $origCharset = mb_internal_encoding();
       
  1250                             mb_internal_encoding($charset);
       
  1251                             //Undo any RFC2047-encoded spaces-as-underscores
       
  1252                             $name = str_replace('_', '=20', $name);
       
  1253                             //Decode the name
  1239                             $name = mb_decode_mimeheader($name);
  1254                             $name = mb_decode_mimeheader($name);
       
  1255                             mb_internal_encoding($origCharset);
  1240                         }
  1256                         }
  1241                         $addresses[] = [
  1257                         $addresses[] = [
  1242                             //Remove any surrounding quotes and spaces from the name
  1258                             //Remove any surrounding quotes and spaces from the name
  1243                             'name' => trim($name, '\'" '),
  1259                             'name' => trim($name, '\'" '),
  1244                             'address' => $email,
  1260                             'address' => $email,
  1434                 $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet);
  1450                 $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet);
  1435                 //Ignore IDE complaints about this line - method signature changed in PHP 5.4
  1451                 //Ignore IDE complaints about this line - method signature changed in PHP 5.4
  1436                 $errorcode = 0;
  1452                 $errorcode = 0;
  1437                 if (defined('INTL_IDNA_VARIANT_UTS46')) {
  1453                 if (defined('INTL_IDNA_VARIANT_UTS46')) {
  1438                     //Use the current punycode standard (appeared in PHP 7.2)
  1454                     //Use the current punycode standard (appeared in PHP 7.2)
  1439                     $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_UTS46);
  1455                     $punycode = idn_to_ascii(
       
  1456                         $domain,
       
  1457                         \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI |
       
  1458                             \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII,
       
  1459                         \INTL_IDNA_VARIANT_UTS46
       
  1460                     );
  1440                 } elseif (defined('INTL_IDNA_VARIANT_2003')) {
  1461                 } elseif (defined('INTL_IDNA_VARIANT_2003')) {
  1441                     //Fall back to this old, deprecated/removed encoding
  1462                     //Fall back to this old, deprecated/removed encoding
  1442                     // phpcs:ignore PHPCompatibility.Constants.RemovedConstants.intl_idna_variant_2003Deprecated
  1463                     // phpcs:ignore PHPCompatibility.Constants.RemovedConstants.intl_idna_variant_2003Deprecated
  1443                     $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003);
  1464                     $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003);
  1444                 } else {
  1465                 } else {
  1508             && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017)
  1529             && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017)
  1509                 || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103))
  1530                 || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103))
  1510             && ini_get('mail.add_x_header') === '1'
  1531             && ini_get('mail.add_x_header') === '1'
  1511             && stripos(PHP_OS, 'WIN') === 0
  1532             && stripos(PHP_OS, 'WIN') === 0
  1512         ) {
  1533         ) {
  1513             trigger_error(
  1534             trigger_error($this->lang('buggy_php'), E_USER_WARNING);
  1514                 'Your version of PHP is affected by a bug that may result in corrupted messages.' .
       
  1515                 ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' .
       
  1516                 ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.',
       
  1517                 E_USER_WARNING
       
  1518             );
       
  1519         }
  1535         }
  1520 
  1536 
  1521         try {
  1537         try {
  1522             $this->error_count = 0; //Reset errors
  1538             $this->error_count = 0; //Reset errors
  1523             $this->mailHeader = '';
  1539             $this->mailHeader = '';
  1687         //causing problems, so we don't use one
  1703         //causing problems, so we don't use one
  1688         //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
  1704         //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
  1689         //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
  1705         //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
  1690         //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
  1706         //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
  1691         //Example problem: https://www.drupal.org/node/1057954
  1707         //Example problem: https://www.drupal.org/node/1057954
  1692         if (empty($this->Sender) && !empty(ini_get('sendmail_from'))) {
  1708 
       
  1709         //PHP 5.6 workaround
       
  1710         $sendmail_from_value = ini_get('sendmail_from');
       
  1711         if (empty($this->Sender) && !empty($sendmail_from_value)) {
  1693             //PHP config has a sender address we can use
  1712             //PHP config has a sender address we can use
  1694             $this->Sender = ini_get('sendmail_from');
  1713             $this->Sender = ini_get('sendmail_from');
  1695         }
  1714         }
  1696         //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
  1715         //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
  1697         if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) {
  1716         if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) {
  1724                 $this->edebug("To: {$toAddr}");
  1743                 $this->edebug("To: {$toAddr}");
  1725                 fwrite($mail, 'To: ' . $toAddr . "\n");
  1744                 fwrite($mail, 'To: ' . $toAddr . "\n");
  1726                 fwrite($mail, $header);
  1745                 fwrite($mail, $header);
  1727                 fwrite($mail, $body);
  1746                 fwrite($mail, $body);
  1728                 $result = pclose($mail);
  1747                 $result = pclose($mail);
  1729                 $addrinfo = static::parseAddresses($toAddr);
  1748                 $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet);
  1730                 $this->doCallback(
  1749                 $this->doCallback(
  1731                     ($result === 0),
  1750                     ($result === 0),
  1732                     [[$addrinfo['address'], $addrinfo['name']]],
  1751                     [[$addrinfo['address'], $addrinfo['name']]],
  1733                     $this->cc,
  1752                     $this->cc,
  1734                     $this->bcc,
  1753                     $this->bcc,
  1779      *
  1798      *
  1780      * @return bool
  1799      * @return bool
  1781      */
  1800      */
  1782     protected static function isShellSafe($string)
  1801     protected static function isShellSafe($string)
  1783     {
  1802     {
  1784         //Future-proof
  1803         //It's not possible to use shell commands safely (which includes the mail() function) without escapeshellarg,
       
  1804         //but some hosting providers disable it, creating a security problem that we don't want to have to deal with,
       
  1805         //so we don't.
       
  1806         if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) {
       
  1807             return false;
       
  1808         }
       
  1809 
  1785         if (
  1810         if (
  1786             escapeshellcmd($string) !== $string
  1811             escapeshellcmd($string) !== $string
  1787             || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
  1812             || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
  1788         ) {
  1813         ) {
  1789             return false;
  1814             return false;
  1869         //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
  1894         //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
  1870         //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
  1895         //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
  1871         //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
  1896         //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
  1872         //Example problem: https://www.drupal.org/node/1057954
  1897         //Example problem: https://www.drupal.org/node/1057954
  1873         //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
  1898         //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
  1874         if (empty($this->Sender) && !empty(ini_get('sendmail_from'))) {
  1899 
       
  1900         //PHP 5.6 workaround
       
  1901         $sendmail_from_value = ini_get('sendmail_from');
       
  1902         if (empty($this->Sender) && !empty($sendmail_from_value)) {
  1875             //PHP config has a sender address we can use
  1903             //PHP config has a sender address we can use
  1876             $this->Sender = ini_get('sendmail_from');
  1904             $this->Sender = ini_get('sendmail_from');
  1877         }
  1905         }
  1878         if (!empty($this->Sender) && static::validateAddress($this->Sender)) {
  1906         if (!empty($this->Sender) && static::validateAddress($this->Sender)) {
  1879             if (self::isShellSafe($this->Sender)) {
  1907             if (self::isShellSafe($this->Sender)) {
  1884         }
  1912         }
  1885         $result = false;
  1913         $result = false;
  1886         if ($this->SingleTo && count($toArr) > 1) {
  1914         if ($this->SingleTo && count($toArr) > 1) {
  1887             foreach ($toArr as $toAddr) {
  1915             foreach ($toArr as $toAddr) {
  1888                 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
  1916                 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
  1889                 $addrinfo = static::parseAddresses($toAddr);
  1917                 $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet);
  1890                 $this->doCallback(
  1918                 $this->doCallback(
  1891                     $result,
  1919                     $result,
  1892                     [[$addrinfo['address'], $addrinfo['name']]],
  1920                     [[$addrinfo['address'], $addrinfo['name']]],
  1893                     $this->cc,
  1921                     $this->cc,
  1894                     $this->bcc,
  1922                     $this->bcc,
  2133                     if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) {
  2161                     if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) {
  2134                         $tls = true;
  2162                         $tls = true;
  2135                     }
  2163                     }
  2136                     if ($tls) {
  2164                     if ($tls) {
  2137                         if (!$this->smtp->startTLS()) {
  2165                         if (!$this->smtp->startTLS()) {
  2138                             throw new Exception($this->lang('connect_host'));
  2166                             $message = $this->getSmtpErrorMessage('connect_host');
       
  2167                             throw new Exception($message);
  2139                         }
  2168                         }
  2140                         //We must resend EHLO after TLS negotiation
  2169                         //We must resend EHLO after TLS negotiation
  2141                         $this->smtp->hello($hello);
  2170                         $this->smtp->hello($hello);
  2142                     }
  2171                     }
  2143                     if (
  2172                     if (
  2163         //If we get here, all connection attempts have failed, so close connection hard
  2192         //If we get here, all connection attempts have failed, so close connection hard
  2164         $this->smtp->close();
  2193         $this->smtp->close();
  2165         //As we've caught all exceptions, just report whatever the last one was
  2194         //As we've caught all exceptions, just report whatever the last one was
  2166         if ($this->exceptions && null !== $lastexception) {
  2195         if ($this->exceptions && null !== $lastexception) {
  2167             throw $lastexception;
  2196             throw $lastexception;
       
  2197         } elseif ($this->exceptions) {
       
  2198             // no exception was thrown, likely $this->smtp->connect() failed
       
  2199             $message = $this->getSmtpErrorMessage('connect_host');
       
  2200             throw new Exception($message);
  2168         }
  2201         }
  2169 
  2202 
  2170         return false;
  2203         return false;
  2171     }
  2204     }
  2172 
  2205 
  2181         }
  2214         }
  2182     }
  2215     }
  2183 
  2216 
  2184     /**
  2217     /**
  2185      * Set the language for error messages.
  2218      * Set the language for error messages.
  2186      * Returns false if it cannot load the language file.
       
  2187      * The default language is English.
  2219      * The default language is English.
  2188      *
  2220      *
  2189      * @param string $langcode  ISO 639-1 2-character language code (e.g. French is "fr")
  2221      * @param string $langcode  ISO 639-1 2-character language code (e.g. French is "fr")
  2190      * @param string $lang_path Path to the language file directory, with trailing separator (slash).D
  2222      *                          Optionally, the language code can be enhanced with a 4-character
       
  2223      *                          script annotation and/or a 2-character country annotation.
       
  2224      * @param string $lang_path Path to the language file directory, with trailing separator (slash)
  2191      *                          Do not set this from user input!
  2225      *                          Do not set this from user input!
  2192      *
  2226      *
  2193      * @return bool
  2227      * @return bool Returns true if the requested language was loaded, false otherwise.
  2194      */
  2228      */
  2195     public function setLanguage($langcode = 'en', $lang_path = '')
  2229     public function setLanguage($langcode = 'en', $lang_path = '')
  2196     {
  2230     {
  2197         //Backwards compatibility for renamed language codes
  2231         //Backwards compatibility for renamed language codes
  2198         $renamed_langcodes = [
  2232         $renamed_langcodes = [
  2211         }
  2245         }
  2212 
  2246 
  2213         //Define full set of translatable strings in English
  2247         //Define full set of translatable strings in English
  2214         $PHPMAILER_LANG = [
  2248         $PHPMAILER_LANG = [
  2215             'authenticate' => 'SMTP Error: Could not authenticate.',
  2249             'authenticate' => 'SMTP Error: Could not authenticate.',
       
  2250             'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' .
       
  2251                 ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' .
       
  2252                 ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.',
  2216             'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
  2253             'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
  2217             'data_not_accepted' => 'SMTP Error: data not accepted.',
  2254             'data_not_accepted' => 'SMTP Error: data not accepted.',
  2218             'empty_message' => 'Message body empty',
  2255             'empty_message' => 'Message body empty',
  2219             'encoding' => 'Unknown encoding: ',
  2256             'encoding' => 'Unknown encoding: ',
  2220             'execute' => 'Could not execute: ',
  2257             'execute' => 'Could not execute: ',
       
  2258             'extension_missing' => 'Extension missing: ',
  2221             'file_access' => 'Could not access file: ',
  2259             'file_access' => 'Could not access file: ',
  2222             'file_open' => 'File Error: Could not open file: ',
  2260             'file_open' => 'File Error: Could not open file: ',
  2223             'from_failed' => 'The following From address failed: ',
  2261             'from_failed' => 'The following From address failed: ',
  2224             'instantiate' => 'Could not instantiate mail function.',
  2262             'instantiate' => 'Could not instantiate mail function.',
  2225             'invalid_address' => 'Invalid address: ',
  2263             'invalid_address' => 'Invalid address: ',
       
  2264             'invalid_header' => 'Invalid header name or value',
  2226             'invalid_hostentry' => 'Invalid hostentry: ',
  2265             'invalid_hostentry' => 'Invalid hostentry: ',
  2227             'invalid_host' => 'Invalid host: ',
  2266             'invalid_host' => 'Invalid host: ',
  2228             'mailer_not_supported' => ' mailer is not supported.',
  2267             'mailer_not_supported' => ' mailer is not supported.',
  2229             'provide_address' => 'You must provide at least one recipient email address.',
  2268             'provide_address' => 'You must provide at least one recipient email address.',
  2230             'recipients_failed' => 'SMTP Error: The following recipients failed: ',
  2269             'recipients_failed' => 'SMTP Error: The following recipients failed: ',
  2231             'signing' => 'Signing Error: ',
  2270             'signing' => 'Signing Error: ',
       
  2271             'smtp_code' => 'SMTP code: ',
       
  2272             'smtp_code_ex' => 'Additional SMTP info: ',
  2232             'smtp_connect_failed' => 'SMTP connect() failed.',
  2273             'smtp_connect_failed' => 'SMTP connect() failed.',
       
  2274             'smtp_detail' => 'Detail: ',
  2233             'smtp_error' => 'SMTP server error: ',
  2275             'smtp_error' => 'SMTP server error: ',
  2234             'variable_set' => 'Cannot set or reset variable: ',
  2276             'variable_set' => 'Cannot set or reset variable: ',
  2235             'extension_missing' => 'Extension missing: ',
       
  2236         ];
  2277         ];
  2237         if (empty($lang_path)) {
  2278         if (empty($lang_path)) {
  2238             //Calculate an absolute path so it can work if CWD is not here
  2279             //Calculate an absolute path so it can work if CWD is not here
  2239             $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
  2280             $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
  2240         }
  2281         }
       
  2282 
  2241         //Validate $langcode
  2283         //Validate $langcode
  2242         if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
  2284         $foundlang = true;
       
  2285         $langcode  = strtolower($langcode);
       
  2286         if (
       
  2287             !preg_match('/^(?P<lang>[a-z]{2})(?P<script>_[a-z]{4})?(?P<country>_[a-z]{2})?$/', $langcode, $matches)
       
  2288             && $langcode !== 'en'
       
  2289         ) {
       
  2290             $foundlang = false;
  2243             $langcode = 'en';
  2291             $langcode = 'en';
  2244         }
  2292         }
  2245         $foundlang = true;
  2293 
  2246         $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
       
  2247         //There is no English translation file
  2294         //There is no English translation file
  2248         if ('en' !== $langcode) {
  2295         if ('en' !== $langcode) {
  2249             //Make sure language file path is readable
  2296             $langcodes = [];
  2250             if (!static::fileIsAccessible($lang_file)) {
  2297             if (!empty($matches['script']) && !empty($matches['country'])) {
       
  2298                 $langcodes[] = $matches['lang'] . $matches['script'] . $matches['country'];
       
  2299             }
       
  2300             if (!empty($matches['country'])) {
       
  2301                 $langcodes[] = $matches['lang'] . $matches['country'];
       
  2302             }
       
  2303             if (!empty($matches['script'])) {
       
  2304                 $langcodes[] = $matches['lang'] . $matches['script'];
       
  2305             }
       
  2306             $langcodes[] = $matches['lang'];
       
  2307 
       
  2308             //Try and find a readable language file for the requested language.
       
  2309             $foundFile = false;
       
  2310             foreach ($langcodes as $code) {
       
  2311                 $lang_file = $lang_path . 'phpmailer.lang-' . $code . '.php';
       
  2312                 if (static::fileIsAccessible($lang_file)) {
       
  2313                     $foundFile = true;
       
  2314                     break;
       
  2315                 }
       
  2316             }
       
  2317 
       
  2318             if ($foundFile === false) {
  2251                 $foundlang = false;
  2319                 $foundlang = false;
  2252             } else {
  2320             } else {
  2253                 //$foundlang = include $lang_file;
       
  2254                 $lines = file($lang_file);
  2321                 $lines = file($lang_file);
  2255                 foreach ($lines as $line) {
  2322                 foreach ($lines as $line) {
  2256                     //Translation file lines look like this:
  2323                     //Translation file lines look like this:
  2257                     //$PHPMAILER_LANG['authenticate'] = 'SMTP-Fehler: Authentifizierung fehlgeschlagen.';
  2324                     //$PHPMAILER_LANG['authenticate'] = 'SMTP-Fehler: Authentifizierung fehlgeschlagen.';
  2258                     //These files are parsed as text and not PHP so as to avoid the possibility of code injection
  2325                     //These files are parsed as text and not PHP so as to avoid the possibility of code injection
  2283      *
  2350      *
  2284      * @return array
  2351      * @return array
  2285      */
  2352      */
  2286     public function getTranslations()
  2353     public function getTranslations()
  2287     {
  2354     {
       
  2355         if (empty($this->language)) {
       
  2356             $this->setLanguage(); // Set the default language.
       
  2357         }
       
  2358 
  2288         return $this->language;
  2359         return $this->language;
  2289     }
  2360     }
  2290 
  2361 
  2291     /**
  2362     /**
  2292      * Create recipient headers.
  2363      * Create recipient headers.
  2551             $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
  2622             $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
  2552         }
  2623         }
  2553 
  2624 
  2554         //Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
  2625         //Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
  2555         //https://tools.ietf.org/html/rfc5322#section-3.6.4
  2626         //https://tools.ietf.org/html/rfc5322#section-3.6.4
  2556         if ('' !== $this->MessageID && preg_match('/^<.*@.*>$/', $this->MessageID)) {
  2627         if (
       
  2628             '' !== $this->MessageID &&
       
  2629             preg_match(
       
  2630                 '/^<((([a-z\d!#$%&\'*+\/=?^_`{|}~-]+(\.[a-z\d!#$%&\'*+\/=?^_`{|}~-]+)*)' .
       
  2631                 '|("(([\x01-\x08\x0B\x0C\x0E-\x1F\x7F]|[\x21\x23-\x5B\x5D-\x7E])' .
       
  2632                 '|(\\[\x01-\x09\x0B\x0C\x0E-\x7F]))*"))@(([a-z\d!#$%&\'*+\/=?^_`{|}~-]+' .
       
  2633                 '(\.[a-z\d!#$%&\'*+\/=?^_`{|}~-]+)*)|(\[(([\x01-\x08\x0B\x0C\x0E-\x1F\x7F]' .
       
  2634                 '|[\x21-\x5A\x5E-\x7E])|(\\[\x01-\x09\x0B\x0C\x0E-\x7F]))*\])))>$/Di',
       
  2635                 $this->MessageID
       
  2636             )
       
  2637         ) {
  2557             $this->lastMessageID = $this->MessageID;
  2638             $this->lastMessageID = $this->MessageID;
  2558         } else {
  2639         } else {
  2559             $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
  2640             $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
  2560         }
  2641         }
  2561         $result .= $this->headerLine('Message-ID', $this->lastMessageID);
  2642         $result .= $this->headerLine('Message-ID', $this->lastMessageID);
  2562         if (null !== $this->Priority) {
  2643         if (null !== $this->Priority) {
  2563             $result .= $this->headerLine('X-Priority', $this->Priority);
  2644             $result .= $this->headerLine('X-Priority', $this->Priority);
  2564         }
  2645         }
  2565         if ('' === $this->XMailer) {
  2646         if ('' === $this->XMailer) {
       
  2647             //Empty string for default X-Mailer header
  2566             $result .= $this->headerLine(
  2648             $result .= $this->headerLine(
  2567                 'X-Mailer',
  2649                 'X-Mailer',
  2568                 'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)'
  2650                 'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)'
  2569             );
  2651             );
  2570         } else {
  2652         } elseif (is_string($this->XMailer) && trim($this->XMailer) !== '') {
  2571             $myXmailer = trim($this->XMailer);
  2653             //Some string
  2572             if ($myXmailer) {
  2654             $result .= $this->headerLine('X-Mailer', trim($this->XMailer));
  2573                 $result .= $this->headerLine('X-Mailer', $myXmailer);
  2655         } //Other values result in no X-Mailer header
  2574             }
       
  2575         }
       
  2576 
  2656 
  2577         if ('' !== $this->ConfirmReadingTo) {
  2657         if ('' !== $this->ConfirmReadingTo) {
  2578             $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
  2658             $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
  2579         }
  2659         }
  2580 
  2660 
  3935         if ('smtp' === $this->Mailer && null !== $this->smtp) {
  4015         if ('smtp' === $this->Mailer && null !== $this->smtp) {
  3936             $lasterror = $this->smtp->getError();
  4016             $lasterror = $this->smtp->getError();
  3937             if (!empty($lasterror['error'])) {
  4017             if (!empty($lasterror['error'])) {
  3938                 $msg .= $this->lang('smtp_error') . $lasterror['error'];
  4018                 $msg .= $this->lang('smtp_error') . $lasterror['error'];
  3939                 if (!empty($lasterror['detail'])) {
  4019                 if (!empty($lasterror['detail'])) {
  3940                     $msg .= ' Detail: ' . $lasterror['detail'];
  4020                     $msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detail'];
  3941                 }
  4021                 }
  3942                 if (!empty($lasterror['smtp_code'])) {
  4022                 if (!empty($lasterror['smtp_code'])) {
  3943                     $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
  4023                     $msg .= ' ' . $this->lang('smtp_code') . $lasterror['smtp_code'];
  3944                 }
  4024                 }
  3945                 if (!empty($lasterror['smtp_code_ex'])) {
  4025                 if (!empty($lasterror['smtp_code_ex'])) {
  3946                     $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
  4026                     $msg .= ' ' . $this->lang('smtp_code_ex') . $lasterror['smtp_code_ex'];
  3947                 }
  4027                 }
  3948             }
  4028             }
  3949         }
  4029         }
  3950         $this->ErrorInfo = $msg;
  4030         $this->ErrorInfo = $msg;
  3951     }
  4031     }
  4002         //Simple syntax limits
  4082         //Simple syntax limits
  4003         if (
  4083         if (
  4004             empty($host)
  4084             empty($host)
  4005             || !is_string($host)
  4085             || !is_string($host)
  4006             || strlen($host) > 256
  4086             || strlen($host) > 256
  4007             || !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+])$/', $host)
  4087             || !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+\])$/', $host)
  4008         ) {
  4088         ) {
  4009             return false;
  4089             return false;
  4010         }
  4090         }
  4011         //Looks like a bracketed IPv6 address
  4091         //Looks like a bracketed IPv6 address
  4012         if (strlen($host) > 2 && substr($host, 0, 1) === '[' && substr($host, -1, 1) === ']') {
  4092         if (strlen($host) > 2 && substr($host, 0, 1) === '[' && substr($host, -1, 1) === ']') {
  4053         //Return the key as a fallback
  4133         //Return the key as a fallback
  4054         return $key;
  4134         return $key;
  4055     }
  4135     }
  4056 
  4136 
  4057     /**
  4137     /**
       
  4138      * Build an error message starting with a generic one and adding details if possible.
       
  4139      *
       
  4140      * @param string $base_key
       
  4141      * @return string
       
  4142      */
       
  4143     private function getSmtpErrorMessage($base_key)
       
  4144     {
       
  4145         $message = $this->lang($base_key);
       
  4146         $error = $this->smtp->getError();
       
  4147         if (!empty($error['error'])) {
       
  4148             $message .= ' ' . $error['error'];
       
  4149             if (!empty($error['detail'])) {
       
  4150                 $message .= ' ' . $error['detail'];
       
  4151             }
       
  4152         }
       
  4153 
       
  4154         return $message;
       
  4155     }
       
  4156 
       
  4157     /**
  4058      * Check if an error occurred.
  4158      * Check if an error occurred.
  4059      *
  4159      *
  4060      * @return bool True if an error did occur
  4160      * @return bool True if an error did occur
  4061      */
  4161      */
  4062     public function isError()
  4162     public function isError()
  4079         if (null === $value && strpos($name, ':') !== false) {
  4179         if (null === $value && strpos($name, ':') !== false) {
  4080             //Value passed in as name:value
  4180             //Value passed in as name:value
  4081             list($name, $value) = explode(':', $name, 2);
  4181             list($name, $value) = explode(':', $name, 2);
  4082         }
  4182         }
  4083         $name = trim($name);
  4183         $name = trim($name);
  4084         $value = trim($value);
  4184         $value = (null === $value) ? '' : trim($value);
  4085         //Ensure name is not empty, and that neither name nor value contain line breaks
  4185         //Ensure name is not empty, and that neither name nor value contain line breaks
  4086         if (empty($name) || strpbrk($name . $value, "\r\n") !== false) {
  4186         if (empty($name) || strpbrk($name . $value, "\r\n") !== false) {
  4087             if ($this->exceptions) {
  4187             if ($this->exceptions) {
  4088                 throw new Exception('Invalid header name or value');
  4188                 throw new Exception($this->lang('invalid_header'));
  4089             }
  4189             }
  4090 
  4190 
  4091             return false;
  4191             return false;
  4092         }
  4192         }
  4093         $this->CustomHeader[] = [$name, $value];
  4193         $this->CustomHeader[] = [$name, $value];
  4237      * });
  4337      * });
  4238      * ```
  4338      * ```
  4239      *
  4339      *
  4240      * @param string        $html     The HTML text to convert
  4340      * @param string        $html     The HTML text to convert
  4241      * @param bool|callable $advanced Any boolean value to use the internal converter,
  4341      * @param bool|callable $advanced Any boolean value to use the internal converter,
  4242      *                                or provide your own callable for custom conversion
  4342      *                                or provide your own callable for custom conversion.
       
  4343      *                                *Never* pass user-supplied data into this parameter
  4243      *
  4344      *
  4244      * @return string
  4345      * @return string
  4245      */
  4346      */
  4246     public function html2text($html, $advanced = false)
  4347     public function html2text($html, $advanced = false)
  4247     {
  4348     {
  4951             call_user_func($this->action_function, $isSent, $to, $cc, $bcc, $subject, $body, $from, $extra);
  5052             call_user_func($this->action_function, $isSent, $to, $cc, $bcc, $subject, $body, $from, $extra);
  4952         }
  5053         }
  4953     }
  5054     }
  4954 
  5055 
  4955     /**
  5056     /**
  4956      * Get the OAuth instance.
  5057      * Get the OAuthTokenProvider instance.
  4957      *
  5058      *
  4958      * @return OAuth
  5059      * @return OAuthTokenProvider
  4959      */
  5060      */
  4960     public function getOAuth()
  5061     public function getOAuth()
  4961     {
  5062     {
  4962         return $this->oauth;
  5063         return $this->oauth;
  4963     }
  5064     }
  4964 
  5065 
  4965     /**
  5066     /**
  4966      * Set an OAuth instance.
  5067      * Set an OAuthTokenProvider instance.
  4967      */
  5068      */
  4968     public function setOAuth(OAuth $oauth)
  5069     public function setOAuth(OAuthTokenProvider $oauth)
  4969     {
  5070     {
  4970         $this->oauth = $oauth;
  5071         $this->oauth = $oauth;
  4971     }
  5072     }
  4972 }
  5073 }