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 |
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', |
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 * |