wp/wp-includes/PHPMailer/PHPMailer.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
   348      * @var string
   348      * @var string
   349      */
   349      */
   350     public $Password = '';
   350     public $Password = '';
   351 
   351 
   352     /**
   352     /**
   353      * SMTP auth type.
   353      * SMTP authentication type. Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2.
   354      * Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2, attempted in that order if not specified.
   354      * If not specified, the first one from that list that the server supports will be selected.
   355      *
   355      *
   356      * @var string
   356      * @var string
   357      */
   357      */
   358     public $AuthType = '';
   358     public $AuthType = '';
       
   359 
       
   360     /**
       
   361      * SMTP SMTPXClient command attibutes
       
   362      *
       
   363      * @var array
       
   364      */
       
   365     protected $SMTPXClient = [];
   359 
   366 
   360     /**
   367     /**
   361      * An implementation of the PHPMailer OAuthTokenProvider interface.
   368      * An implementation of the PHPMailer OAuthTokenProvider interface.
   362      *
   369      *
   363      * @var OAuthTokenProvider
   370      * @var OAuthTokenProvider
   748     /**
   755     /**
   749      * The PHPMailer Version number.
   756      * The PHPMailer Version number.
   750      *
   757      *
   751      * @var string
   758      * @var string
   752      */
   759      */
   753     const VERSION = '6.6.0';
   760     const VERSION = '6.9.1';
   754 
   761 
   755     /**
   762     /**
   756      * Error severity: message only, continue processing.
   763      * Error severity: message only, continue processing.
   757      *
   764      *
   758      * @var int
   765      * @var int
   793 
   800 
   794     /**
   801     /**
   795      * The maximum line length supported by mail().
   802      * The maximum line length supported by mail().
   796      *
   803      *
   797      * Background: mail() will sometimes corrupt messages
   804      * Background: mail() will sometimes corrupt messages
   798      * with headers headers longer than 65 chars, see #818.
   805      * with headers longer than 65 chars, see #818.
   799      *
   806      *
   800      * @var int
   807      * @var int
   801      */
   808      */
   802     const MAIL_MAX_LINE_LENGTH = 63;
   809     const MAIL_MAX_LINE_LENGTH = 63;
   803 
   810 
   856      * @return bool
   863      * @return bool
   857      */
   864      */
   858     private function mailPassthru($to, $subject, $body, $header, $params)
   865     private function mailPassthru($to, $subject, $body, $header, $params)
   859     {
   866     {
   860         //Check overloading of mail function to avoid double-encoding
   867         //Check overloading of mail function to avoid double-encoding
   861         if (ini_get('mbstring.func_overload') & 1) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated
   868         if ((int)ini_get('mbstring.func_overload') & 1) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated
   862             $subject = $this->secureHeader($subject);
   869             $subject = $this->secureHeader($subject);
   863         } else {
   870         } else {
   864             $subject = $this->encodeHeader($this->secureHeader($subject));
   871             $subject = $this->encodeHeader($this->secureHeader($subject));
   865         }
   872         }
   866         //Calling mail() with null params breaks
   873         //Calling mail() with null params breaks
  1064      * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
  1071      * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
  1065      * be modified after calling this function), addition of such addresses is delayed until send().
  1072      * be modified after calling this function), addition of such addresses is delayed until send().
  1066      * Addresses that have been added already return false, but do not throw exceptions.
  1073      * Addresses that have been added already return false, but do not throw exceptions.
  1067      *
  1074      *
  1068      * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
  1075      * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
  1069      * @param string $address The email address to send, resp. to reply to
  1076      * @param string $address The email address
  1070      * @param string $name
  1077      * @param string $name    An optional username associated with the address
  1071      *
  1078      *
  1072      * @throws Exception
  1079      * @throws Exception
  1073      *
  1080      *
  1074      * @return bool true on success, false if address already used or invalid in some way
  1081      * @return bool true on success, false if address already used or invalid in some way
  1075      */
  1082      */
  1076     protected function addOrEnqueueAnAddress($kind, $address, $name)
  1083     protected function addOrEnqueueAnAddress($kind, $address, $name)
  1077     {
  1084     {
  1078         $address = trim($address);
  1085         $pos = false;
  1079         $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
  1086         if ($address !== null) {
  1080         $pos = strrpos($address, '@');
  1087             $address = trim($address);
       
  1088             $pos = strrpos($address, '@');
       
  1089         }
  1081         if (false === $pos) {
  1090         if (false === $pos) {
  1082             //At-sign is missing.
  1091             //At-sign is missing.
  1083             $error_message = sprintf(
  1092             $error_message = sprintf(
  1084                 '%s (%s): %s',
  1093                 '%s (%s): %s',
  1085                 $this->lang('invalid_address'),
  1094                 $this->lang('invalid_address'),
  1092                 throw new Exception($error_message);
  1101                 throw new Exception($error_message);
  1093             }
  1102             }
  1094 
  1103 
  1095             return false;
  1104             return false;
  1096         }
  1105         }
       
  1106         if ($name !== null && is_string($name)) {
       
  1107             $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
       
  1108         } else {
       
  1109             $name = '';
       
  1110         }
  1097         $params = [$kind, $address, $name];
  1111         $params = [$kind, $address, $name];
  1098         //Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
  1112         //Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
       
  1113         //Domain is assumed to be whatever is after the last @ symbol in the address
  1099         if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) {
  1114         if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) {
  1100             if ('Reply-To' !== $kind) {
  1115             if ('Reply-To' !== $kind) {
  1101                 if (!array_key_exists($address, $this->RecipientsQueue)) {
  1116                 if (!array_key_exists($address, $this->RecipientsQueue)) {
  1102                     $this->RecipientsQueue[$address] = $params;
  1117                     $this->RecipientsQueue[$address] = $params;
  1103 
  1118 
  1112             return false;
  1127             return false;
  1113         }
  1128         }
  1114 
  1129 
  1115         //Immediately add standard addresses without IDN.
  1130         //Immediately add standard addresses without IDN.
  1116         return call_user_func_array([$this, 'addAnAddress'], $params);
  1131         return call_user_func_array([$this, 'addAnAddress'], $params);
       
  1132     }
       
  1133 
       
  1134     /**
       
  1135      * Set the boundaries to use for delimiting MIME parts.
       
  1136      * If you override this, ensure you set all 3 boundaries to unique values.
       
  1137      * The default boundaries include a "=_" sequence which cannot occur in quoted-printable bodies,
       
  1138      * as suggested by https://www.rfc-editor.org/rfc/rfc2045#section-6.7
       
  1139      *
       
  1140      * @return void
       
  1141      */
       
  1142     public function setBoundaries()
       
  1143     {
       
  1144         $this->uniqueid = $this->generateId();
       
  1145         $this->boundary[1] = 'b1=_' . $this->uniqueid;
       
  1146         $this->boundary[2] = 'b2=_' . $this->uniqueid;
       
  1147         $this->boundary[3] = 'b3=_' . $this->uniqueid;
  1117     }
  1148     }
  1118 
  1149 
  1119     /**
  1150     /**
  1120      * Add an address to one of the recipient arrays or to the ReplyTo array.
  1151      * Add an address to one of the recipient arrays or to the ReplyTo array.
  1121      * Addresses that have been added already return false, but do not throw exceptions.
  1152      * Addresses that have been added already return false, but do not throw exceptions.
  1278      *
  1309      *
  1279      * @return bool
  1310      * @return bool
  1280      */
  1311      */
  1281     public function setFrom($address, $name = '', $auto = true)
  1312     public function setFrom($address, $name = '', $auto = true)
  1282     {
  1313     {
  1283         $address = trim($address);
  1314         $address = trim((string)$address);
  1284         $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
  1315         $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
  1285         //Don't validate now addresses with IDN. Will be done in send().
  1316         //Don't validate now addresses with IDN. Will be done in send().
  1286         $pos = strrpos($address, '@');
  1317         $pos = strrpos($address, '@');
  1287         if (
  1318         if (
  1288             (false === $pos)
  1319             (false === $pos)
  1547                 throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
  1578                 throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
  1548             }
  1579             }
  1549 
  1580 
  1550             //Validate From, Sender, and ConfirmReadingTo addresses
  1581             //Validate From, Sender, and ConfirmReadingTo addresses
  1551             foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
  1582             foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
  1552                 $this->$address_kind = trim($this->$address_kind);
  1583                 if ($this->{$address_kind} === null) {
  1553                 if (empty($this->$address_kind)) {
  1584                     $this->{$address_kind} = '';
  1554                     continue;
  1585                     continue;
  1555                 }
  1586                 }
  1556                 $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
  1587                 $this->{$address_kind} = trim($this->{$address_kind});
  1557                 if (!static::validateAddress($this->$address_kind)) {
  1588                 if (empty($this->{$address_kind})) {
       
  1589                     continue;
       
  1590                 }
       
  1591                 $this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind});
       
  1592                 if (!static::validateAddress($this->{$address_kind})) {
  1558                     $error_message = sprintf(
  1593                     $error_message = sprintf(
  1559                         '%s (%s): %s',
  1594                         '%s (%s): %s',
  1560                         $this->lang('invalid_address'),
  1595                         $this->lang('invalid_address'),
  1561                         $address_kind,
  1596                         $address_kind,
  1562                         $this->$address_kind
  1597                         $this->{$address_kind}
  1563                     );
  1598                     );
  1564                     $this->setError($error_message);
  1599                     $this->setError($error_message);
  1565                     $this->edebug($error_message);
  1600                     $this->edebug($error_message);
  1566                     if ($this->exceptions) {
  1601                     if ($this->exceptions) {
  1567                         throw new Exception($error_message);
  1602                         throw new Exception($error_message);
  1657                 case 'mail':
  1692                 case 'mail':
  1658                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
  1693                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
  1659                 default:
  1694                 default:
  1660                     $sendMethod = $this->Mailer . 'Send';
  1695                     $sendMethod = $this->Mailer . 'Send';
  1661                     if (method_exists($this, $sendMethod)) {
  1696                     if (method_exists($this, $sendMethod)) {
  1662                         return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
  1697                         return $this->{$sendMethod}($this->MIMEHeader, $this->MIMEBody);
  1663                     }
  1698                     }
  1664 
  1699 
  1665                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
  1700                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
  1666             }
  1701             }
  1667         } catch (Exception $exc) {
  1702         } catch (Exception $exc) {
  1668             if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true) {
       
  1669                 $this->smtp->reset();
       
  1670             }
       
  1671             $this->setError($exc->getMessage());
  1703             $this->setError($exc->getMessage());
  1672             $this->edebug($exc->getMessage());
  1704             $this->edebug($exc->getMessage());
       
  1705             if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true && $this->smtp->connected()) {
       
  1706                 $this->smtp->reset();
       
  1707             }
  1673             if ($this->exceptions) {
  1708             if ($this->exceptions) {
  1674                 throw $exc;
  1709                 throw $exc;
  1675             }
  1710             }
  1676         }
  1711         }
  1677 
  1712 
  1855     protected static function fileIsAccessible($path)
  1890     protected static function fileIsAccessible($path)
  1856     {
  1891     {
  1857         if (!static::isPermittedPath($path)) {
  1892         if (!static::isPermittedPath($path)) {
  1858             return false;
  1893             return false;
  1859         }
  1894         }
  1860         $readable = file_exists($path);
  1895         $readable = is_file($path);
  1861         //If not a UNC path (expected to start with \\), check read permission, see #2069
  1896         //If not a UNC path (expected to start with \\), check read permission, see #2069
  1862         if (strpos($path, '\\\\') !== 0) {
  1897         if (strpos($path, '\\\\') !== 0) {
  1863             $readable = $readable && is_readable($path);
  1898             $readable = $readable && is_readable($path);
  1864         }
  1899         }
  1865         return  $readable;
  1900         return  $readable;
  1883 
  1918 
  1884         $toArr = [];
  1919         $toArr = [];
  1885         foreach ($this->to as $toaddr) {
  1920         foreach ($this->to as $toaddr) {
  1886             $toArr[] = $this->addrFormat($toaddr);
  1921             $toArr[] = $this->addrFormat($toaddr);
  1887         }
  1922         }
  1888         $to = implode(', ', $toArr);
  1923         $to = trim(implode(', ', $toArr));
       
  1924 
       
  1925         //If there are no To-addresses (e.g. when sending only to BCC-addresses)
       
  1926         //the following should be added to get a correct DKIM-signature.
       
  1927         //Compare with $this->preSend()
       
  1928         if ($to === '') {
       
  1929             $to = 'undisclosed-recipients:;';
       
  1930         }
  1889 
  1931 
  1890         $params = null;
  1932         $params = null;
  1891         //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
  1933         //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
  1892         //A space after `-f` is optional, but there is a long history of its presence
  1934         //A space after `-f` is optional, but there is a long history of its presence
  1893         //causing problems, so we don't use one
  1935         //causing problems, so we don't use one
  1967 
  2009 
  1968         return $this->smtp;
  2010         return $this->smtp;
  1969     }
  2011     }
  1970 
  2012 
  1971     /**
  2013     /**
       
  2014      * Provide SMTP XCLIENT attributes
       
  2015      *
       
  2016      * @param string $name  Attribute name
       
  2017      * @param ?string $value Attribute value
       
  2018      *
       
  2019      * @return bool
       
  2020      */
       
  2021     public function setSMTPXclientAttribute($name, $value)
       
  2022     {
       
  2023         if (!in_array($name, SMTP::$xclient_allowed_attributes)) {
       
  2024             return false;
       
  2025         }
       
  2026         if (isset($this->SMTPXClient[$name]) && $value === null) {
       
  2027             unset($this->SMTPXClient[$name]);
       
  2028         } elseif ($value !== null) {
       
  2029             $this->SMTPXClient[$name] = $value;
       
  2030         }
       
  2031 
       
  2032         return true;
       
  2033     }
       
  2034 
       
  2035     /**
       
  2036      * Get SMTP XCLIENT attributes
       
  2037      *
       
  2038      * @return array
       
  2039      */
       
  2040     public function getSMTPXclientAttributes()
       
  2041     {
       
  2042         return $this->SMTPXClient;
       
  2043     }
       
  2044 
       
  2045     /**
  1972      * Send mail via SMTP.
  2046      * Send mail via SMTP.
  1973      * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
  2047      * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
  1974      *
  2048      *
  1975      * @see PHPMailer::setSMTPInstance() to use a different class.
  2049      * @see PHPMailer::setSMTPInstance() to use a different class.
  1976      *
  2050      *
  1993         //Sender already validated in preSend()
  2067         //Sender already validated in preSend()
  1994         if ('' === $this->Sender) {
  2068         if ('' === $this->Sender) {
  1995             $smtp_from = $this->From;
  2069             $smtp_from = $this->From;
  1996         } else {
  2070         } else {
  1997             $smtp_from = $this->Sender;
  2071             $smtp_from = $this->Sender;
       
  2072         }
       
  2073         if (count($this->SMTPXClient)) {
       
  2074             $this->smtp->xclient($this->SMTPXClient);
  1998         }
  2075         }
  1999         if (!$this->smtp->mail($smtp_from)) {
  2076         if (!$this->smtp->mail($smtp_from)) {
  2000             $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
  2077             $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
  2001             throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
  2078             throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
  2002         }
  2079         }
  2086 
  2163 
  2087         $this->smtp->setTimeout($this->Timeout);
  2164         $this->smtp->setTimeout($this->Timeout);
  2088         $this->smtp->setDebugLevel($this->SMTPDebug);
  2165         $this->smtp->setDebugLevel($this->SMTPDebug);
  2089         $this->smtp->setDebugOutput($this->Debugoutput);
  2166         $this->smtp->setDebugOutput($this->Debugoutput);
  2090         $this->smtp->setVerp($this->do_verp);
  2167         $this->smtp->setVerp($this->do_verp);
       
  2168         if ($this->Host === null) {
       
  2169             $this->Host = 'localhost';
       
  2170         }
  2091         $hosts = explode(';', $this->Host);
  2171         $hosts = explode(';', $this->Host);
  2092         $lastexception = null;
  2172         $lastexception = null;
  2093 
  2173 
  2094         foreach ($hosts as $hostentry) {
  2174         foreach ($hosts as $hostentry) {
  2095             $hostinfo = [];
  2175             $hostinfo = [];
  2153                         $hello = $this->serverHostname();
  2233                         $hello = $this->serverHostname();
  2154                     }
  2234                     }
  2155                     $this->smtp->hello($hello);
  2235                     $this->smtp->hello($hello);
  2156                     //Automatically enable TLS encryption if:
  2236                     //Automatically enable TLS encryption if:
  2157                     //* it's not disabled
  2237                     //* it's not disabled
       
  2238                     //* we are not connecting to localhost
  2158                     //* we have openssl extension
  2239                     //* we have openssl extension
  2159                     //* we are not already using SSL
  2240                     //* we are not already using SSL
  2160                     //* the server offers STARTTLS
  2241                     //* the server offers STARTTLS
  2161                     if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) {
  2242                     if (
       
  2243                         $this->SMTPAutoTLS &&
       
  2244                         $this->Host !== 'localhost' &&
       
  2245                         $sslext &&
       
  2246                         $secure !== 'ssl' &&
       
  2247                         $this->smtp->getServerExt('STARTTLS')
       
  2248                     ) {
  2162                         $tls = true;
  2249                         $tls = true;
  2163                     }
  2250                     }
  2164                     if ($tls) {
  2251                     if ($tls) {
  2165                         if (!$this->smtp->startTLS()) {
  2252                         if (!$this->smtp->startTLS()) {
  2166                             $message = $this->getSmtpErrorMessage('connect_host');
  2253                             $message = $this->getSmtpErrorMessage('connect_host');
  2192         //If we get here, all connection attempts have failed, so close connection hard
  2279         //If we get here, all connection attempts have failed, so close connection hard
  2193         $this->smtp->close();
  2280         $this->smtp->close();
  2194         //As we've caught all exceptions, just report whatever the last one was
  2281         //As we've caught all exceptions, just report whatever the last one was
  2195         if ($this->exceptions && null !== $lastexception) {
  2282         if ($this->exceptions && null !== $lastexception) {
  2196             throw $lastexception;
  2283             throw $lastexception;
  2197         } elseif ($this->exceptions) {
  2284         }
       
  2285         if ($this->exceptions) {
  2198             // no exception was thrown, likely $this->smtp->connect() failed
  2286             // no exception was thrown, likely $this->smtp->connect() failed
  2199             $message = $this->getSmtpErrorMessage('connect_host');
  2287             $message = $this->getSmtpErrorMessage('connect_host');
  2200             throw new Exception($message);
  2288             throw new Exception($message);
  2201         }
  2289         }
  2202 
  2290 
  2388      *
  2476      *
  2389      * @return string
  2477      * @return string
  2390      */
  2478      */
  2391     public function addrFormat($addr)
  2479     public function addrFormat($addr)
  2392     {
  2480     {
  2393         if (empty($addr[1])) { //No name provided
  2481         if (!isset($addr[1]) || ($addr[1] === '')) { //No name provided
  2394             return $this->secureHeader($addr[0]);
  2482             return $this->secureHeader($addr[0]);
  2395         }
  2483         }
  2396 
  2484 
  2397         return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') .
  2485         return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') .
  2398             ' <' . $this->secureHeader($addr[0]) . '>';
  2486             ' <' . $this->secureHeader($addr[0]) . '>';
  2775      */
  2863      */
  2776     public function createBody()
  2864     public function createBody()
  2777     {
  2865     {
  2778         $body = '';
  2866         $body = '';
  2779         //Create unique IDs and preset boundaries
  2867         //Create unique IDs and preset boundaries
  2780         $this->uniqueid = $this->generateId();
  2868         $this->setBoundaries();
  2781         $this->boundary[1] = 'b1_' . $this->uniqueid;
       
  2782         $this->boundary[2] = 'b2_' . $this->uniqueid;
       
  2783         $this->boundary[3] = 'b3_' . $this->uniqueid;
       
  2784 
  2869 
  2785         if ($this->sign_key_file) {
  2870         if ($this->sign_key_file) {
  2786             $body .= $this->getMailMIME() . static::$LE;
  2871             $body .= $this->getMailMIME() . static::$LE;
  2787         }
  2872         }
  2788 
  2873 
  2814         //change to quoted-printable transfer encoding for the alt body part only
  2899         //change to quoted-printable transfer encoding for the alt body part only
  2815         if (static::ENCODING_BASE64 !== $altBodyEncoding && static::hasLineLongerThanMax($this->AltBody)) {
  2900         if (static::ENCODING_BASE64 !== $altBodyEncoding && static::hasLineLongerThanMax($this->AltBody)) {
  2816             $altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE;
  2901             $altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE;
  2817         }
  2902         }
  2818         //Use this as a preamble in all multipart message types
  2903         //Use this as a preamble in all multipart message types
  2819         $mimepre = 'This is a multi-part message in MIME format.' . static::$LE . static::$LE;
  2904         $mimepre = '';
  2820         switch ($this->message_type) {
  2905         switch ($this->message_type) {
  2821             case 'inline':
  2906             case 'inline':
  2822                 $body .= $mimepre;
  2907                 $body .= $mimepre;
  2823                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
  2908                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
  2824                 $body .= $this->encodeString($this->Body, $bodyEncoding);
  2909                 $body .= $this->encodeString($this->Body, $bodyEncoding);
  3051 
  3136 
  3052         return $body;
  3137         return $body;
  3053     }
  3138     }
  3054 
  3139 
  3055     /**
  3140     /**
       
  3141      * Get the boundaries that this message will use
       
  3142      * @return array
       
  3143      */
       
  3144     public function getBoundaries()
       
  3145     {
       
  3146         if (empty($this->boundary)) {
       
  3147             $this->setBoundaries();
       
  3148         }
       
  3149         return $this->boundary;
       
  3150     }
       
  3151 
       
  3152     /**
  3056      * Return the start of a message boundary.
  3153      * Return the start of a message boundary.
  3057      *
  3154      *
  3058      * @param string $boundary
  3155      * @param string $boundary
  3059      * @param string $charSet
  3156      * @param string $charSet
  3060      * @param string $contentType
  3157      * @param string $contentType
  3705      * Add an embedded (inline) attachment from a file.
  3802      * Add an embedded (inline) attachment from a file.
  3706      * This can include images, sounds, and just about any other document type.
  3803      * This can include images, sounds, and just about any other document type.
  3707      * These differ from 'regular' attachments in that they are intended to be
  3804      * These differ from 'regular' attachments in that they are intended to be
  3708      * displayed inline with the message, not just attached for download.
  3805      * displayed inline with the message, not just attached for download.
  3709      * This is used in HTML messages that embed the images
  3806      * This is used in HTML messages that embed the images
  3710      * the HTML refers to using the $cid value.
  3807      * the HTML refers to using the `$cid` value in `img` tags, for example `<img src="cid:mylogo">`.
  3711      * Never use a user-supplied path to a file!
  3808      * Never use a user-supplied path to a file!
  3712      *
  3809      *
  3713      * @param string $path        Path to the attachment
  3810      * @param string $path        Path to the attachment
  3714      * @param string $cid         Content ID of the attachment; Use this to reference
  3811      * @param string $cid         Content ID of the attachment; Use this to reference
  3715      *                            the content when using an embedded image in HTML
  3812      *                            the content when using an embedded image in HTML
  3716      * @param string $name        Overrides the attachment name
  3813      * @param string $name        Overrides the attachment filename
  3717      * @param string $encoding    File encoding (see $Encoding)
  3814      * @param string $encoding    File encoding (see $Encoding) defaults to `base64`
  3718      * @param string $type        File MIME type
  3815      * @param string $type        File MIME type (by default mapped from the `$path` filename's extension)
  3719      * @param string $disposition Disposition to use
  3816      * @param string $disposition Disposition to use: `inline` (default) or `attachment`
  3720      *
  3817      *                            (unlikely you want this – {@see `addAttachment()`} instead)
       
  3818      *
       
  3819      * @return bool True on successfully adding an attachment
  3721      * @throws Exception
  3820      * @throws Exception
  3722      *
  3821      *
  3723      * @return bool True on successfully adding an attachment
       
  3724      */
  3822      */
  3725     public function addEmbeddedImage(
  3823     public function addEmbeddedImage(
  3726         $path,
  3824         $path,
  3727         $cid,
  3825         $cid,
  3728         $name = '',
  3826         $name = '',
  4003     {
  4101     {
  4004         $this->CustomHeader = [];
  4102         $this->CustomHeader = [];
  4005     }
  4103     }
  4006 
  4104 
  4007     /**
  4105     /**
       
  4106      * Clear a specific custom header by name or name and value.
       
  4107      * $name value can be overloaded to contain
       
  4108      * both header name and value (name:value).
       
  4109      *
       
  4110      * @param string      $name  Custom header name
       
  4111      * @param string|null $value Header value
       
  4112      *
       
  4113      * @return bool True if a header was replaced successfully
       
  4114      */
       
  4115     public function clearCustomHeader($name, $value = null)
       
  4116     {
       
  4117         if (null === $value && strpos($name, ':') !== false) {
       
  4118             //Value passed in as name:value
       
  4119             list($name, $value) = explode(':', $name, 2);
       
  4120         }
       
  4121         $name = trim($name);
       
  4122         $value = (null === $value) ? null : trim($value);
       
  4123 
       
  4124         foreach ($this->CustomHeader as $k => $pair) {
       
  4125             if ($pair[0] == $name) {
       
  4126                 // We remove the header if the value is not provided or it matches.
       
  4127                 if (null === $value ||  $pair[1] == $value) {
       
  4128                     unset($this->CustomHeader[$k]);
       
  4129                 }
       
  4130             }
       
  4131         }
       
  4132 
       
  4133         return true;
       
  4134     }
       
  4135 
       
  4136     /**
       
  4137      * Replace a custom header.
       
  4138      * $name value can be overloaded to contain
       
  4139      * both header name and value (name:value).
       
  4140      *
       
  4141      * @param string      $name  Custom header name
       
  4142      * @param string|null $value Header value
       
  4143      *
       
  4144      * @return bool True if a header was replaced successfully
       
  4145      * @throws Exception
       
  4146      */
       
  4147     public function replaceCustomHeader($name, $value = null)
       
  4148     {
       
  4149         if (null === $value && strpos($name, ':') !== false) {
       
  4150             //Value passed in as name:value
       
  4151             list($name, $value) = explode(':', $name, 2);
       
  4152         }
       
  4153         $name = trim($name);
       
  4154         $value = (null === $value) ? '' : trim($value);
       
  4155 
       
  4156         $replaced = false;
       
  4157         foreach ($this->CustomHeader as $k => $pair) {
       
  4158             if ($pair[0] == $name) {
       
  4159                 if ($replaced) {
       
  4160                     unset($this->CustomHeader[$k]);
       
  4161                     continue;
       
  4162                 }
       
  4163                 if (strpbrk($name . $value, "\r\n") !== false) {
       
  4164                     if ($this->exceptions) {
       
  4165                         throw new Exception($this->lang('invalid_header'));
       
  4166                     }
       
  4167 
       
  4168                     return false;
       
  4169                 }
       
  4170                 $this->CustomHeader[$k] = [$name, $value];
       
  4171                 $replaced = true;
       
  4172             }
       
  4173         }
       
  4174 
       
  4175         return true;
       
  4176     }
       
  4177 
       
  4178     /**
  4008      * Add an error message to the error container.
  4179      * Add an error message to the error container.
  4009      *
  4180      *
  4010      * @param string $msg
  4181      * @param string $msg
  4011      */
  4182      */
  4012     protected function setError($msg)
  4183     protected function setError($msg)
  4096         //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names
  4267         //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names
  4097         if (is_numeric(str_replace('.', '', $host))) {
  4268         if (is_numeric(str_replace('.', '', $host))) {
  4098             //Is it a valid IPv4 address?
  4269             //Is it a valid IPv4 address?
  4099             return filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
  4270             return filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
  4100         }
  4271         }
  4101         if (filter_var('http://' . $host, FILTER_VALIDATE_URL) !== false) {
  4272         //Is it a syntactically valid hostname (when embeded in a URL)?
  4102             //Is it a syntactically valid hostname?
  4273         return filter_var('http://' . $host, FILTER_VALIDATE_URL) !== false;
  4103             return true;
       
  4104         }
       
  4105 
       
  4106         return false;
       
  4107     }
  4274     }
  4108 
  4275 
  4109     /**
  4276     /**
  4110      * Get an error message in the current language.
  4277      * Get an error message in the current language.
  4111      *
  4278      *
  4170      * both header name and value (name:value).
  4337      * both header name and value (name:value).
  4171      *
  4338      *
  4172      * @param string      $name  Custom header name
  4339      * @param string      $name  Custom header name
  4173      * @param string|null $value Header value
  4340      * @param string|null $value Header value
  4174      *
  4341      *
       
  4342      * @return bool True if a header was set successfully
  4175      * @throws Exception
  4343      * @throws Exception
  4176      */
  4344      */
  4177     public function addCustomHeader($name, $value = null)
  4345     public function addCustomHeader($name, $value = null)
  4178     {
  4346     {
  4179         if (null === $value && strpos($name, ':') !== false) {
  4347         if (null === $value && strpos($name, ':') !== false) {
  4464             'vcf' => 'text/vcard',
  4632             'vcf' => 'text/vcard',
  4465             'vcard' => 'text/vcard',
  4633             'vcard' => 'text/vcard',
  4466             'ics' => 'text/calendar',
  4634             'ics' => 'text/calendar',
  4467             'xml' => 'text/xml',
  4635             'xml' => 'text/xml',
  4468             'xsl' => 'text/xml',
  4636             'xsl' => 'text/xml',
       
  4637             'csv' => 'text/csv',
  4469             'wmv' => 'video/x-ms-wmv',
  4638             'wmv' => 'video/x-ms-wmv',
  4470             'mpeg' => 'video/mpeg',
  4639             'mpeg' => 'video/mpeg',
  4471             'mpe' => 'video/mpeg',
  4640             'mpe' => 'video/mpeg',
  4472             'mpg' => 'video/mpeg',
  4641             'mpg' => 'video/mpeg',
  4473             'mp4' => 'video/mp4',
  4642             'mp4' => 'video/mp4',
  4571      * @return bool
  4740      * @return bool
  4572      */
  4741      */
  4573     public function set($name, $value = '')
  4742     public function set($name, $value = '')
  4574     {
  4743     {
  4575         if (property_exists($this, $name)) {
  4744         if (property_exists($this, $name)) {
  4576             $this->$name = $value;
  4745             $this->{$name} = $value;
  4577 
  4746 
  4578             return true;
  4747             return true;
  4579         }
  4748         }
  4580         $this->setError($this->lang('variable_set') . $name);
  4749         $this->setError($this->lang('variable_set') . $name);
  4581 
  4750 
  4618 
  4787 
  4619         return $text;
  4788         return $text;
  4620     }
  4789     }
  4621 
  4790 
  4622     /**
  4791     /**
  4623      * Remove trailing breaks from a string.
  4792      * Remove trailing whitespace from a string.
  4624      *
  4793      *
  4625      * @param string $text
  4794      * @param string $text
  4626      *
  4795      *
       
  4796      * @return string The text to remove whitespace from
       
  4797      */
       
  4798     public static function stripTrailingWSP($text)
       
  4799     {
       
  4800         return rtrim($text, " \r\n\t");
       
  4801     }
       
  4802 
       
  4803     /**
       
  4804      * Strip trailing line breaks from a string.
       
  4805      *
       
  4806      * @param string $text
       
  4807      *
  4627      * @return string The text to remove breaks from
  4808      * @return string The text to remove breaks from
  4628      */
  4809      */
  4629     public static function stripTrailingWSP($text)
  4810     public static function stripTrailingBreaks($text)
  4630     {
  4811     {
  4631         return rtrim($text, " \r\n\t");
  4812         return rtrim($text, "\r\n");
  4632     }
  4813     }
  4633 
  4814 
  4634     /**
  4815     /**
  4635      * Return the current line break format string.
  4816      * Return the current line break format string.
  4636      *
  4817      *
  4792         }
  4973         }
  4793         //Normalize line endings to CRLF
  4974         //Normalize line endings to CRLF
  4794         $body = static::normalizeBreaks($body, self::CRLF);
  4975         $body = static::normalizeBreaks($body, self::CRLF);
  4795 
  4976 
  4796         //Reduce multiple trailing line breaks to a single one
  4977         //Reduce multiple trailing line breaks to a single one
  4797         return static::stripTrailingWSP($body) . self::CRLF;
  4978         return static::stripTrailingBreaks($body) . self::CRLF;
  4798     }
  4979     }
  4799 
  4980 
  4800     /**
  4981     /**
  4801      * Create the DKIM header and body in a new message header.
  4982      * Create the DKIM header and body in a new message header.
  4802      *
  4983      *