695 | | //Can't use additional_parameters in safe_mode, calling mail() with null params breaks |
696 | | //@link http://php.net/manual/en/function.mail.php |
697 | | if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { |
698 | | $result = @mail($to, $subject, $body, $header); |
699 | | } else { |
700 | | $result = @mail($to, $subject, $body, $header, $params); |
701 | | } |
702 | | return $result; |
703 | | } |
704 | | /** |
705 | | * Output debugging info via user-defined method. |
706 | | * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). |
707 | | * @see PHPMailer::$Debugoutput |
708 | | * @see PHPMailer::$SMTPDebug |
709 | | * @param string $str |
710 | | */ |
711 | | protected function edebug($str) |
712 | | { |
713 | | if ($this->SMTPDebug <= 0) { |
714 | | return; |
715 | | } |
716 | | //Avoid clash with built-in function names |
717 | | if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { |
718 | | call_user_func($this->Debugoutput, $str, $this->SMTPDebug); |
719 | | return; |
720 | | } |
721 | | switch ($this->Debugoutput) { |
722 | | case 'error_log': |
723 | | //Don't output, just log |
724 | | error_log($str); |
725 | | break; |
726 | | case 'html': |
727 | | //Cleans up output a bit for a better looking, HTML-safe output |
728 | | echo htmlentities( |
729 | | preg_replace('/[\r\n]+/', '', $str), |
730 | | ENT_QUOTES, |
731 | | 'UTF-8' |
732 | | ) |
733 | | . "<br>\n"; |
734 | | break; |
735 | | case 'echo': |
736 | | default: |
737 | | //Normalize line breaks |
738 | | $str = preg_replace('/\r\n?/ms', "\n", $str); |
739 | | echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( |
740 | | "\n", |
741 | | "\n \t ", |
742 | | trim($str) |
743 | | ) . "\n"; |
744 | | } |
745 | | } |
| 697 | //Can't use additional_parameters in safe_mode, calling mail() with null params breaks |
| 698 | //@link http://php.net/manual/en/function.mail.php |
| 699 | if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { |
| 700 | $result = @mail($to, $subject, $body, $header); |
| 701 | } else { |
| 702 | $result = @mail($to, $subject, $body, $header, $params); |
| 703 | } |
| 704 | return $result; |
| 705 | } |
| 706 | /** |
| 707 | * Output debugging info via user-defined method. |
| 708 | * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). |
| 709 | * @see PHPMailer::$Debugoutput |
| 710 | * @see PHPMailer::$SMTPDebug |
| 711 | * @param string $str |
| 712 | */ |
| 713 | protected function edebug($str) |
| 714 | { |
| 715 | if ($this->SMTPDebug <= 0) { |
| 716 | return; |
| 717 | } |
| 718 | //Avoid clash with built-in function names |
| 719 | if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { |
| 720 | call_user_func($this->Debugoutput, $str, $this->SMTPDebug); |
| 721 | return; |
| 722 | } |
| 723 | switch ($this->Debugoutput) { |
| 724 | case 'error_log': |
| 725 | //Don't output, just log |
| 726 | error_log($str); |
| 727 | break; |
| 728 | case 'html': |
| 729 | //Cleans up output a bit for a better looking, HTML-safe output |
| 730 | echo htmlentities( |
| 731 | preg_replace('/[\r\n]+/', '', $str), |
| 732 | ENT_QUOTES, |
| 733 | 'UTF-8' |
| 734 | ) |
| 735 | . "<br>\n"; |
| 736 | break; |
| 737 | case 'echo': |
| 738 | default: |
| 739 | //Normalize line breaks |
| 740 | $str = preg_replace('/\r\n?/ms', "\n", $str); |
| 741 | echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( |
| 742 | "\n", |
| 743 | "\n \t ", |
| 744 | trim($str) |
| 745 | ) . "\n"; |
| 746 | } |
| 747 | } |
857 | | /** |
858 | | * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer |
859 | | * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still |
860 | | * be modified after calling this function), addition of such addresses is delayed until send(). |
861 | | * Addresses that have been added already return false, but do not throw exceptions. |
862 | | * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' |
863 | | * @param string $address The email address to send, resp. to reply to |
864 | | * @param string $name |
865 | | * @throws phpmailerException |
866 | | * @return boolean true on success, false if address already used or invalid in some way |
867 | | * @access protected |
868 | | */ |
869 | | protected function addOrEnqueueAnAddress($kind, $address, $name) |
870 | | { |
871 | | $address = trim($address); |
872 | | $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim |
873 | | if (($pos = strrpos($address, '@')) === false) { |
874 | | // At-sign is misssing. |
875 | | $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; |
876 | | $this->setError($error_message); |
877 | | $this->edebug($error_message); |
878 | | if ($this->exceptions) { |
879 | | throw new phpmailerException($error_message); |
880 | | } |
881 | | return false; |
882 | | } |
883 | | $params = array($kind, $address, $name); |
884 | | // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. |
885 | | if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) { |
886 | | if ($kind != 'Reply-To') { |
887 | | if (!array_key_exists($address, $this->RecipientsQueue)) { |
888 | | $this->RecipientsQueue[$address] = $params; |
889 | | return true; |
890 | | } |
891 | | } else { |
892 | | if (!array_key_exists($address, $this->ReplyToQueue)) { |
893 | | $this->ReplyToQueue[$address] = $params; |
894 | | return true; |
895 | | } |
896 | | } |
897 | | return false; |
898 | | } |
899 | | // Immediately add standard addresses without IDN. |
900 | | return call_user_func_array(array($this, 'addAnAddress'), $params); |
901 | | } |
| 859 | /** |
| 860 | * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer |
| 861 | * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still |
| 862 | * be modified after calling this function), addition of such addresses is delayed until send(). |
| 863 | * Addresses that have been added already return false, but do not throw exceptions. |
| 864 | * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' |
| 865 | * @param string $address The email address to send, resp. to reply to |
| 866 | * @param string $name |
| 867 | * @throws phpmailerException |
| 868 | * @return boolean true on success, false if address already used or invalid in some way |
| 869 | * @access protected |
| 870 | */ |
| 871 | protected function addOrEnqueueAnAddress($kind, $address, $name) |
| 872 | { |
| 873 | $address = trim($address); |
| 874 | $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim |
| 875 | if (($pos = strrpos($address, '@')) === false) { |
| 876 | // At-sign is misssing. |
| 877 | $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; |
| 878 | $this->setError($error_message); |
| 879 | $this->edebug($error_message); |
| 880 | if ($this->exceptions) { |
| 881 | throw new phpmailerException($error_message); |
| 882 | } |
| 883 | return false; |
| 884 | } |
| 885 | $params = array($kind, $address, $name); |
| 886 | // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. |
| 887 | if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) { |
| 888 | if ($kind != 'Reply-To') { |
| 889 | if (!array_key_exists($address, $this->RecipientsQueue)) { |
| 890 | $this->RecipientsQueue[$address] = $params; |
| 891 | return true; |
| 892 | } |
| 893 | } else { |
| 894 | if (!array_key_exists($address, $this->ReplyToQueue)) { |
| 895 | $this->ReplyToQueue[$address] = $params; |
| 896 | return true; |
| 897 | } |
| 898 | } |
| 899 | return false; |
| 900 | } |
| 901 | // Immediately add standard addresses without IDN. |
| 902 | return call_user_func_array(array($this, 'addAnAddress'), $params); |
| 903 | } |
903 | | /** |
904 | | * Add an address to one of the recipient arrays or to the ReplyTo array. |
905 | | * Addresses that have been added already return false, but do not throw exceptions. |
906 | | * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' |
907 | | * @param string $address The email address to send, resp. to reply to |
908 | | * @param string $name |
909 | | * @throws phpmailerException |
910 | | * @return boolean true on success, false if address already used or invalid in some way |
911 | | * @access protected |
912 | | */ |
913 | | protected function addAnAddress($kind, $address, $name = '') |
914 | | { |
915 | | if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { |
916 | | $error_message = $this->lang('Invalid recipient kind: ') . $kind; |
917 | | $this->setError($error_message); |
918 | | $this->edebug($error_message); |
919 | | if ($this->exceptions) { |
920 | | throw new phpmailerException($error_message); |
921 | | } |
922 | | return false; |
923 | | } |
924 | | if (!$this->validateAddress($address)) { |
925 | | $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; |
926 | | $this->setError($error_message); |
927 | | $this->edebug($error_message); |
928 | | if ($this->exceptions) { |
929 | | throw new phpmailerException($error_message); |
930 | | } |
931 | | return false; |
932 | | } |
933 | | if ($kind != 'Reply-To') { |
934 | | if (!array_key_exists(strtolower($address), $this->all_recipients)) { |
935 | | array_push($this->$kind, array($address, $name)); |
936 | | $this->all_recipients[strtolower($address)] = true; |
937 | | return true; |
938 | | } |
939 | | } else { |
940 | | if (!array_key_exists(strtolower($address), $this->ReplyTo)) { |
941 | | $this->ReplyTo[strtolower($address)] = array($address, $name); |
942 | | return true; |
943 | | } |
944 | | } |
945 | | return false; |
946 | | } |
| 905 | /** |
| 906 | * Add an address to one of the recipient arrays or to the ReplyTo array. |
| 907 | * Addresses that have been added already return false, but do not throw exceptions. |
| 908 | * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' |
| 909 | * @param string $address The email address to send, resp. to reply to |
| 910 | * @param string $name |
| 911 | * @throws phpmailerException |
| 912 | * @return boolean true on success, false if address already used or invalid in some way |
| 913 | * @access protected |
| 914 | */ |
| 915 | protected function addAnAddress($kind, $address, $name = '') |
| 916 | { |
| 917 | if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { |
| 918 | $error_message = $this->lang('Invalid recipient kind: ') . $kind; |
| 919 | $this->setError($error_message); |
| 920 | $this->edebug($error_message); |
| 921 | if ($this->exceptions) { |
| 922 | throw new phpmailerException($error_message); |
| 923 | } |
| 924 | return false; |
| 925 | } |
| 926 | if (!$this->validateAddress($address)) { |
| 927 | $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; |
| 928 | $this->setError($error_message); |
| 929 | $this->edebug($error_message); |
| 930 | if ($this->exceptions) { |
| 931 | throw new phpmailerException($error_message); |
| 932 | } |
| 933 | return false; |
| 934 | } |
| 935 | if ($kind != 'Reply-To') { |
| 936 | if (!array_key_exists(strtolower($address), $this->all_recipients)) { |
| 937 | array_push($this->$kind, array($address, $name)); |
| 938 | $this->all_recipients[strtolower($address)] = true; |
| 939 | return true; |
| 940 | } |
| 941 | } else { |
| 942 | if (!array_key_exists(strtolower($address), $this->ReplyTo)) { |
| 943 | $this->ReplyTo[strtolower($address)] = array($address, $name); |
| 944 | return true; |
| 945 | } |
| 946 | } |
| 947 | return false; |
| 948 | } |
948 | | /** |
949 | | * Parse and validate a string containing one or more RFC822-style comma-separated email addresses |
950 | | * of the form "display name <address>" into an array of name/address pairs. |
951 | | * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. |
952 | | * Note that quotes in the name part are removed. |
953 | | * @param string $addrstr The address list string |
954 | | * @param bool $useimap Whether to use the IMAP extension to parse the list |
955 | | * @return array |
956 | | * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation |
957 | | */ |
958 | | public function parseAddresses($addrstr, $useimap = true) |
959 | | { |
960 | | $addresses = array(); |
961 | | if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { |
962 | | //Use this built-in parser if it's available |
963 | | $list = imap_rfc822_parse_adrlist($addrstr, ''); |
964 | | foreach ($list as $address) { |
965 | | if ($address->host != '.SYNTAX-ERROR.') { |
966 | | if ($this->validateAddress($address->mailbox . '@' . $address->host)) { |
967 | | $addresses[] = array( |
968 | | 'name' => (property_exists($address, 'personal') ? $address->personal : ''), |
969 | | 'address' => $address->mailbox . '@' . $address->host |
970 | | ); |
971 | | } |
972 | | } |
973 | | } |
974 | | } else { |
975 | | //Use this simpler parser |
976 | | $list = explode(',', $addrstr); |
977 | | foreach ($list as $address) { |
978 | | $address = trim($address); |
979 | | //Is there a separate name part? |
980 | | if (strpos($address, '<') === false) { |
981 | | //No separate name, just use the whole thing |
982 | | if ($this->validateAddress($address)) { |
983 | | $addresses[] = array( |
984 | | 'name' => '', |
985 | | 'address' => $address |
986 | | ); |
987 | | } |
988 | | } else { |
989 | | list($name, $email) = explode('<', $address); |
990 | | $email = trim(str_replace('>', '', $email)); |
991 | | if ($this->validateAddress($email)) { |
992 | | $addresses[] = array( |
993 | | 'name' => trim(str_replace(array('"', "'"), '', $name)), |
994 | | 'address' => $email |
995 | | ); |
996 | | } |
997 | | } |
998 | | } |
999 | | } |
1000 | | return $addresses; |
1001 | | } |
| 950 | /** |
| 951 | * Parse and validate a string containing one or more RFC822-style comma-separated email addresses |
| 952 | * of the form "display name <address>" into an array of name/address pairs. |
| 953 | * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. |
| 954 | * Note that quotes in the name part are removed. |
| 955 | * @param string $addrstr The address list string |
| 956 | * @param bool $useimap Whether to use the IMAP extension to parse the list |
| 957 | * @return array |
| 958 | * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation |
| 959 | */ |
| 960 | public function parseAddresses($addrstr, $useimap = true) |
| 961 | { |
| 962 | $addresses = array(); |
| 963 | if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { |
| 964 | //Use this built-in parser if it's available |
| 965 | $list = imap_rfc822_parse_adrlist($addrstr, ''); |
| 966 | foreach ($list as $address) { |
| 967 | if ($address->host != '.SYNTAX-ERROR.') { |
| 968 | if ($this->validateAddress($address->mailbox . '@' . $address->host)) { |
| 969 | $addresses[] = array( |
| 970 | 'name' => (property_exists($address, 'personal') ? $address->personal : ''), |
| 971 | 'address' => $address->mailbox . '@' . $address->host |
| 972 | ); |
| 973 | } |
| 974 | } |
| 975 | } |
| 976 | } else { |
| 977 | //Use this simpler parser |
| 978 | $list = explode(',', $addrstr); |
| 979 | foreach ($list as $address) { |
| 980 | $address = trim($address); |
| 981 | //Is there a separate name part? |
| 982 | if (strpos($address, '<') === false) { |
| 983 | //No separate name, just use the whole thing |
| 984 | if ($this->validateAddress($address)) { |
| 985 | $addresses[] = array( |
| 986 | 'name' => '', |
| 987 | 'address' => $address |
| 988 | ); |
| 989 | } |
| 990 | } else { |
| 991 | list($name, $email) = explode('<', $address); |
| 992 | $email = trim(str_replace('>', '', $email)); |
| 993 | if ($this->validateAddress($email)) { |
| 994 | $addresses[] = array( |
| 995 | 'name' => trim(str_replace(array('"', "'"), '', $name)), |
| 996 | 'address' => $email |
| 997 | ); |
| 998 | } |
| 999 | } |
| 1000 | } |
| 1001 | } |
| 1002 | return $addresses; |
| 1003 | } |
1049 | | /** |
1050 | | * Check that a string looks like an email address. |
1051 | | * @param string $address The email address to check |
1052 | | * @param string|callable $patternselect A selector for the validation pattern to use : |
1053 | | * * `auto` Pick best pattern automatically; |
1054 | | * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; |
1055 | | * * `pcre` Use old PCRE implementation; |
1056 | | * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; |
1057 | | * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. |
1058 | | * * `noregex` Don't use a regex: super fast, really dumb. |
1059 | | * Alternatively you may pass in a callable to inject your own validator, for example: |
1060 | | * PHPMailer::validateAddress('user@example.com', function($address) { |
1061 | | * return (strpos($address, '@') !== false); |
1062 | | * }); |
1063 | | * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. |
1064 | | * @return boolean |
1065 | | * @static |
1066 | | * @access public |
1067 | | */ |
1068 | | public static function validateAddress($address, $patternselect = null) |
1069 | | { |
1070 | | if (is_null($patternselect)) { |
1071 | | $patternselect = self::$validator; |
1072 | | } |
1073 | | if (is_callable($patternselect)) { |
1074 | | return call_user_func($patternselect, $address); |
1075 | | } |
1076 | | //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 |
1077 | | if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { |
1078 | | return false; |
1079 | | } |
1080 | | if (!$patternselect or $patternselect == 'auto') { |
1081 | | //Check this constant first so it works when extension_loaded() is disabled by safe mode |
1082 | | //Constant was added in PHP 5.2.4 |
1083 | | if (defined('PCRE_VERSION')) { |
1084 | | //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 |
1085 | | if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { |
1086 | | $patternselect = 'pcre8'; |
1087 | | } else { |
1088 | | $patternselect = 'pcre'; |
1089 | | } |
1090 | | } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { |
1091 | | //Fall back to older PCRE |
1092 | | $patternselect = 'pcre'; |
1093 | | } else { |
1094 | | //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension |
1095 | | if (version_compare(PHP_VERSION, '5.2.0') >= 0) { |
1096 | | $patternselect = 'php'; |
1097 | | } else { |
1098 | | $patternselect = 'noregex'; |
1099 | | } |
1100 | | } |
1101 | | } |
1102 | | switch ($patternselect) { |
1103 | | case 'pcre8': |
1104 | | /** |
1105 | | * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. |
1106 | | * @link http://squiloople.com/2009/12/20/email-address-validation/ |
1107 | | * @copyright 2009-2010 Michael Rushton |
1108 | | * Feel free to use and redistribute this code. But please keep this copyright notice. |
1109 | | */ |
1110 | | return (boolean)preg_match( |
1111 | | '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . |
1112 | | '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . |
1113 | | '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . |
1114 | | '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . |
1115 | | '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . |
1116 | | '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . |
1117 | | '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . |
1118 | | '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . |
1119 | | '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', |
1120 | | $address |
1121 | | ); |
1122 | | case 'pcre': |
1123 | | //An older regex that doesn't need a recent PCRE |
1124 | | return (boolean)preg_match( |
1125 | | '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . |
1126 | | '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . |
1127 | | '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . |
1128 | | '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . |
1129 | | '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . |
1130 | | '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . |
1131 | | '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . |
1132 | | '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . |
1133 | | '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . |
1134 | | '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', |
1135 | | $address |
1136 | | ); |
1137 | | case 'html5': |
1138 | | /** |
1139 | | * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. |
1140 | | * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) |
1141 | | */ |
1142 | | return (boolean)preg_match( |
1143 | | '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . |
1144 | | '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', |
1145 | | $address |
1146 | | ); |
1147 | | case 'noregex': |
1148 | | //No PCRE! Do something _very_ approximate! |
1149 | | //Check the address is 3 chars or longer and contains an @ that's not the first or last char |
1150 | | return (strlen($address) >= 3 |
1151 | | and strpos($address, '@') >= 1 |
1152 | | and strpos($address, '@') != strlen($address) - 1); |
1153 | | case 'php': |
1154 | | default: |
1155 | | return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); |
1156 | | } |
1157 | | } |
| 1051 | /** |
| 1052 | * Check that a string looks like an email address. |
| 1053 | * @param string $address The email address to check |
| 1054 | * @param string|callable $patternselect A selector for the validation pattern to use : |
| 1055 | * * `auto` Pick best pattern automatically; |
| 1056 | * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; |
| 1057 | * * `pcre` Use old PCRE implementation; |
| 1058 | * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; |
| 1059 | * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. |
| 1060 | * * `noregex` Don't use a regex: super fast, really dumb. |
| 1061 | * Alternatively you may pass in a callable to inject your own validator, for example: |
| 1062 | * PHPMailer::validateAddress('user@example.com', function($address) { |
| 1063 | * return (strpos($address, '@') !== false); |
| 1064 | * }); |
| 1065 | * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. |
| 1066 | * @return boolean |
| 1067 | * @static |
| 1068 | * @access public |
| 1069 | */ |
| 1070 | public static function validateAddress($address, $patternselect = null) |
| 1071 | { |
| 1072 | if (is_null($patternselect)) { |
| 1073 | $patternselect = self::$validator; |
| 1074 | } |
| 1075 | if (is_callable($patternselect)) { |
| 1076 | return call_user_func($patternselect, $address); |
| 1077 | } |
| 1078 | //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 |
| 1079 | if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { |
| 1080 | return false; |
| 1081 | } |
| 1082 | if (!$patternselect or $patternselect == 'auto') { |
| 1083 | //Check this constant first so it works when extension_loaded() is disabled by safe mode |
| 1084 | //Constant was added in PHP 5.2.4 |
| 1085 | if (defined('PCRE_VERSION')) { |
| 1086 | //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 |
| 1087 | if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { |
| 1088 | $patternselect = 'pcre8'; |
| 1089 | } else { |
| 1090 | $patternselect = 'pcre'; |
| 1091 | } |
| 1092 | } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { |
| 1093 | //Fall back to older PCRE |
| 1094 | $patternselect = 'pcre'; |
| 1095 | } else { |
| 1096 | //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension |
| 1097 | if (version_compare(PHP_VERSION, '5.2.0') >= 0) { |
| 1098 | $patternselect = 'php'; |
| 1099 | } else { |
| 1100 | $patternselect = 'noregex'; |
| 1101 | } |
| 1102 | } |
| 1103 | } |
| 1104 | switch ($patternselect) { |
| 1105 | case 'pcre8': |
| 1106 | /** |
| 1107 | * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. |
| 1108 | * @link http://squiloople.com/2009/12/20/email-address-validation/ |
| 1109 | * @copyright 2009-2010 Michael Rushton |
| 1110 | * Feel free to use and redistribute this code. But please keep this copyright notice. |
| 1111 | */ |
| 1112 | return (boolean)preg_match( |
| 1113 | '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . |
| 1114 | '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . |
| 1115 | '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . |
| 1116 | '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . |
| 1117 | '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . |
| 1118 | '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . |
| 1119 | '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . |
| 1120 | '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . |
| 1121 | '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', |
| 1122 | $address |
| 1123 | ); |
| 1124 | case 'pcre': |
| 1125 | //An older regex that doesn't need a recent PCRE |
| 1126 | return (boolean)preg_match( |
| 1127 | '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . |
| 1128 | '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . |
| 1129 | '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . |
| 1130 | '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . |
| 1131 | '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . |
| 1132 | '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . |
| 1133 | '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . |
| 1134 | '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . |
| 1135 | '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . |
| 1136 | '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', |
| 1137 | $address |
| 1138 | ); |
| 1139 | case 'html5': |
| 1140 | /** |
| 1141 | * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. |
| 1142 | * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) |
| 1143 | */ |
| 1144 | return (boolean)preg_match( |
| 1145 | '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . |
| 1146 | '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', |
| 1147 | $address |
| 1148 | ); |
| 1149 | case 'noregex': |
| 1150 | //No PCRE! Do something _very_ approximate! |
| 1151 | //Check the address is 3 chars or longer and contains an @ that's not the first or last char |
| 1152 | return (strlen($address) >= 3 |
| 1153 | and strpos($address, '@') >= 1 |
| 1154 | and strpos($address, '@') != strlen($address) - 1); |
| 1155 | case 'php': |
| 1156 | default: |
| 1157 | return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); |
| 1158 | } |
| 1159 | } |
1170 | | /** |
1171 | | * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. |
1172 | | * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. |
1173 | | * This function silently returns unmodified address if: |
1174 | | * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) |
1175 | | * - Conversion to punycode is impossible (e.g. required PHP functions are not available) |
1176 | | * or fails for any reason (e.g. domain has characters not allowed in an IDN) |
1177 | | * @see PHPMailer::$CharSet |
1178 | | * @param string $address The email address to convert |
1179 | | * @return string The encoded address in ASCII form |
1180 | | */ |
1181 | | public function punyencodeAddress($address) |
1182 | | { |
1183 | | // Verify we have required functions, CharSet, and at-sign. |
1184 | | if ($this->idnSupported() and |
1185 | | !empty($this->CharSet) and |
1186 | | ($pos = strrpos($address, '@')) !== false) { |
1187 | | $domain = substr($address, ++$pos); |
1188 | | // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. |
1189 | | if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { |
1190 | | $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); |
1191 | | if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? |
1192 | | idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : |
1193 | | idn_to_ascii($domain)) !== false) { |
1194 | | return substr($address, 0, $pos) . $punycode; |
1195 | | } |
1196 | | } |
1197 | | } |
1198 | | return $address; |
1199 | | } |
| 1172 | /** |
| 1173 | * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. |
| 1174 | * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. |
| 1175 | * This function silently returns unmodified address if: |
| 1176 | * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) |
| 1177 | * - Conversion to punycode is impossible (e.g. required PHP functions are not available) |
| 1178 | * or fails for any reason (e.g. domain has characters not allowed in an IDN) |
| 1179 | * @see PHPMailer::$CharSet |
| 1180 | * @param string $address The email address to convert |
| 1181 | * @return string The encoded address in ASCII form |
| 1182 | */ |
| 1183 | public function punyencodeAddress($address) |
| 1184 | { |
| 1185 | // Verify we have required functions, CharSet, and at-sign. |
| 1186 | if ($this->idnSupported() and |
| 1187 | !empty($this->CharSet) and |
| 1188 | ($pos = strrpos($address, '@')) !== false) { |
| 1189 | $domain = substr($address, ++$pos); |
| 1190 | // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. |
| 1191 | if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { |
| 1192 | $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); |
| 1193 | if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? |
| 1194 | idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : |
| 1195 | idn_to_ascii($domain)) !== false) { |
| 1196 | return substr($address, 0, $pos) . $punycode; |
| 1197 | } |
| 1198 | } |
| 1199 | } |
| 1200 | return $address; |
| 1201 | } |
1481 | | $params = null; |
1482 | | //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver |
1483 | | if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { |
1484 | | // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. |
1485 | | if (self::isShellSafe($this->Sender)) { |
1486 | | $params = sprintf('-f%s', $this->Sender); |
1487 | | } |
1488 | | } |
1489 | | if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) { |
1490 | | $old_from = ini_get('sendmail_from'); |
1491 | | ini_set('sendmail_from', $this->Sender); |
1492 | | } |
1493 | | $result = false; |
1494 | | if ($this->SingleTo and count($toArr) > 1) { |
1495 | | foreach ($toArr as $toAddr) { |
1496 | | $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); |
1497 | | $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); |
1498 | | } |
1499 | | } else { |
1500 | | $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); |
1501 | | $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); |
1502 | | } |
1503 | | if (isset($old_from)) { |
1504 | | ini_set('sendmail_from', $old_from); |
1505 | | } |
1506 | | if (!$result) { |
1507 | | throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); |
1508 | | } |
1509 | | return true; |
1510 | | } |
| 1483 | $params = null; |
| 1484 | //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver |
| 1485 | if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { |
| 1486 | // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. |
| 1487 | if (self::isShellSafe($this->Sender)) { |
| 1488 | $params = sprintf('-f%s', $this->Sender); |
| 1489 | } |
| 1490 | } |
| 1491 | if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) { |
| 1492 | $old_from = ini_get('sendmail_from'); |
| 1493 | ini_set('sendmail_from', $this->Sender); |
| 1494 | } |
| 1495 | $result = false; |
| 1496 | if ($this->SingleTo and count($toArr) > 1) { |
| 1497 | foreach ($toArr as $toAddr) { |
| 1498 | $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); |
| 1499 | $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); |
| 1500 | } |
| 1501 | } else { |
| 1502 | $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); |
| 1503 | $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); |
| 1504 | } |
| 1505 | if (isset($old_from)) { |
| 1506 | ini_set('sendmail_from', $old_from); |
| 1507 | } |
| 1508 | if (!$result) { |
| 1509 | throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); |
| 1510 | } |
| 1511 | return true; |
| 1512 | } |
1624 | | foreach ($hosts as $hostentry) { |
1625 | | $hostinfo = array(); |
1626 | | if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { |
1627 | | // Not a valid host entry |
1628 | | continue; |
1629 | | } |
1630 | | // $hostinfo[2]: optional ssl or tls prefix |
1631 | | // $hostinfo[3]: the hostname |
1632 | | // $hostinfo[4]: optional port number |
1633 | | // The host string prefix can temporarily override the current setting for SMTPSecure |
1634 | | // If it's not specified, the default value is used |
1635 | | $prefix = ''; |
1636 | | $secure = $this->SMTPSecure; |
1637 | | $tls = ($this->SMTPSecure == 'tls'); |
1638 | | if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { |
1639 | | $prefix = 'ssl://'; |
1640 | | $tls = false; // Can't have SSL and TLS at the same time |
1641 | | $secure = 'ssl'; |
1642 | | } elseif ($hostinfo[2] == 'tls') { |
1643 | | $tls = true; |
1644 | | // tls doesn't use a prefix |
1645 | | $secure = 'tls'; |
1646 | | } |
1647 | | //Do we need the OpenSSL extension? |
1648 | | $sslext = defined('OPENSSL_ALGO_SHA1'); |
1649 | | if ('tls' === $secure or 'ssl' === $secure) { |
1650 | | //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled |
1651 | | if (!$sslext) { |
1652 | | throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); |
1653 | | } |
1654 | | } |
1655 | | $host = $hostinfo[3]; |
1656 | | $port = $this->Port; |
1657 | | $tport = (integer)$hostinfo[4]; |
1658 | | if ($tport > 0 and $tport < 65536) { |
1659 | | $port = $tport; |
1660 | | } |
1661 | | if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { |
1662 | | try { |
1663 | | if ($this->Helo) { |
1664 | | $hello = $this->Helo; |
1665 | | } else { |
1666 | | $hello = $this->serverHostname(); |
1667 | | } |
1668 | | $this->smtp->hello($hello); |
1669 | | //Automatically enable TLS encryption if: |
1670 | | // * it's not disabled |
1671 | | // * we have openssl extension |
1672 | | // * we are not already using SSL |
1673 | | // * the server offers STARTTLS |
1674 | | if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { |
1675 | | $tls = true; |
1676 | | } |
1677 | | if ($tls) { |
1678 | | if (!$this->smtp->startTLS()) { |
1679 | | throw new phpmailerException($this->lang('connect_host')); |
1680 | | } |
1681 | | // We must resend EHLO after TLS negotiation |
1682 | | $this->smtp->hello($hello); |
1683 | | } |
1684 | | if ($this->SMTPAuth) { |
1685 | | if (!$this->smtp->authenticate( |
1686 | | $this->Username, |
1687 | | $this->Password, |
1688 | | $this->AuthType, |
1689 | | $this->Realm, |
1690 | | $this->Workstation |
1691 | | ) |
1692 | | ) { |
1693 | | throw new phpmailerException($this->lang('authenticate')); |
1694 | | } |
1695 | | } |
1696 | | return true; |
1697 | | } catch (phpmailerException $exc) { |
1698 | | $lastexception = $exc; |
1699 | | $this->edebug($exc->getMessage()); |
1700 | | // We must have connected, but then failed TLS or Auth, so close connection nicely |
1701 | | $this->smtp->quit(); |
1702 | | } |
1703 | | } |
1704 | | } |
1705 | | // If we get here, all connection attempts have failed, so close connection hard |
1706 | | $this->smtp->close(); |
1707 | | // As we've caught all exceptions, just report whatever the last one was |
1708 | | if ($this->exceptions and !is_null($lastexception)) { |
1709 | | throw $lastexception; |
1710 | | } |
1711 | | return false; |
1712 | | } |
| 1626 | foreach ($hosts as $hostentry) { |
| 1627 | $hostinfo = array(); |
| 1628 | if (!preg_match( |
| 1629 | '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/', |
| 1630 | trim($hostentry), |
| 1631 | $hostinfo |
| 1632 | )) { |
| 1633 | // Not a valid host entry |
| 1634 | $this->edebug('Ignoring invalid host: ' . $hostentry); |
| 1635 | continue; |
| 1636 | } |
| 1637 | // $hostinfo[2]: optional ssl or tls prefix |
| 1638 | // $hostinfo[3]: the hostname |
| 1639 | // $hostinfo[4]: optional port number |
| 1640 | // The host string prefix can temporarily override the current setting for SMTPSecure |
| 1641 | // If it's not specified, the default value is used |
| 1642 | $prefix = ''; |
| 1643 | $secure = $this->SMTPSecure; |
| 1644 | $tls = ($this->SMTPSecure == 'tls'); |
| 1645 | if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { |
| 1646 | $prefix = 'ssl://'; |
| 1647 | $tls = false; // Can't have SSL and TLS at the same time |
| 1648 | $secure = 'ssl'; |
| 1649 | } elseif ($hostinfo[2] == 'tls') { |
| 1650 | $tls = true; |
| 1651 | // tls doesn't use a prefix |
| 1652 | $secure = 'tls'; |
| 1653 | } |
| 1654 | //Do we need the OpenSSL extension? |
| 1655 | $sslext = defined('OPENSSL_ALGO_SHA1'); |
| 1656 | if ('tls' === $secure or 'ssl' === $secure) { |
| 1657 | //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled |
| 1658 | if (!$sslext) { |
| 1659 | throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); |
| 1660 | } |
| 1661 | } |
| 1662 | $host = $hostinfo[3]; |
| 1663 | $port = $this->Port; |
| 1664 | $tport = (integer)$hostinfo[4]; |
| 1665 | if ($tport > 0 and $tport < 65536) { |
| 1666 | $port = $tport; |
| 1667 | } |
| 1668 | if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { |
| 1669 | try { |
| 1670 | if ($this->Helo) { |
| 1671 | $hello = $this->Helo; |
| 1672 | } else { |
| 1673 | $hello = $this->serverHostname(); |
| 1674 | } |
| 1675 | $this->smtp->hello($hello); |
| 1676 | //Automatically enable TLS encryption if: |
| 1677 | // * it's not disabled |
| 1678 | // * we have openssl extension |
| 1679 | // * we are not already using SSL |
| 1680 | // * the server offers STARTTLS |
| 1681 | if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { |
| 1682 | $tls = true; |
| 1683 | } |
| 1684 | if ($tls) { |
| 1685 | if (!$this->smtp->startTLS()) { |
| 1686 | throw new phpmailerException($this->lang('connect_host')); |
| 1687 | } |
| 1688 | // We must resend EHLO after TLS negotiation |
| 1689 | $this->smtp->hello($hello); |
| 1690 | } |
| 1691 | if ($this->SMTPAuth) { |
| 1692 | if (!$this->smtp->authenticate( |
| 1693 | $this->Username, |
| 1694 | $this->Password, |
| 1695 | $this->AuthType, |
| 1696 | $this->Realm, |
| 1697 | $this->Workstation |
| 1698 | ) |
| 1699 | ) { |
| 1700 | throw new phpmailerException($this->lang('authenticate')); |
| 1701 | } |
| 1702 | } |
| 1703 | return true; |
| 1704 | } catch (phpmailerException $exc) { |
| 1705 | $lastexception = $exc; |
| 1706 | $this->edebug($exc->getMessage()); |
| 1707 | // We must have connected, but then failed TLS or Auth, so close connection nicely |
| 1708 | $this->smtp->quit(); |
| 1709 | } |
| 1710 | } |
| 1711 | } |
| 1712 | // If we get here, all connection attempts have failed, so close connection hard |
| 1713 | $this->smtp->close(); |
| 1714 | // As we've caught all exceptions, just report whatever the last one was |
| 1715 | if ($this->exceptions and !is_null($lastexception)) { |
| 1716 | throw $lastexception; |
| 1717 | } |
| 1718 | return false; |
| 1719 | } |
1752 | | // Define full set of translatable strings in English |
1753 | | $PHPMAILER_LANG = array( |
1754 | | 'authenticate' => 'SMTP Error: Could not authenticate.', |
1755 | | 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', |
1756 | | 'data_not_accepted' => 'SMTP Error: data not accepted.', |
1757 | | 'empty_message' => 'Message body empty', |
1758 | | 'encoding' => 'Unknown encoding: ', |
1759 | | 'execute' => 'Could not execute: ', |
1760 | | 'file_access' => 'Could not access file: ', |
1761 | | 'file_open' => 'File Error: Could not open file: ', |
1762 | | 'from_failed' => 'The following From address failed: ', |
1763 | | 'instantiate' => 'Could not instantiate mail function.', |
1764 | | 'invalid_address' => 'Invalid address: ', |
1765 | | 'mailer_not_supported' => ' mailer is not supported.', |
1766 | | 'provide_address' => 'You must provide at least one recipient email address.', |
1767 | | 'recipients_failed' => 'SMTP Error: The following recipients failed: ', |
1768 | | 'signing' => 'Signing Error: ', |
1769 | | 'smtp_connect_failed' => 'SMTP connect() failed.', |
1770 | | 'smtp_error' => 'SMTP server error: ', |
1771 | | 'variable_set' => 'Cannot set or reset variable: ', |
1772 | | 'extension_missing' => 'Extension missing: ' |
1773 | | ); |
1774 | | if (empty($lang_path)) { |
1775 | | // Calculate an absolute path so it can work if CWD is not here |
1776 | | $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; |
1777 | | } |
1778 | | //Validate $langcode |
1779 | | if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { |
1780 | | $langcode = 'en'; |
1781 | | } |
1782 | | $foundlang = true; |
1783 | | $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; |
1784 | | // There is no English translation file |
1785 | | if ($langcode != 'en') { |
1786 | | // Make sure language file path is readable |
1787 | | if (!is_readable($lang_file)) { |
1788 | | $foundlang = false; |
1789 | | } else { |
1790 | | // Overwrite language-specific strings. |
1791 | | // This way we'll never have missing translation keys. |
1792 | | $foundlang = include $lang_file; |
1793 | | } |
1794 | | } |
1795 | | $this->language = $PHPMAILER_LANG; |
1796 | | return (boolean)$foundlang; // Returns false if language not found |
1797 | | } |
| 1760 | // Define full set of translatable strings in English |
| 1761 | $PHPMAILER_LANG = array( |
| 1762 | 'authenticate' => 'SMTP Error: Could not authenticate.', |
| 1763 | 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', |
| 1764 | 'data_not_accepted' => 'SMTP Error: data not accepted.', |
| 1765 | 'empty_message' => 'Message body empty', |
| 1766 | 'encoding' => 'Unknown encoding: ', |
| 1767 | 'execute' => 'Could not execute: ', |
| 1768 | 'file_access' => 'Could not access file: ', |
| 1769 | 'file_open' => 'File Error: Could not open file: ', |
| 1770 | 'from_failed' => 'The following From address failed: ', |
| 1771 | 'instantiate' => 'Could not instantiate mail function.', |
| 1772 | 'invalid_address' => 'Invalid address: ', |
| 1773 | 'mailer_not_supported' => ' mailer is not supported.', |
| 1774 | 'provide_address' => 'You must provide at least one recipient email address.', |
| 1775 | 'recipients_failed' => 'SMTP Error: The following recipients failed: ', |
| 1776 | 'signing' => 'Signing Error: ', |
| 1777 | 'smtp_connect_failed' => 'SMTP connect() failed.', |
| 1778 | 'smtp_error' => 'SMTP server error: ', |
| 1779 | 'variable_set' => 'Cannot set or reset variable: ', |
| 1780 | 'extension_missing' => 'Extension missing: ' |
| 1781 | ); |
| 1782 | if (empty($lang_path)) { |
| 1783 | // Calculate an absolute path so it can work if CWD is not here |
| 1784 | $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; |
| 1785 | } |
| 1786 | //Validate $langcode |
| 1787 | if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { |
| 1788 | $langcode = 'en'; |
| 1789 | } |
| 1790 | $foundlang = true; |
| 1791 | $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; |
| 1792 | // There is no English translation file |
| 1793 | if ($langcode != 'en') { |
| 1794 | // Make sure language file path is readable |
| 1795 | if (!is_readable($lang_file)) { |
| 1796 | $foundlang = false; |
| 1797 | } else { |
| 1798 | // Overwrite language-specific strings. |
| 1799 | // This way we'll never have missing translation keys. |
| 1800 | $foundlang = include $lang_file; |
| 1801 | } |
| 1802 | } |
| 1803 | $this->language = $PHPMAILER_LANG; |
| 1804 | return (boolean)$foundlang; // Returns false if language not found |
| 1805 | } |
1875 | | //Split message into lines |
1876 | | $lines = explode($this->LE, $message); |
1877 | | //Message will be rebuilt in here |
1878 | | $message = ''; |
1879 | | foreach ($lines as $line) { |
1880 | | $words = explode(' ', $line); |
1881 | | $buf = ''; |
1882 | | $firstword = true; |
1883 | | foreach ($words as $word) { |
1884 | | if ($qp_mode and (strlen($word) > $length)) { |
1885 | | $space_left = $length - strlen($buf) - $crlflen; |
1886 | | if (!$firstword) { |
1887 | | if ($space_left > 20) { |
1888 | | $len = $space_left; |
1889 | | if ($is_utf8) { |
1890 | | $len = $this->utf8CharBoundary($word, $len); |
1891 | | } elseif (substr($word, $len - 1, 1) == '=') { |
1892 | | $len--; |
1893 | | } elseif (substr($word, $len - 2, 1) == '=') { |
1894 | | $len -= 2; |
1895 | | } |
1896 | | $part = substr($word, 0, $len); |
1897 | | $word = substr($word, $len); |
1898 | | $buf .= ' ' . $part; |
1899 | | $message .= $buf . sprintf('=%s', self::CRLF); |
1900 | | } else { |
1901 | | $message .= $buf . $soft_break; |
1902 | | } |
1903 | | $buf = ''; |
1904 | | } |
1905 | | while (strlen($word) > 0) { |
1906 | | if ($length <= 0) { |
1907 | | break; |
1908 | | } |
1909 | | $len = $length; |
1910 | | if ($is_utf8) { |
1911 | | $len = $this->utf8CharBoundary($word, $len); |
1912 | | } elseif (substr($word, $len - 1, 1) == '=') { |
1913 | | $len--; |
1914 | | } elseif (substr($word, $len - 2, 1) == '=') { |
1915 | | $len -= 2; |
1916 | | } |
1917 | | $part = substr($word, 0, $len); |
1918 | | $word = substr($word, $len); |
| 1883 | //Split message into lines |
| 1884 | $lines = explode($this->LE, $message); |
| 1885 | //Message will be rebuilt in here |
| 1886 | $message = ''; |
| 1887 | foreach ($lines as $line) { |
| 1888 | $words = explode(' ', $line); |
| 1889 | $buf = ''; |
| 1890 | $firstword = true; |
| 1891 | foreach ($words as $word) { |
| 1892 | if ($qp_mode and (strlen($word) > $length)) { |
| 1893 | $space_left = $length - strlen($buf) - $crlflen; |
| 1894 | if (!$firstword) { |
| 1895 | if ($space_left > 20) { |
| 1896 | $len = $space_left; |
| 1897 | if ($is_utf8) { |
| 1898 | $len = $this->utf8CharBoundary($word, $len); |
| 1899 | } elseif (substr($word, $len - 1, 1) == '=') { |
| 1900 | $len--; |
| 1901 | } elseif (substr($word, $len - 2, 1) == '=') { |
| 1902 | $len -= 2; |
| 1903 | } |
| 1904 | $part = substr($word, 0, $len); |
| 1905 | $word = substr($word, $len); |
| 1906 | $buf .= ' ' . $part; |
| 1907 | $message .= $buf . sprintf('=%s', self::CRLF); |
| 1908 | } else { |
| 1909 | $message .= $buf . $soft_break; |
| 1910 | } |
| 1911 | $buf = ''; |
| 1912 | } |
| 1913 | while (strlen($word) > 0) { |
| 1914 | if ($length <= 0) { |
| 1915 | break; |
| 1916 | } |
| 1917 | $len = $length; |
| 1918 | if ($is_utf8) { |
| 1919 | $len = $this->utf8CharBoundary($word, $len); |
| 1920 | } elseif (substr($word, $len - 1, 1) == '=') { |
| 1921 | $len--; |
| 1922 | } elseif (substr($word, $len - 2, 1) == '=') { |
| 1923 | $len -= 2; |
| 1924 | } |
| 1925 | $part = substr($word, 0, $len); |
| 1926 | $word = substr($word, $len); |
1946 | | /** |
1947 | | * Find the last character boundary prior to $maxLength in a utf-8 |
1948 | | * quoted-printable encoded string. |
1949 | | * Original written by Colin Brown. |
1950 | | * @access public |
1951 | | * @param string $encodedText utf-8 QP text |
1952 | | * @param integer $maxLength Find the last character boundary prior to this length |
1953 | | * @return integer |
1954 | | */ |
1955 | | public function utf8CharBoundary($encodedText, $maxLength) |
1956 | | { |
1957 | | $foundSplitPos = false; |
1958 | | $lookBack = 3; |
1959 | | while (!$foundSplitPos) { |
1960 | | $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); |
1961 | | $encodedCharPos = strpos($lastChunk, '='); |
1962 | | if (false !== $encodedCharPos) { |
1963 | | // Found start of encoded character byte within $lookBack block. |
1964 | | // Check the encoded byte value (the 2 chars after the '=') |
1965 | | $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); |
1966 | | $dec = hexdec($hex); |
1967 | | if ($dec < 128) { |
1968 | | // Single byte character. |
1969 | | // If the encoded char was found at pos 0, it will fit |
1970 | | // otherwise reduce maxLength to start of the encoded char |
1971 | | if ($encodedCharPos > 0) { |
1972 | | $maxLength = $maxLength - ($lookBack - $encodedCharPos); |
1973 | | } |
1974 | | $foundSplitPos = true; |
1975 | | } elseif ($dec >= 192) { |
1976 | | // First byte of a multi byte character |
1977 | | // Reduce maxLength to split at start of character |
1978 | | $maxLength = $maxLength - ($lookBack - $encodedCharPos); |
1979 | | $foundSplitPos = true; |
1980 | | } elseif ($dec < 192) { |
1981 | | // Middle byte of a multi byte character, look further back |
1982 | | $lookBack += 3; |
1983 | | } |
1984 | | } else { |
1985 | | // No encoded character found |
1986 | | $foundSplitPos = true; |
1987 | | } |
1988 | | } |
1989 | | return $maxLength; |
1990 | | } |
| 1954 | /** |
| 1955 | * Find the last character boundary prior to $maxLength in a utf-8 |
| 1956 | * quoted-printable encoded string. |
| 1957 | * Original written by Colin Brown. |
| 1958 | * @access public |
| 1959 | * @param string $encodedText utf-8 QP text |
| 1960 | * @param integer $maxLength Find the last character boundary prior to this length |
| 1961 | * @return integer |
| 1962 | */ |
| 1963 | public function utf8CharBoundary($encodedText, $maxLength) |
| 1964 | { |
| 1965 | $foundSplitPos = false; |
| 1966 | $lookBack = 3; |
| 1967 | while (!$foundSplitPos) { |
| 1968 | $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); |
| 1969 | $encodedCharPos = strpos($lastChunk, '='); |
| 1970 | if (false !== $encodedCharPos) { |
| 1971 | // Found start of encoded character byte within $lookBack block. |
| 1972 | // Check the encoded byte value (the 2 chars after the '=') |
| 1973 | $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); |
| 1974 | $dec = hexdec($hex); |
| 1975 | if ($dec < 128) { |
| 1976 | // Single byte character. |
| 1977 | // If the encoded char was found at pos 0, it will fit |
| 1978 | // otherwise reduce maxLength to start of the encoded char |
| 1979 | if ($encodedCharPos > 0) { |
| 1980 | $maxLength = $maxLength - ($lookBack - $encodedCharPos); |
| 1981 | } |
| 1982 | $foundSplitPos = true; |
| 1983 | } elseif ($dec >= 192) { |
| 1984 | // First byte of a multi byte character |
| 1985 | // Reduce maxLength to split at start of character |
| 1986 | $maxLength = $maxLength - ($lookBack - $encodedCharPos); |
| 1987 | $foundSplitPos = true; |
| 1988 | } elseif ($dec < 192) { |
| 1989 | // Middle byte of a multi byte character, look further back |
| 1990 | $lookBack += 3; |
| 1991 | } |
| 1992 | } else { |
| 1993 | // No encoded character found |
| 1994 | $foundSplitPos = true; |
| 1995 | } |
| 1996 | } |
| 1997 | return $maxLength; |
| 1998 | } |
2117 | | /** |
2118 | | * Get the message MIME type headers. |
2119 | | * @access public |
2120 | | * @return string |
2121 | | */ |
2122 | | public function getMailMIME() |
2123 | | { |
2124 | | $result = ''; |
2125 | | $ismultipart = true; |
2126 | | switch ($this->message_type) { |
2127 | | case 'inline': |
2128 | | $result .= $this->headerLine('Content-Type', 'multipart/related;'); |
2129 | | $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); |
2130 | | break; |
2131 | | case 'attach': |
2132 | | case 'inline_attach': |
2133 | | case 'alt_attach': |
2134 | | case 'alt_inline_attach': |
2135 | | $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); |
2136 | | $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); |
2137 | | break; |
2138 | | case 'alt': |
2139 | | case 'alt_inline': |
2140 | | $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); |
2141 | | $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); |
2142 | | break; |
2143 | | default: |
2144 | | // Catches case 'plain': and case '': |
2145 | | $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); |
2146 | | $ismultipart = false; |
2147 | | break; |
2148 | | } |
2149 | | // RFC1341 part 5 says 7bit is assumed if not specified |
2150 | | if ($this->Encoding != '7bit') { |
2151 | | // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE |
2152 | | if ($ismultipart) { |
2153 | | if ($this->Encoding == '8bit') { |
2154 | | $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); |
2155 | | } |
2156 | | // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible |
2157 | | } else { |
2158 | | $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); |
2159 | | } |
2160 | | } |
| 2122 | /** |
| 2123 | * Get the message MIME type headers. |
| 2124 | * @access public |
| 2125 | * @return string |
| 2126 | */ |
| 2127 | public function getMailMIME() |
| 2128 | { |
| 2129 | $result = ''; |
| 2130 | $ismultipart = true; |
| 2131 | switch ($this->message_type) { |
| 2132 | case 'inline': |
| 2133 | $result .= $this->headerLine('Content-Type', 'multipart/related;'); |
| 2134 | $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); |
| 2135 | break; |
| 2136 | case 'attach': |
| 2137 | case 'inline_attach': |
| 2138 | case 'alt_attach': |
| 2139 | case 'alt_inline_attach': |
| 2140 | $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); |
| 2141 | $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); |
| 2142 | break; |
| 2143 | case 'alt': |
| 2144 | case 'alt_inline': |
| 2145 | $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); |
| 2146 | $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); |
| 2147 | break; |
| 2148 | default: |
| 2149 | // Catches case 'plain': and case '': |
| 2150 | $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); |
| 2151 | $ismultipart = false; |
| 2152 | break; |
| 2153 | } |
| 2154 | // RFC1341 part 5 says 7bit is assumed if not specified |
| 2155 | if ($this->Encoding != '7bit') { |
| 2156 | // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE |
| 2157 | if ($ismultipart) { |
| 2158 | if ($this->Encoding == '8bit') { |
| 2159 | $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); |
| 2160 | } |
| 2161 | // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible |
| 2162 | } else { |
| 2163 | $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); |
| 2164 | } |
| 2165 | } |
2226 | | $altBodyEncoding = $this->Encoding; |
2227 | | $altBodyCharSet = $this->CharSet; |
2228 | | //Can we do a 7-bit downgrade? |
2229 | | if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { |
2230 | | $altBodyEncoding = '7bit'; |
2231 | | //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit |
2232 | | $altBodyCharSet = 'us-ascii'; |
2233 | | } |
2234 | | //If lines are too long, and we're not already using an encoding that will shorten them, |
2235 | | //change to quoted-printable transfer encoding for the alt body part only |
2236 | | if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { |
2237 | | $altBodyEncoding = 'quoted-printable'; |
2238 | | } |
2239 | | //Use this as a preamble in all multipart message types |
2240 | | $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; |
2241 | | switch ($this->message_type) { |
2242 | | case 'inline': |
2243 | | $body .= $mimepre; |
2244 | | $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); |
2245 | | $body .= $this->encodeString($this->Body, $bodyEncoding); |
2246 | | $body .= $this->LE . $this->LE; |
2247 | | $body .= $this->attachAll('inline', $this->boundary[1]); |
2248 | | break; |
2249 | | case 'attach': |
2250 | | $body .= $mimepre; |
2251 | | $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); |
2252 | | $body .= $this->encodeString($this->Body, $bodyEncoding); |
2253 | | $body .= $this->LE . $this->LE; |
2254 | | $body .= $this->attachAll('attachment', $this->boundary[1]); |
2255 | | break; |
2256 | | case 'inline_attach': |
2257 | | $body .= $mimepre; |
2258 | | $body .= $this->textLine('--' . $this->boundary[1]); |
2259 | | $body .= $this->headerLine('Content-Type', 'multipart/related;'); |
2260 | | $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); |
2261 | | $body .= $this->LE; |
2262 | | $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); |
2263 | | $body .= $this->encodeString($this->Body, $bodyEncoding); |
2264 | | $body .= $this->LE . $this->LE; |
2265 | | $body .= $this->attachAll('inline', $this->boundary[2]); |
2266 | | $body .= $this->LE; |
2267 | | $body .= $this->attachAll('attachment', $this->boundary[1]); |
2268 | | break; |
2269 | | case 'alt': |
2270 | | $body .= $mimepre; |
2271 | | $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); |
2272 | | $body .= $this->encodeString($this->AltBody, $altBodyEncoding); |
2273 | | $body .= $this->LE . $this->LE; |
2274 | | $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); |
2275 | | $body .= $this->encodeString($this->Body, $bodyEncoding); |
2276 | | $body .= $this->LE . $this->LE; |
2277 | | if (!empty($this->Ical)) { |
2278 | | $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); |
2279 | | $body .= $this->encodeString($this->Ical, $this->Encoding); |
2280 | | $body .= $this->LE . $this->LE; |
2281 | | } |
2282 | | $body .= $this->endBoundary($this->boundary[1]); |
2283 | | break; |
2284 | | case 'alt_inline': |
2285 | | $body .= $mimepre; |
2286 | | $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); |
2287 | | $body .= $this->encodeString($this->AltBody, $altBodyEncoding); |
2288 | | $body .= $this->LE . $this->LE; |
2289 | | $body .= $this->textLine('--' . $this->boundary[1]); |
2290 | | $body .= $this->headerLine('Content-Type', 'multipart/related;'); |
2291 | | $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); |
2292 | | $body .= $this->LE; |
2293 | | $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); |
2294 | | $body .= $this->encodeString($this->Body, $bodyEncoding); |
2295 | | $body .= $this->LE . $this->LE; |
2296 | | $body .= $this->attachAll('inline', $this->boundary[2]); |
2297 | | $body .= $this->LE; |
2298 | | $body .= $this->endBoundary($this->boundary[1]); |
2299 | | break; |
2300 | | case 'alt_attach': |
2301 | | $body .= $mimepre; |
2302 | | $body .= $this->textLine('--' . $this->boundary[1]); |
2303 | | $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); |
2304 | | $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); |
2305 | | $body .= $this->LE; |
2306 | | $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); |
2307 | | $body .= $this->encodeString($this->AltBody, $altBodyEncoding); |
2308 | | $body .= $this->LE . $this->LE; |
2309 | | $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); |
2310 | | $body .= $this->encodeString($this->Body, $bodyEncoding); |
2311 | | $body .= $this->LE . $this->LE; |
2312 | | $body .= $this->endBoundary($this->boundary[2]); |
2313 | | $body .= $this->LE; |
2314 | | $body .= $this->attachAll('attachment', $this->boundary[1]); |
2315 | | break; |
2316 | | case 'alt_inline_attach': |
2317 | | $body .= $mimepre; |
2318 | | $body .= $this->textLine('--' . $this->boundary[1]); |
2319 | | $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); |
2320 | | $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); |
2321 | | $body .= $this->LE; |
2322 | | $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); |
2323 | | $body .= $this->encodeString($this->AltBody, $altBodyEncoding); |
2324 | | $body .= $this->LE . $this->LE; |
2325 | | $body .= $this->textLine('--' . $this->boundary[2]); |
2326 | | $body .= $this->headerLine('Content-Type', 'multipart/related;'); |
2327 | | $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); |
2328 | | $body .= $this->LE; |
2329 | | $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); |
2330 | | $body .= $this->encodeString($this->Body, $bodyEncoding); |
2331 | | $body .= $this->LE . $this->LE; |
2332 | | $body .= $this->attachAll('inline', $this->boundary[3]); |
2333 | | $body .= $this->LE; |
2334 | | $body .= $this->endBoundary($this->boundary[2]); |
2335 | | $body .= $this->LE; |
2336 | | $body .= $this->attachAll('attachment', $this->boundary[1]); |
2337 | | break; |
2338 | | default: |
2339 | | // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types |
2340 | | //Reset the `Encoding` property in case we changed it for line length reasons |
2341 | | $this->Encoding = $bodyEncoding; |
2342 | | $body .= $this->encodeString($this->Body, $this->Encoding); |
2343 | | break; |
2344 | | } |
| 2231 | $altBodyEncoding = $this->Encoding; |
| 2232 | $altBodyCharSet = $this->CharSet; |
| 2233 | //Can we do a 7-bit downgrade? |
| 2234 | if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { |
| 2235 | $altBodyEncoding = '7bit'; |
| 2236 | //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit |
| 2237 | $altBodyCharSet = 'us-ascii'; |
| 2238 | } |
| 2239 | //If lines are too long, and we're not already using an encoding that will shorten them, |
| 2240 | //change to quoted-printable transfer encoding for the alt body part only |
| 2241 | if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { |
| 2242 | $altBodyEncoding = 'quoted-printable'; |
| 2243 | } |
| 2244 | //Use this as a preamble in all multipart message types |
| 2245 | $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; |
| 2246 | switch ($this->message_type) { |
| 2247 | case 'inline': |
| 2248 | $body .= $mimepre; |
| 2249 | $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); |
| 2250 | $body .= $this->encodeString($this->Body, $bodyEncoding); |
| 2251 | $body .= $this->LE . $this->LE; |
| 2252 | $body .= $this->attachAll('inline', $this->boundary[1]); |
| 2253 | break; |
| 2254 | case 'attach': |
| 2255 | $body .= $mimepre; |
| 2256 | $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); |
| 2257 | $body .= $this->encodeString($this->Body, $bodyEncoding); |
| 2258 | $body .= $this->LE . $this->LE; |
| 2259 | $body .= $this->attachAll('attachment', $this->boundary[1]); |
| 2260 | break; |
| 2261 | case 'inline_attach': |
| 2262 | $body .= $mimepre; |
| 2263 | $body .= $this->textLine('--' . $this->boundary[1]); |
| 2264 | $body .= $this->headerLine('Content-Type', 'multipart/related;'); |
| 2265 | $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); |
| 2266 | $body .= $this->LE; |
| 2267 | $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); |
| 2268 | $body .= $this->encodeString($this->Body, $bodyEncoding); |
| 2269 | $body .= $this->LE . $this->LE; |
| 2270 | $body .= $this->attachAll('inline', $this->boundary[2]); |
| 2271 | $body .= $this->LE; |
| 2272 | $body .= $this->attachAll('attachment', $this->boundary[1]); |
| 2273 | break; |
| 2274 | case 'alt': |
| 2275 | $body .= $mimepre; |
| 2276 | $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); |
| 2277 | $body .= $this->encodeString($this->AltBody, $altBodyEncoding); |
| 2278 | $body .= $this->LE . $this->LE; |
| 2279 | $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); |
| 2280 | $body .= $this->encodeString($this->Body, $bodyEncoding); |
| 2281 | $body .= $this->LE . $this->LE; |
| 2282 | if (!empty($this->Ical)) { |
| 2283 | $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); |
| 2284 | $body .= $this->encodeString($this->Ical, $this->Encoding); |
| 2285 | $body .= $this->LE . $this->LE; |
| 2286 | } |
| 2287 | $body .= $this->endBoundary($this->boundary[1]); |
| 2288 | break; |
| 2289 | case 'alt_inline': |
| 2290 | $body .= $mimepre; |
| 2291 | $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); |
| 2292 | $body .= $this->encodeString($this->AltBody, $altBodyEncoding); |
| 2293 | $body .= $this->LE . $this->LE; |
| 2294 | $body .= $this->textLine('--' . $this->boundary[1]); |
| 2295 | $body .= $this->headerLine('Content-Type', 'multipart/related;'); |
| 2296 | $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); |
| 2297 | $body .= $this->LE; |
| 2298 | $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); |
| 2299 | $body .= $this->encodeString($this->Body, $bodyEncoding); |
| 2300 | $body .= $this->LE . $this->LE; |
| 2301 | $body .= $this->attachAll('inline', $this->boundary[2]); |
| 2302 | $body .= $this->LE; |
| 2303 | $body .= $this->endBoundary($this->boundary[1]); |
| 2304 | break; |
| 2305 | case 'alt_attach': |
| 2306 | $body .= $mimepre; |
| 2307 | $body .= $this->textLine('--' . $this->boundary[1]); |
| 2308 | $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); |
| 2309 | $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); |
| 2310 | $body .= $this->LE; |
| 2311 | $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); |
| 2312 | $body .= $this->encodeString($this->AltBody, $altBodyEncoding); |
| 2313 | $body .= $this->LE . $this->LE; |
| 2314 | $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); |
| 2315 | $body .= $this->encodeString($this->Body, $bodyEncoding); |
| 2316 | $body .= $this->LE . $this->LE; |
| 2317 | $body .= $this->endBoundary($this->boundary[2]); |
| 2318 | $body .= $this->LE; |
| 2319 | $body .= $this->attachAll('attachment', $this->boundary[1]); |
| 2320 | break; |
| 2321 | case 'alt_inline_attach': |
| 2322 | $body .= $mimepre; |
| 2323 | $body .= $this->textLine('--' . $this->boundary[1]); |
| 2324 | $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); |
| 2325 | $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); |
| 2326 | $body .= $this->LE; |
| 2327 | $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); |
| 2328 | $body .= $this->encodeString($this->AltBody, $altBodyEncoding); |
| 2329 | $body .= $this->LE . $this->LE; |
| 2330 | $body .= $this->textLine('--' . $this->boundary[2]); |
| 2331 | $body .= $this->headerLine('Content-Type', 'multipart/related;'); |
| 2332 | $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); |
| 2333 | $body .= $this->LE; |
| 2334 | $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); |
| 2335 | $body .= $this->encodeString($this->Body, $bodyEncoding); |
| 2336 | $body .= $this->LE . $this->LE; |
| 2337 | $body .= $this->attachAll('inline', $this->boundary[3]); |
| 2338 | $body .= $this->LE; |
| 2339 | $body .= $this->endBoundary($this->boundary[2]); |
| 2340 | $body .= $this->LE; |
| 2341 | $body .= $this->attachAll('attachment', $this->boundary[1]); |
| 2342 | break; |
| 2343 | default: |
| 2344 | // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types |
| 2345 | //Reset the `Encoding` property in case we changed it for line length reasons |
| 2346 | $this->Encoding = $bodyEncoding; |
| 2347 | $body .= $this->encodeString($this->Body, $this->Encoding); |
| 2348 | break; |
| 2349 | } |
2346 | | if ($this->isError()) { |
2347 | | $body = ''; |
2348 | | } elseif ($this->sign_key_file) { |
2349 | | try { |
2350 | | if (!defined('PKCS7_TEXT')) { |
2351 | | throw new phpmailerException($this->lang('extension_missing') . 'openssl'); |
2352 | | } |
2353 | | // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 |
2354 | | $file = tempnam(sys_get_temp_dir(), 'mail'); |
2355 | | if (false === file_put_contents($file, $body)) { |
2356 | | throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); |
2357 | | } |
2358 | | $signed = tempnam(sys_get_temp_dir(), 'signed'); |
2359 | | //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 |
2360 | | if (empty($this->sign_extracerts_file)) { |
2361 | | $sign = @openssl_pkcs7_sign( |
2362 | | $file, |
2363 | | $signed, |
2364 | | 'file://' . realpath($this->sign_cert_file), |
2365 | | array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), |
2366 | | null |
2367 | | ); |
2368 | | } else { |
2369 | | $sign = @openssl_pkcs7_sign( |
2370 | | $file, |
2371 | | $signed, |
2372 | | 'file://' . realpath($this->sign_cert_file), |
2373 | | array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), |
2374 | | null, |
2375 | | PKCS7_DETACHED, |
2376 | | $this->sign_extracerts_file |
2377 | | ); |
2378 | | } |
2379 | | if ($sign) { |
2380 | | @unlink($file); |
2381 | | $body = file_get_contents($signed); |
2382 | | @unlink($signed); |
2383 | | //The message returned by openssl contains both headers and body, so need to split them up |
2384 | | $parts = explode("\n\n", $body, 2); |
2385 | | $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; |
2386 | | $body = $parts[1]; |
2387 | | } else { |
2388 | | @unlink($file); |
2389 | | @unlink($signed); |
2390 | | throw new phpmailerException($this->lang('signing') . openssl_error_string()); |
2391 | | } |
2392 | | } catch (phpmailerException $exc) { |
2393 | | $body = ''; |
2394 | | if ($this->exceptions) { |
2395 | | throw $exc; |
2396 | | } |
2397 | | } |
2398 | | } |
2399 | | return $body; |
2400 | | } |
| 2351 | if ($this->isError()) { |
| 2352 | $body = ''; |
| 2353 | } elseif ($this->sign_key_file) { |
| 2354 | try { |
| 2355 | if (!defined('PKCS7_TEXT')) { |
| 2356 | throw new phpmailerException($this->lang('extension_missing') . 'openssl'); |
| 2357 | } |
| 2358 | // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 |
| 2359 | $file = tempnam(sys_get_temp_dir(), 'mail'); |
| 2360 | if (false === file_put_contents($file, $body)) { |
| 2361 | throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); |
| 2362 | } |
| 2363 | $signed = tempnam(sys_get_temp_dir(), 'signed'); |
| 2364 | //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 |
| 2365 | if (empty($this->sign_extracerts_file)) { |
| 2366 | $sign = @openssl_pkcs7_sign( |
| 2367 | $file, |
| 2368 | $signed, |
| 2369 | 'file://' . realpath($this->sign_cert_file), |
| 2370 | array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), |
| 2371 | null |
| 2372 | ); |
| 2373 | } else { |
| 2374 | $sign = @openssl_pkcs7_sign( |
| 2375 | $file, |
| 2376 | $signed, |
| 2377 | 'file://' . realpath($this->sign_cert_file), |
| 2378 | array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), |
| 2379 | null, |
| 2380 | PKCS7_DETACHED, |
| 2381 | $this->sign_extracerts_file |
| 2382 | ); |
| 2383 | } |
| 2384 | if ($sign) { |
| 2385 | @unlink($file); |
| 2386 | $body = file_get_contents($signed); |
| 2387 | @unlink($signed); |
| 2388 | //The message returned by openssl contains both headers and body, so need to split them up |
| 2389 | $parts = explode("\n\n", $body, 2); |
| 2390 | $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; |
| 2391 | $body = $parts[1]; |
| 2392 | } else { |
| 2393 | @unlink($file); |
| 2394 | @unlink($signed); |
| 2395 | throw new phpmailerException($this->lang('signing') . openssl_error_string()); |
| 2396 | } |
| 2397 | } catch (phpmailerException $exc) { |
| 2398 | $body = ''; |
| 2399 | if ($this->exceptions) { |
| 2400 | throw $exc; |
| 2401 | } |
| 2402 | } |
| 2403 | } |
| 2404 | return $body; |
| 2405 | } |