Ticket #37210: 37210v2.diff
File 37210v2.diff, 34.9 KB (added by , 9 years ago) |
---|
-
src/wp-includes/class-phpmailer.php
31 31 * The PHPMailer Version number. 32 32 * @var string 33 33 */ 34 public $Version = '5.2.1 4';34 public $Version = '5.2.19'; 35 35 36 36 /** 37 37 * Email priority. … … 201 201 /** 202 202 * An ID to be used in the Message-ID header. 203 203 * If empty, a unique id will be generated. 204 * You can set your own, but it must be in the format "<id@domain>", 205 * as defined in RFC5322 section 3.6.4 or it will be ignored. 206 * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 204 207 * @var string 205 208 */ 206 209 public $MessageID = ''; … … 285 288 286 289 /** 287 290 * SMTP auth type. 288 * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5291 * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified 289 292 * @var string 290 293 */ 291 294 public $AuthType = ''; … … 352 355 /** 353 356 * Whether to split multiple to addresses into multiple messages 354 357 * or send them all in one message. 358 * Only supported in `mail` and `sendmail` transports, not in SMTP. 355 359 * @var boolean 356 360 */ 357 361 public $SingleTo = false; … … 394 398 395 399 /** 396 400 * DKIM Identity. 397 * Usually the email address used as the source of the email 401 * Usually the email address used as the source of the email. 398 402 * @var string 399 403 */ 400 404 public $DKIM_identity = ''; … … 420 424 public $DKIM_private = ''; 421 425 422 426 /** 427 * DKIM private key string. 428 * If set, takes precedence over `$DKIM_private`. 429 * @var string 430 */ 431 public $DKIM_private_string = ''; 432 433 /** 423 434 * Callback Action function name. 424 435 * 425 436 * The function that handles the result of the send email action. … … 447 458 public $XMailer = ''; 448 459 449 460 /** 461 * Which validator to use by default when validating email addresses. 462 * May be a callable to inject your own validator, but there are several built-in validators. 463 * @see PHPMailer::validateAddress() 464 * @var string|callable 465 * @static 466 */ 467 public static $validator = 'auto'; 468 469 /** 450 470 * An instance of the SMTP sender class. 451 471 * @var SMTP 452 472 * @access protected … … 634 654 * Constructor. 635 655 * @param boolean $exceptions Should we throw external exceptions? 636 656 */ 637 public function __construct($exceptions = false)657 public function __construct($exceptions = null) 638 658 { 639 $this->exceptions = (boolean)$exceptions; 659 if ($exceptions !== null) { 660 $this->exceptions = (boolean)$exceptions; 661 } 640 662 } 641 663 642 664 /** … … 645 667 public function __destruct() 646 668 { 647 669 //Close any open SMTP connection nicely 648 if ($this->Mailer == 'smtp') { 649 $this->smtpClose(); 650 } 670 $this->smtpClose(); 651 671 } 652 672 653 673 /** … … 671 691 } else { 672 692 $subject = $this->encodeHeader($this->secureHeader($subject)); 673 693 } 674 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) { 694 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)) { 675 698 $result = @mail($to, $subject, $body, $header); 676 699 } else { 677 700 $result = @mail($to, $subject, $body, $header, $params); … … 678 701 } 679 702 return $result; 680 703 } 681 682 704 /** 683 705 * Output debugging info via user-defined method. 684 706 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). … … 713 735 case 'echo': 714 736 default: 715 737 //Normalize line breaks 716 $str = preg_replace('/ (\r\n|\r|\n)/ms', "\n", $str);738 $str = preg_replace('/\r\n?/ms', "\n", $str); 717 739 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 718 740 "\n", 719 741 "\n \t ", … … 850 872 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 851 873 if (($pos = strrpos($address, '@')) === false) { 852 874 // At-sign is misssing. 853 $error_message = $this->lang('invalid_address') . $address;875 $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; 854 876 $this->setError($error_message); 855 877 $this->edebug($error_message); 856 878 if ($this->exceptions) { … … 900 922 return false; 901 923 } 902 924 if (!$this->validateAddress($address)) { 903 $error_message = $this->lang('invalid_address') . $address;925 $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; 904 926 $this->setError($error_message); 905 927 $this->edebug($error_message); 906 928 if ($this->exceptions) { … … 924 946 } 925 947 926 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 } 1002 1003 /** 927 1004 * Set the From and FromName properties. 928 1005 * @param string $address 929 1006 * @param string $name … … 939 1016 if (($pos = strrpos($address, '@')) === false or 940 1017 (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and 941 1018 !$this->validateAddress($address)) { 942 $error_message = $this->lang('invalid_address') . $address;1019 $error_message = $this->lang('invalid_address') . " (setFrom) $address"; 943 1020 $this->setError($error_message); 944 1021 $this->edebug($error_message); 945 1022 if ($this->exceptions) { … … 972 1049 /** 973 1050 * Check that a string looks like an email address. 974 1051 * @param string $address The email address to check 975 * @param string $patternselect A selector for the validation pattern to use :1052 * @param string|callable $patternselect A selector for the validation pattern to use : 976 1053 * * `auto` Pick best pattern automatically; 977 1054 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; 978 1055 * * `pcre` Use old PCRE implementation; … … 979 1056 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; 980 1057 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. 981 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. 982 1064 * @return boolean 983 1065 * @static 984 1066 * @access public 985 1067 */ 986 public static function validateAddress($address, $patternselect = 'auto')1068 public static function validateAddress($address, $patternselect = null) 987 1069 { 1070 if (is_null($patternselect)) { 1071 $patternselect = self::$validator; 1072 } 1073 if (is_callable($patternselect)) { 1074 return call_user_func($patternselect, $address); 1075 } 988 1076 //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 989 1077 if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { 990 1078 return false; … … 1161 1249 } 1162 1250 $this->$address_kind = $this->punyencodeAddress($this->$address_kind); 1163 1251 if (!$this->validateAddress($this->$address_kind)) { 1164 $error_message = $this->lang('invalid_address') . $this->$address_kind;1252 $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; 1165 1253 $this->setError($error_message); 1166 1254 $this->edebug($error_message); 1167 1255 if ($this->exceptions) { … … 1172 1260 } 1173 1261 1174 1262 // Set whether the message is multipart/alternative 1175 if ( !empty($this->AltBody)) {1263 if ($this->alternativeExists()) { 1176 1264 $this->ContentType = 'multipart/alternative'; 1177 1265 } 1178 1266 … … 1206 1294 1207 1295 // Sign with DKIM if enabled 1208 1296 if (!empty($this->DKIM_domain) 1209 && !empty($this->DKIM_private)1210 1297 && !empty($this->DKIM_selector) 1211 && file_exists($this->DKIM_private)) { 1298 && (!empty($this->DKIM_private_string) 1299 || (!empty($this->DKIM_private) && file_exists($this->DKIM_private)) 1300 ) 1301 ) { 1212 1302 $header_dkim = $this->DKIM_Add( 1213 1303 $this->MIMEHeader . $this->mailHeader, 1214 1304 $this->encodeHeader($this->secureHeader($this->Subject)), … … 1274 1364 */ 1275 1365 protected function sendmailSend($header, $body) 1276 1366 { 1277 if ( $this->Sender != '') {1367 if (!empty($this->Sender)) { 1278 1368 if ($this->Mailer == 'qmail') { 1279 1369 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 1280 1370 } else { … … 1349 1439 } 1350 1440 $to = implode(', ', $toArr); 1351 1441 1352 if (empty($this->Sender)) {1353 $params = ' ';1354 } else{1355 $params = sprintf('-f%s', $this->Sender);1442 $params = null; 1443 //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver 1444 if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { 1445 $params = sprintf('-f%s', escapeshellarg($this->Sender)); 1356 1446 } 1357 if ( $this->Sender != '' and !ini_get('safe_mode')) {1447 if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) { 1358 1448 $old_from = ini_get('sendmail_from'); 1359 1449 ini_set('sendmail_from', $this->Sender); 1360 1450 } 1361 1451 $result = false; 1362 if ($this->SingleTo &&count($toArr) > 1) {1452 if ($this->SingleTo and count($toArr) > 1) { 1363 1453 foreach ($toArr as $toAddr) { 1364 1454 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); 1365 1455 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); … … 1385 1475 public function getSMTPInstance() 1386 1476 { 1387 1477 if (!is_object($this->smtp)) { 1388 1478 require_once( 'class-smtp.php' ); 1389 1479 $this->smtp = new SMTP; 1390 1480 } 1391 1481 return $this->smtp; … … 1409 1499 if (!$this->smtpConnect($this->SMTPOptions)) { 1410 1500 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); 1411 1501 } 1412 if ('' == $this->Sender) { 1502 if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { 1503 $smtp_from = $this->Sender; 1504 } else { 1413 1505 $smtp_from = $this->From; 1414 } else {1415 $smtp_from = $this->Sender;1416 1506 } 1417 1507 if (!$this->smtp->mail($smtp_from)) { 1418 1508 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); … … 1466 1556 * @throws phpmailerException 1467 1557 * @return boolean 1468 1558 */ 1469 public function smtpConnect($options = array())1559 public function smtpConnect($options = null) 1470 1560 { 1471 1561 if (is_null($this->smtp)) { 1472 1562 $this->smtp = $this->getSMTPInstance(); 1473 1563 } 1474 1564 1565 //If no options are provided, use whatever is set in the instance 1566 if (is_null($options)) { 1567 $options = $this->SMTPOptions; 1568 } 1569 1475 1570 // Already connected? 1476 1571 if ($this->smtp->connected()) { 1477 1572 return true; … … 1541 1636 if (!$this->smtp->startTLS()) { 1542 1637 throw new phpmailerException($this->lang('connect_host')); 1543 1638 } 1544 // We must resend HELO after tlsnegotiation1639 // We must resend EHLO after TLS negotiation 1545 1640 $this->smtp->hello($hello); 1546 1641 } 1547 1642 if ($this->SMTPAuth) { … … 1580 1675 */ 1581 1676 public function smtpClose() 1582 1677 { 1583 if ( $this->smtp !== null) {1678 if (is_a($this->smtp, 'SMTP')) { 1584 1679 if ($this->smtp->connected()) { 1585 1680 $this->smtp->quit(); 1586 1681 $this->smtp->close(); … … 1599 1694 */ 1600 1695 public function setLanguage($langcode = 'en', $lang_path = '') 1601 1696 { 1697 // Backwards compatibility for renamed language codes 1698 $renamed_langcodes = array( 1699 'br' => 'pt_br', 1700 'cz' => 'cs', 1701 'dk' => 'da', 1702 'no' => 'nb', 1703 'se' => 'sv', 1704 ); 1705 1706 if (isset($renamed_langcodes[$langcode])) { 1707 $langcode = $renamed_langcodes[$langcode]; 1708 } 1709 1602 1710 // Define full set of translatable strings in English 1603 1711 $PHPMAILER_LANG = array( 1604 1712 'authenticate' => 'SMTP Error: Could not authenticate.', … … 1625 1733 // Calculate an absolute path so it can work if CWD is not here 1626 1734 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; 1627 1735 } 1736 //Validate $langcode 1737 if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { 1738 $langcode = 'en'; 1739 } 1628 1740 $foundlang = true; 1629 1741 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; 1630 1742 // There is no English translation file … … 1918 2030 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); 1919 2031 } 1920 2032 1921 if ($this->MessageID != '') { 2033 // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 2034 // https://tools.ietf.org/html/rfc5322#section-3.6.4 2035 if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { 1922 2036 $this->lastMessageID = $this->MessageID; 1923 2037 } else { 1924 2038 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); … … 2020 2134 */ 2021 2135 public function getSentMIMEMessage() 2022 2136 { 2023 return $this->MIMEHeader . $this->mailHeader. self::CRLF . $this->MIMEBody;2137 return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; 2024 2138 } 2025 2139 2026 2140 /** 2141 * Create unique ID 2142 * @return string 2143 */ 2144 protected function generateId() { 2145 return md5(uniqid(time())); 2146 } 2147 2148 /** 2027 2149 * Assemble the message body. 2028 2150 * Returns an empty string on failure. 2029 2151 * @access public … … 2034 2156 { 2035 2157 $body = ''; 2036 2158 //Create unique IDs and preset boundaries 2037 $this->uniqueid = md5(uniqid(time()));2159 $this->uniqueid = $this->generateId(); 2038 2160 $this->boundary[1] = 'b1_' . $this->uniqueid; 2039 2161 $this->boundary[2] = 'b2_' . $this->uniqueid; 2040 2162 $this->boundary[3] = 'b3_' . $this->uniqueid; … … 2050 2172 //Can we do a 7-bit downgrade? 2051 2173 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { 2052 2174 $bodyEncoding = '7bit'; 2175 //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit 2053 2176 $bodyCharSet = 'us-ascii'; 2054 2177 } 2055 //If lines are too long, change to quoted-printable transfer encoding2056 if (self::hasLineLongerThanMax($this->Body)) {2057 $this->Encoding = 'quoted-printable';2178 //If lines are too long, and we're not already using an encoding that will shorten them, 2179 //change to quoted-printable transfer encoding for the body part only 2180 if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { 2058 2181 $bodyEncoding = 'quoted-printable'; 2059 2182 } 2060 2183 … … 2063 2186 //Can we do a 7-bit downgrade? 2064 2187 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { 2065 2188 $altBodyEncoding = '7bit'; 2189 //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit 2066 2190 $altBodyCharSet = 'us-ascii'; 2067 2191 } 2068 //If lines are too long, change to quoted-printable transfer encoding 2069 if (self::hasLineLongerThanMax($this->AltBody)) { 2192 //If lines are too long, and we're not already using an encoding that will shorten them, 2193 //change to quoted-printable transfer encoding for the alt body part only 2194 if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { 2070 2195 $altBodyEncoding = 'quoted-printable'; 2071 2196 } 2072 2197 //Use this as a preamble in all multipart message types … … 2169 2294 $body .= $this->attachAll('attachment', $this->boundary[1]); 2170 2295 break; 2171 2296 default: 2172 // catch case 'plain' and case '' 2173 $body .= $this->encodeString($this->Body, $bodyEncoding); 2297 // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types 2298 //Reset the `Encoding` property in case we changed it for line length reasons 2299 $this->Encoding = $bodyEncoding; 2300 $body .= $this->encodeString($this->Body, $this->Encoding); 2174 2301 break; 2175 2302 } 2176 2303 … … 2276 2403 2277 2404 /** 2278 2405 * Set the message type. 2279 * PHPMailer only supports some preset message types, 2280 * not arbitrary MIME structures. 2406 * PHPMailer only supports some preset message types, not arbitrary MIME structures. 2281 2407 * @access protected 2282 2408 * @return void 2283 2409 */ … … 2295 2421 } 2296 2422 $this->message_type = implode('_', $type); 2297 2423 if ($this->message_type == '') { 2424 //The 'plain' message_type refers to the message having a single body element, not that it is plain-text 2298 2425 $this->message_type = 'plain'; 2299 2426 } 2300 2427 } … … 3209 3336 } 3210 3337 3211 3338 /** 3212 * Create a message from an HTML string. 3213 * Automatically makes modifications for inline images and backgrounds 3214 * and creates a plain-text version by converting the HTML. 3215 * Overwrites any existing values in $this->Body and $this->AltBody 3339 * Create a message body from an HTML string. 3340 * Automatically inlines images and creates a plain-text version by converting the HTML, 3341 * overwriting any existing values in Body and AltBody. 3342 * $basedir is used when handling relative image paths, e.g. <img src="images/a.png"> 3343 * will look for an image file in $basedir/images/a.png and convert it to inline. 3344 * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself. 3216 3345 * @access public 3217 3346 * @param string $message HTML message string 3218 * @param string $basedir base line directory for path3347 * @param string $basedir base directory for relative paths to images 3219 3348 * @param boolean|callable $advanced Whether to use the internal HTML to text converter 3220 3349 * or your own custom converter @see PHPMailer::html2text() 3221 * @return string $message 3350 * @return string $message The transformed message Body 3222 3351 */ 3223 3352 public function msgHTML($message, $basedir = '', $advanced = false) 3224 3353 { … … 3241 3370 $message 3242 3371 ); 3243 3372 } 3244 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[ A-z]+://#', $url)) {3373 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { 3245 3374 // Do not change urls for absolute images (thanks to corvuscorax) 3246 3375 // Do not change urls that are already inline images 3247 3376 $filename = basename($url); … … 3277 3406 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 3278 3407 $this->Body = $this->normalizeBreaks($message); 3279 3408 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); 3280 if ( empty($this->AltBody)) {3409 if (!$this->alternativeExists()) { 3281 3410 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . 3282 3411 self::CRLF . self::CRLF; 3283 3412 } … … 3288 3417 * Convert an HTML string into plain text. 3289 3418 * This is used by msgHTML(). 3290 3419 * Note - older versions of this function used a bundled advanced converter 3291 * which was been removed for license reasons in #232 3420 * which was been removed for license reasons in #232. 3292 3421 * Example usage: 3293 3422 * <code> 3294 3423 * // Use default conversion … … 3588 3717 * @access public 3589 3718 * @param string $signHeader 3590 3719 * @throws phpmailerException 3591 * @return string 3720 * @return string The DKIM signature value 3592 3721 */ 3593 3722 public function DKIM_Sign($signHeader) 3594 3723 { … … 3598 3727 } 3599 3728 return ''; 3600 3729 } 3601 $privKeyStr = file_get_contents($this->DKIM_private);3602 if ( $this->DKIM_passphrase != '') {3730 $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private); 3731 if ('' != $this->DKIM_passphrase) { 3603 3732 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); 3604 3733 } else { 3605 $privKey = $privKeyStr;3734 $privKey = openssl_pkey_get_private($privKeyStr); 3606 3735 } 3607 if (openssl_sign($signHeader, $signature, $privKey)) { 3608 return base64_encode($signature); 3736 //Workaround for missing digest algorithms in old PHP & OpenSSL versions 3737 //@link http://stackoverflow.com/a/11117338/333340 3738 if (version_compare(PHP_VERSION, '5.3.0') >= 0 and 3739 in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { 3740 if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { 3741 openssl_pkey_free($privKey); 3742 return base64_encode($signature); 3743 } 3744 } else { 3745 $pinfo = openssl_pkey_get_details($privKey); 3746 $hash = hash('sha256', $signHeader); 3747 //'Magic' constant for SHA256 from RFC3447 3748 //@link https://tools.ietf.org/html/rfc3447#page-43 3749 $t = '3031300d060960864801650304020105000420' . $hash; 3750 $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); 3751 $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); 3752 3753 if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { 3754 openssl_pkey_free($privKey); 3755 return base64_encode($signature); 3756 } 3609 3757 } 3758 openssl_pkey_free($privKey); 3610 3759 return ''; 3611 3760 } 3612 3761 … … 3623 3772 foreach ($lines as $key => $line) { 3624 3773 list($heading, $value) = explode(':', $line, 2); 3625 3774 $heading = strtolower($heading); 3626 $value = preg_replace('/\s +/', ' ', $value); // Compress useless spaces3775 $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces 3627 3776 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value 3628 3777 } 3629 3778 $signHeader = implode("\r\n", $lines); … … 3661 3810 */ 3662 3811 public function DKIM_Add($headers_line, $subject, $body) 3663 3812 { 3664 $DKIMsignatureType = 'rsa-sha 1'; // Signature & hash algorithms3813 $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms 3665 3814 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body 3666 3815 $DKIMquery = 'dns/txt'; // Query method 3667 3816 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) … … 3669 3818 $headers = explode($this->LE, $headers_line); 3670 3819 $from_header = ''; 3671 3820 $to_header = ''; 3821 $date_header = ''; 3672 3822 $current = ''; 3673 3823 foreach ($headers as $header) { 3674 3824 if (strpos($header, 'From:') === 0) { … … 3677 3827 } elseif (strpos($header, 'To:') === 0) { 3678 3828 $to_header = $header; 3679 3829 $current = 'to_header'; 3830 } elseif (strpos($header, 'Date:') === 0) { 3831 $date_header = $header; 3832 $current = 'date_header'; 3680 3833 } else { 3681 3834 if (!empty($$current) && strpos($header, ' =?') === 0) { 3682 3835 $$current .= $header; … … 3687 3840 } 3688 3841 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 3689 3842 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 3843 $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); 3690 3844 $subject = str_replace( 3691 3845 '|', 3692 3846 '=7C', … … 3694 3848 ); // Copied header fields (dkim-quoted-printable) 3695 3849 $body = $this->DKIM_BodyC($body); 3696 3850 $DKIMlen = strlen($body); // Length of body 3697 $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1hash of body3851 $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body 3698 3852 if ('' == $this->DKIM_identity) { 3699 3853 $ident = ''; 3700 3854 } else { … … 3707 3861 $this->DKIM_selector . 3708 3862 ";\r\n" . 3709 3863 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . 3710 "\th=From:To: Subject;\r\n" .3864 "\th=From:To:Date:Subject;\r\n" . 3711 3865 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . 3712 3866 "\tz=$from\r\n" . 3713 3867 "\t|$to\r\n" . 3868 "\t|$date\r\n" . 3714 3869 "\t|$subject;\r\n" . 3715 3870 "\tbh=" . $DKIMb64 . ";\r\n" . 3716 3871 "\tb="; … … 3717 3872 $toSign = $this->DKIM_HeaderC( 3718 3873 $from_header . "\r\n" . 3719 3874 $to_header . "\r\n" . 3875 $date_header . "\r\n" . 3720 3876 $subject_header . "\r\n" . 3721 3877 $dkimhdrs 3722 3878 ); -
src/wp-includes/class-smtp.php
30 30 * The PHPMailer SMTP version number. 31 31 * @var string 32 32 */ 33 const VERSION = '5.2.1 4';33 const VERSION = '5.2.19'; 34 34 35 35 /** 36 36 * SMTP line break constant. … … 81 81 * @deprecated Use the `VERSION` constant instead 82 82 * @see SMTP::VERSION 83 83 */ 84 public $Version = '5.2.1 4';84 public $Version = '5.2.19'; 85 85 86 86 /** 87 87 * SMTP server port number. … … 150 150 */ 151 151 public $Timelimit = 300; 152 152 153 /** 154 * @var array patterns to extract smtp transaction id from smtp reply 155 * Only first capture group will be use, use non-capturing group to deal with it 156 * Extend this class to override this property to fulfil your needs. 157 */ 158 protected $smtp_transaction_id_patterns = array( 159 'exim' => '/[0-9]{3} OK id=(.*)/', 160 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', 161 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' 162 ); 163 153 164 /** 154 165 * The socket for the server connection. 155 166 * @var resource … … 206 217 } 207 218 //Avoid clash with built-in function names 208 219 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { 209 call_user_func($this->Debugoutput, $str, $ this->do_debug);220 call_user_func($this->Debugoutput, $str, $level); 210 221 return; 211 222 } 212 223 switch ($this->Debugoutput) { … … 272 283 $errstr = ''; 273 284 if ($streamok) { 274 285 $socket_context = stream_context_create($options); 275 //Suppress errors; connection failures are handled at a higher level276 $this->smtp_conn = @stream_socket_client(286 set_error_handler(array($this, 'errorHandler')); 287 $this->smtp_conn = stream_socket_client( 277 288 $host . ":" . $port, 278 289 $errno, 279 290 $errstr, … … 281 292 STREAM_CLIENT_CONNECT, 282 293 $socket_context 283 294 ); 295 restore_error_handler(); 284 296 } else { 285 297 //Fall back to fsockopen which should work in more places, but is missing some features 286 298 $this->edebug( … … 287 299 "Connection: stream_socket_client not available, falling back to fsockopen", 288 300 self::DEBUG_CONNECTION 289 301 ); 302 set_error_handler(array($this, 'errorHandler')); 290 303 $this->smtp_conn = fsockopen( 291 304 $host, 292 305 $port, … … 294 307 $errstr, 295 308 $timeout 296 309 ); 310 restore_error_handler(); 297 311 } 298 312 // Verify we connected properly 299 313 if (!is_resource($this->smtp_conn)) { … … 336 350 if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { 337 351 return false; 338 352 } 353 354 //Allow the best TLS version(s) we can 355 $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; 356 357 //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT 358 //so add them back in manually if we can 359 if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { 360 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; 361 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; 362 } 363 339 364 // Begin encrypted connection 340 365 if (!stream_socket_enable_crypto( 341 366 $this->smtp_conn, 342 367 true, 343 STREAM_CRYPTO_METHOD_TLS_CLIENT368 $crypto_method 344 369 )) { 345 370 return false; 346 371 } … … 389 414 ); 390 415 391 416 if (empty($authtype)) { 392 foreach (array(' LOGIN', 'CRAM-MD5', 'PLAIN') as $method) {417 foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2') as $method) { 393 418 if (in_array($method, $this->server_caps['AUTH'])) { 394 419 $authtype = $method; 395 420 break; … … 437 462 return false; 438 463 } 439 464 break; 465 case 'XOAUTH2': 466 //If the OAuth Instance is not set. Can be a case when PHPMailer is used 467 //instead of PHPMailerOAuth 468 if (is_null($OAuth)) { 469 return false; 470 } 471 $oauth = $OAuth->getOauth64(); 472 473 // Start authentication 474 if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { 475 return false; 476 } 477 break; 440 478 case 'CRAM-MD5': 441 479 // Start authentication 442 480 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { … … 673 711 protected function parseHelloFields($type) 674 712 { 675 713 $this->server_caps = array(); 676 $lines = explode("\n", $this-> last_reply);714 $lines = explode("\n", $this->helo_rply); 677 715 678 716 foreach ($lines as $n => $s) { 679 717 //First 4 chars contain response code followed by - or space … … 1115 1153 { 1116 1154 return $this->Timeout; 1117 1155 } 1156 1157 /** 1158 * Reports an error number and string. 1159 * @param integer $errno The error number returned by PHP. 1160 * @param string $errmsg The error message returned by PHP. 1161 */ 1162 protected function errorHandler($errno, $errmsg) 1163 { 1164 $notice = 'Connection: Failed to connect to server.'; 1165 $this->setError( 1166 $notice, 1167 $errno, 1168 $errmsg 1169 ); 1170 $this->edebug( 1171 $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, 1172 self::DEBUG_CONNECTION 1173 ); 1174 } 1175 1176 /** 1177 * Will return the ID of the last smtp transaction based on a list of patterns provided 1178 * in SMTP::$smtp_transaction_id_patterns. 1179 * If no reply has been received yet, it will return null. 1180 * If no pattern has been matched, it will return false. 1181 * @return bool|null|string 1182 */ 1183 public function getLastTransactionID() 1184 { 1185 $reply = $this->getLastReply(); 1186 1187 if (empty($reply)) { 1188 return null; 1189 } 1190 1191 foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { 1192 if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { 1193 return $matches[1]; 1194 } 1195 } 1196 1197 return false; 1198 } 1118 1199 }