Ticket #37210: 39397.patch
File 39397.patch, 42.3 KB (added by , 8 years ago) |
---|
-
wp-includes/class-smtp.php
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
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) { … … 217 228 case 'html': 218 229 //Cleans up output a bit for a better looking, HTML-safe output 219 230 echo htmlentities( 220 preg_replace('/[\r\n]+/', '', $str),221 ENT_QUOTES,222 'UTF-8'223 )224 . "<br>\n";231 preg_replace('/[\r\n]+/', '', $str), 232 ENT_QUOTES, 233 'UTF-8' 234 ) 235 . "<br>\n"; 225 236 break; 226 237 case 'echo': 227 238 default: 228 239 //Normalize line breaks 229 240 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 230 241 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 231 "\n",232 "\n \t ",233 trim($str)234 )."\n";242 "\n", 243 "\n \t ", 244 trim($str) 245 )."\n"; 235 246 } 236 247 } 237 248 … … 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 } … … 373 398 } 374 399 375 400 if (array_key_exists('EHLO', $this->server_caps)) { 376 // SMTP extensions are available. Let's try to find a proper authentication method401 // SMTP extensions are available. Let's try to find a proper authentication method 377 402 378 403 if (!array_key_exists('AUTH', $this->server_caps)) { 379 404 $this->setError('Authentication is not allowed at this stage'); … … 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', 'NTLM', '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; 478 case 'NTLM': 479 /* 480 * ntlm_sasl_client.php 481 * Bundled with Permission 482 * 483 * How to telnet in windows: 484 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx 485 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication 486 */ 487 require_once 'extras/ntlm_sasl_client.php'; 488 $temp = new stdClass; 489 $ntlm_client = new ntlm_sasl_client_class; 490 //Check that functions are available 491 if (!$ntlm_client->initialize($temp)) { 492 $this->setError($temp->error); 493 $this->edebug( 494 'You need to enable some modules in your php.ini file: ' 495 . $this->error['error'], 496 self::DEBUG_CLIENT 497 ); 498 return false; 499 } 500 //msg1 501 $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1 502 503 if (!$this->sendCommand( 504 'AUTH NTLM', 505 'AUTH NTLM ' . base64_encode($msg1), 506 334 507 ) 508 ) { 509 return false; 510 } 511 //Though 0 based, there is a white space after the 3 digit number 512 //msg2 513 $challenge = substr($this->last_reply, 3); 514 $challenge = base64_decode($challenge); 515 $ntlm_res = $ntlm_client->NTLMResponse( 516 substr($challenge, 24, 8), 517 $password 518 ); 519 //msg3 520 $msg3 = $ntlm_client->typeMsg3( 521 $ntlm_res, 522 $username, 523 $realm, 524 $workstation 525 ); 526 // send encoded username 527 return $this->sendCommand('Username', base64_encode($msg3), 235); 440 528 case 'CRAM-MD5': 441 529 // Start authentication 442 530 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { … … 673 761 protected function parseHelloFields($type) 674 762 { 675 763 $this->server_caps = array(); 676 $lines = explode("\n", $this-> last_reply);764 $lines = explode("\n", $this->helo_rply); 677 765 678 766 foreach ($lines as $n => $s) { 679 767 //First 4 chars contain response code followed by - or space … … 1115 1203 { 1116 1204 return $this->Timeout; 1117 1205 } 1118 } 1206 1207 /** 1208 * Reports an error number and string. 1209 * @param integer $errno The error number returned by PHP. 1210 * @param string $errmsg The error message returned by PHP. 1211 */ 1212 protected function errorHandler($errno, $errmsg) 1213 { 1214 $notice = 'Connection: Failed to connect to server.'; 1215 $this->setError( 1216 $notice, 1217 $errno, 1218 $errmsg 1219 ); 1220 $this->edebug( 1221 $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, 1222 self::DEBUG_CONNECTION 1223 ); 1224 } 1225 1226 /** 1227 * Will return the ID of the last smtp transaction based on a list of patterns provided 1228 * in SMTP::$smtp_transaction_id_patterns. 1229 * If no reply has been received yet, it will return null. 1230 * If no pattern has been matched, it will return false. 1231 * @return bool|null|string 1232 */ 1233 public function getLastTransactionID() 1234 { 1235 $reply = $this->getLastReply(); 1236 1237 if (empty($reply)) { 1238 return null; 1239 } 1240 1241 foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { 1242 if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { 1243 return $matches[1]; 1244 } 1245 } 1246 1247 return false; 1248 } 1249 } 1250 No newline at end of file -
wp-includes/class-phpmailer.php
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
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 = ''; … … 419 423 */ 420 424 public $DKIM_private = ''; 421 425 426 /** 427 * DKIM private key string. 428 * If set, takes precedence over `$DKIM_private`. 429 * @var string 430 */ 431 public $DKIM_private_string = ''; 432 422 433 /** 423 434 * Callback Action function name. 424 435 * … … 446 457 */ 447 458 public $XMailer = ''; 448 459 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 449 469 /** 450 470 * An instance of the SMTP sender class. 451 471 * @var SMTP … … 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). … … 704 726 case 'html': 705 727 //Cleans up output a bit for a better looking, HTML-safe output 706 728 echo htmlentities( 707 preg_replace('/[\r\n]+/', '', $str),708 ENT_QUOTES,709 'UTF-8'710 )711 . "<br>\n";729 preg_replace('/[\r\n]+/', '', $str), 730 ENT_QUOTES, 731 'UTF-8' 732 ) 733 . "<br>\n"; 712 734 break; 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 "\n",719 "\n \t ",720 trim($str)721 ) . "\n";740 "\n", 741 "\n \t ", 742 trim($str) 743 ) . "\n"; 722 744 } 723 745 } 724 746 … … 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) { … … 923 945 return false; 924 946 } 925 947 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 926 1003 /** 927 1004 * Set the From and FromName properties. 928 1005 * @param string $address … … 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; … … 1101 1189 if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { 1102 1190 $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); 1103 1191 if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? 1104 idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :1105 idn_to_ascii($domain)) !== false) {1192 idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : 1193 idn_to_ascii($domain)) !== false) { 1106 1194 return substr($address, 0, $pos) . $punycode; 1107 1195 } 1108 1196 } … … 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 require_once( 'class-smtp.php' );1389 1478 $this->smtp = new SMTP; 1390 1479 } 1391 1480 return $this->smtp; … … 1409 1498 if (!$this->smtpConnect($this->SMTPOptions)) { 1410 1499 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); 1411 1500 } 1412 if ('' == $this->Sender) { 1413 $smtp_from = $this->From; 1414 } else { 1501 if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { 1415 1502 $smtp_from = $this->Sender; 1503 } else { 1504 $smtp_from = $this->From; 1416 1505 } 1417 1506 if (!$this->smtp->mail($smtp_from)) { 1418 1507 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); … … 1466 1555 * @throws phpmailerException 1467 1556 * @return boolean 1468 1557 */ 1469 public function smtpConnect($options = array())1558 public function smtpConnect($options = null) 1470 1559 { 1471 1560 if (is_null($this->smtp)) { 1472 1561 $this->smtp = $this->getSMTPInstance(); 1473 1562 } 1474 1563 1564 //If no options are provided, use whatever is set in the instance 1565 if (is_null($options)) { 1566 $options = $this->SMTPOptions; 1567 } 1568 1475 1569 // Already connected? 1476 1570 if ($this->smtp->connected()) { 1477 1571 return true; … … 1541 1635 if (!$this->smtp->startTLS()) { 1542 1636 throw new phpmailerException($this->lang('connect_host')); 1543 1637 } 1544 // We must resend HELO after tlsnegotiation1638 // We must resend EHLO after TLS negotiation 1545 1639 $this->smtp->hello($hello); 1546 1640 } 1547 1641 if ($this->SMTPAuth) { … … 1580 1674 */ 1581 1675 public function smtpClose() 1582 1676 { 1583 if ( $this->smtp !== null) {1677 if (is_a($this->smtp, 'SMTP')) { 1584 1678 if ($this->smtp->connected()) { 1585 1679 $this->smtp->quit(); 1586 1680 $this->smtp->close(); … … 1599 1693 */ 1600 1694 public function setLanguage($langcode = 'en', $lang_path = '') 1601 1695 { 1696 // Backwards compatibility for renamed language codes 1697 $renamed_langcodes = array( 1698 'br' => 'pt_br', 1699 'cz' => 'cs', 1700 'dk' => 'da', 1701 'no' => 'nb', 1702 'se' => 'sv', 1703 ); 1704 1705 if (isset($renamed_langcodes[$langcode])) { 1706 $langcode = $renamed_langcodes[$langcode]; 1707 } 1708 1602 1709 // Define full set of translatable strings in English 1603 1710 $PHPMAILER_LANG = array( 1604 1711 'authenticate' => 'SMTP Error: Could not authenticate.', … … 1625 1732 // Calculate an absolute path so it can work if CWD is not here 1626 1733 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; 1627 1734 } 1735 //Validate $langcode 1736 if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { 1737 $langcode = 'en'; 1738 } 1628 1739 $foundlang = true; 1629 1740 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; 1630 1741 // There is no English translation file … … 1683 1794 return $this->secureHeader($addr[0]); 1684 1795 } else { 1685 1796 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( 1686 $addr[0]1687 ) . '>';1797 $addr[0] 1798 ) . '>'; 1688 1799 } 1689 1800 } 1690 1801 … … 1918 2029 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); 1919 2030 } 1920 2031 1921 if ($this->MessageID != '') { 2032 // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 2033 // https://tools.ietf.org/html/rfc5322#section-3.6.4 2034 if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { 1922 2035 $this->lastMessageID = $this->MessageID; 1923 2036 } else { 1924 2037 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); … … 2020 2133 */ 2021 2134 public function getSentMIMEMessage() 2022 2135 { 2023 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; 2136 return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; 2137 } 2138 2139 /** 2140 * Create unique ID 2141 * @return string 2142 */ 2143 protected function generateId() { 2144 return md5(uniqid(time())); 2024 2145 } 2025 2146 2026 2147 /** … … 2034 2155 { 2035 2156 $body = ''; 2036 2157 //Create unique IDs and preset boundaries 2037 $this->uniqueid = md5(uniqid(time()));2158 $this->uniqueid = $this->generateId(); 2038 2159 $this->boundary[1] = 'b1_' . $this->uniqueid; 2039 2160 $this->boundary[2] = 'b2_' . $this->uniqueid; 2040 2161 $this->boundary[3] = 'b3_' . $this->uniqueid; … … 2050 2171 //Can we do a 7-bit downgrade? 2051 2172 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { 2052 2173 $bodyEncoding = '7bit'; 2174 //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit 2053 2175 $bodyCharSet = 'us-ascii'; 2054 2176 } 2055 //If lines are too long, change to quoted-printable transfer encoding2056 if (self::hasLineLongerThanMax($this->Body)) {2057 $this->Encoding = 'quoted-printable';2177 //If lines are too long, and we're not already using an encoding that will shorten them, 2178 //change to quoted-printable transfer encoding for the body part only 2179 if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { 2058 2180 $bodyEncoding = 'quoted-printable'; 2059 2181 } 2060 2182 … … 2063 2185 //Can we do a 7-bit downgrade? 2064 2186 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { 2065 2187 $altBodyEncoding = '7bit'; 2188 //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit 2066 2189 $altBodyCharSet = 'us-ascii'; 2067 2190 } 2068 //If lines are too long, change to quoted-printable transfer encoding 2069 if (self::hasLineLongerThanMax($this->AltBody)) { 2191 //If lines are too long, and we're not already using an encoding that will shorten them, 2192 //change to quoted-printable transfer encoding for the alt body part only 2193 if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { 2070 2194 $altBodyEncoding = 'quoted-printable'; 2071 2195 } 2072 2196 //Use this as a preamble in all multipart message types … … 2169 2293 $body .= $this->attachAll('attachment', $this->boundary[1]); 2170 2294 break; 2171 2295 default: 2172 // catch case 'plain' and case '' 2173 $body .= $this->encodeString($this->Body, $bodyEncoding); 2296 // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types 2297 //Reset the `Encoding` property in case we changed it for line length reasons 2298 $this->Encoding = $bodyEncoding; 2299 $body .= $this->encodeString($this->Body, $this->Encoding); 2174 2300 break; 2175 2301 } 2176 2302 … … 2276 2402 2277 2403 /** 2278 2404 * Set the message type. 2279 * PHPMailer only supports some preset message types, 2280 * not arbitrary MIME structures. 2405 * PHPMailer only supports some preset message types, not arbitrary MIME structures. 2281 2406 * @access protected 2282 2407 * @return void 2283 2408 */ … … 2295 2420 } 2296 2421 $this->message_type = implode('_', $type); 2297 2422 if ($this->message_type == '') { 2423 //The 'plain' message_type refers to the message having a single body element, not that it is plain-text 2298 2424 $this->message_type = 'plain'; 2299 2425 } 2300 2426 } … … 2610 2736 /** @noinspection PhpMissingBreakStatementInspection */ 2611 2737 case 'comment': 2612 2738 $matchcount = preg_match_all('/[()"]/', $str, $matches); 2613 2739 // Intentional fall-through 2614 2740 case 'text': 2615 2741 default: 2616 2742 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); … … 2781 2907 case 'comment': 2782 2908 // RFC 2047 section 5.2 2783 2909 $pattern = '\(\)"'; 2784 2785 2910 // intentional fall-through 2911 // for this reason we build the $pattern without including delimiters and [] 2786 2912 case 'text': 2787 2913 default: 2788 2914 // RFC 2047 section 5.1 … … 3209 3335 } 3210 3336 3211 3337 /** 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 3338 * Create a message body from an HTML string. 3339 * Automatically inlines images and creates a plain-text version by converting the HTML, 3340 * overwriting any existing values in Body and AltBody. 3341 * $basedir is used when handling relative image paths, e.g. <img src="images/a.png"> 3342 * will look for an image file in $basedir/images/a.png and convert it to inline. 3343 * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself. 3216 3344 * @access public 3217 3345 * @param string $message HTML message string 3218 * @param string $basedir base line directory for path3346 * @param string $basedir base directory for relative paths to images 3219 3347 * @param boolean|callable $advanced Whether to use the internal HTML to text converter 3220 3348 * or your own custom converter @see PHPMailer::html2text() 3221 * @return string $message 3349 * @return string $message The transformed message Body 3222 3350 */ 3223 3351 public function msgHTML($message, $basedir = '', $advanced = false) 3224 3352 { … … 3241 3369 $message 3242 3370 ); 3243 3371 } 3244 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[ A-z]+://#', $url)) {3372 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { 3245 3373 // Do not change urls for absolute images (thanks to corvuscorax) 3246 3374 // Do not change urls that are already inline images 3247 3375 $filename = basename($url); … … 3277 3405 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better 3278 3406 $this->Body = $this->normalizeBreaks($message); 3279 3407 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); 3280 if ( empty($this->AltBody)) {3408 if (!$this->alternativeExists()) { 3281 3409 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . 3282 3410 self::CRLF . self::CRLF; 3283 3411 } … … 3288 3416 * Convert an HTML string into plain text. 3289 3417 * This is used by msgHTML(). 3290 3418 * Note - older versions of this function used a bundled advanced converter 3291 * which was been removed for license reasons in #232 3419 * which was been removed for license reasons in #232. 3292 3420 * Example usage: 3293 3421 * <code> 3294 3422 * // Use default conversion … … 3588 3716 * @access public 3589 3717 * @param string $signHeader 3590 3718 * @throws phpmailerException 3591 * @return string 3719 * @return string The DKIM signature value 3592 3720 */ 3593 3721 public function DKIM_Sign($signHeader) 3594 3722 { … … 3598 3726 } 3599 3727 return ''; 3600 3728 } 3601 $privKeyStr = file_get_contents($this->DKIM_private);3602 if ( $this->DKIM_passphrase != '') {3729 $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private); 3730 if ('' != $this->DKIM_passphrase) { 3603 3731 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); 3604 3732 } else { 3605 $privKey = $privKeyStr;3733 $privKey = openssl_pkey_get_private($privKeyStr); 3606 3734 } 3607 if (openssl_sign($signHeader, $signature, $privKey)) { 3608 return base64_encode($signature); 3735 //Workaround for missing digest algorithms in old PHP & OpenSSL versions 3736 //@link http://stackoverflow.com/a/11117338/333340 3737 if (version_compare(PHP_VERSION, '5.3.0') >= 0 and 3738 in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { 3739 if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { 3740 openssl_pkey_free($privKey); 3741 return base64_encode($signature); 3742 } 3743 } else { 3744 $pinfo = openssl_pkey_get_details($privKey); 3745 $hash = hash('sha256', $signHeader); 3746 //'Magic' constant for SHA256 from RFC3447 3747 //@link https://tools.ietf.org/html/rfc3447#page-43 3748 $t = '3031300d060960864801650304020105000420' . $hash; 3749 $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); 3750 $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); 3751 3752 if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { 3753 openssl_pkey_free($privKey); 3754 return base64_encode($signature); 3755 } 3609 3756 } 3757 openssl_pkey_free($privKey); 3610 3758 return ''; 3611 3759 } 3612 3760 … … 3623 3771 foreach ($lines as $key => $line) { 3624 3772 list($heading, $value) = explode(':', $line, 2); 3625 3773 $heading = strtolower($heading); 3626 $value = preg_replace('/\s +/', ' ', $value); // Compress useless spaces3774 $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces 3627 3775 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value 3628 3776 } 3629 3777 $signHeader = implode("\r\n", $lines); … … 3661 3809 */ 3662 3810 public function DKIM_Add($headers_line, $subject, $body) 3663 3811 { 3664 $DKIMsignatureType = 'rsa-sha 1'; // Signature & hash algorithms3812 $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms 3665 3813 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body 3666 3814 $DKIMquery = 'dns/txt'; // Query method 3667 3815 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) … … 3669 3817 $headers = explode($this->LE, $headers_line); 3670 3818 $from_header = ''; 3671 3819 $to_header = ''; 3820 $date_header = ''; 3672 3821 $current = ''; 3673 3822 foreach ($headers as $header) { 3674 3823 if (strpos($header, 'From:') === 0) { … … 3677 3826 } elseif (strpos($header, 'To:') === 0) { 3678 3827 $to_header = $header; 3679 3828 $current = 'to_header'; 3829 } elseif (strpos($header, 'Date:') === 0) { 3830 $date_header = $header; 3831 $current = 'date_header'; 3680 3832 } else { 3681 3833 if (!empty($$current) && strpos($header, ' =?') === 0) { 3682 3834 $$current .= $header; … … 3687 3839 } 3688 3840 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); 3689 3841 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); 3842 $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); 3690 3843 $subject = str_replace( 3691 3844 '|', 3692 3845 '=7C', … … 3694 3847 ); // Copied header fields (dkim-quoted-printable) 3695 3848 $body = $this->DKIM_BodyC($body); 3696 3849 $DKIMlen = strlen($body); // Length of body 3697 $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1hash of body3850 $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body 3698 3851 if ('' == $this->DKIM_identity) { 3699 3852 $ident = ''; 3700 3853 } else { … … 3707 3860 $this->DKIM_selector . 3708 3861 ";\r\n" . 3709 3862 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . 3710 "\th=From:To: Subject;\r\n" .3863 "\th=From:To:Date:Subject;\r\n" . 3711 3864 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . 3712 3865 "\tz=$from\r\n" . 3713 3866 "\t|$to\r\n" . 3867 "\t|$date\r\n" . 3714 3868 "\t|$subject;\r\n" . 3715 3869 "\tbh=" . $DKIMb64 . ";\r\n" . 3716 3870 "\tb="; 3717 3871 $toSign = $this->DKIM_HeaderC( 3718 3872 $from_header . "\r\n" . 3719 3873 $to_header . "\r\n" . 3874 $date_header . "\r\n" . 3720 3875 $subject_header . "\r\n" . 3721 3876 $dkimhdrs 3722 3877 ); … … 3825 3980 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n"; 3826 3981 return $errorMsg; 3827 3982 } 3828 } 3983 } 3984 No newline at end of file