Changeset 33124 for trunk/src/wp-includes/class-smtp.php
- Timestamp:
- 07/08/2015 05:15:02 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-smtp.php
r29783 r33124 2 2 /** 3 3 * PHPMailer RFC821 SMTP email transport class. 4 * Version 5.2.7 5 * PHP version 5.0.0 6 * @category PHP 7 * @package PHPMailer 8 * @link https://github.com/PHPMailer/PHPMailer/ 9 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> 4 * PHP Version 5 5 * @package PHPMailer 6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project 7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> 10 8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> 11 9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> 12 * @ copyright 2013 Marcus Bointon13 * @copyright 20 04 - 2008 Andy Prevost10 * @author Brent R. Matzelle (original founder) 11 * @copyright 2014 Marcus Bointon 14 12 * @copyright 2010 - 2012 Jim Jagielski 15 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) 13 * @copyright 2004 - 2009 Andy Prevost 14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 15 * @note This program is distributed in the hope that it will be useful - WITHOUT 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 * FITNESS FOR A PARTICULAR PURPOSE. 16 18 */ 17 19 18 20 /** 19 21 * PHPMailer RFC821 SMTP email transport class. 20 * 21 * Implements RFC 821 SMTP commands 22 * and provides some utility methods for sending mail to an SMTP server. 23 * 24 * PHP Version 5.0.0 25 * 26 * @category PHP 27 * @package PHPMailer 28 * @link https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php 29 * @author Chris Ryan <unknown@example.com> 30 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> 31 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) 22 * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. 23 * @package PHPMailer 24 * @author Chris Ryan 25 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> 32 26 */ 33 34 27 class SMTP 35 28 { 36 29 /** 37 * The PHPMailer SMTP Version number. 38 */ 39 const VERSION = '5.2.7'; 30 * The PHPMailer SMTP version number. 31 * @type string 32 */ 33 const VERSION = '5.2.10'; 40 34 41 35 /** 42 36 * SMTP line break constant. 37 * @type string 43 38 */ 44 39 const CRLF = "\r\n"; … … 46 41 /** 47 42 * The SMTP port to use if one is not specified. 43 * @type integer 48 44 */ 49 45 const DEFAULT_SMTP_PORT = 25; 46 47 /** 48 * The maximum line length allowed by RFC 2822 section 2.1.1 49 * @type integer 50 */ 51 const MAX_LINE_LENGTH = 998; 52 53 /** 54 * Debug level for no output 55 */ 56 const DEBUG_OFF = 0; 57 58 /** 59 * Debug level to show client -> server messages 60 */ 61 const DEBUG_CLIENT = 1; 62 63 /** 64 * Debug level to show client -> server and server -> client messages 65 */ 66 const DEBUG_SERVER = 2; 67 68 /** 69 * Debug level to show connection status, client -> server and server -> client messages 70 */ 71 const DEBUG_CONNECTION = 3; 72 73 /** 74 * Debug level to show all messages 75 */ 76 const DEBUG_LOWLEVEL = 4; 50 77 51 78 /** 52 79 * The PHPMailer SMTP Version number. 53 80 * @type string 54 * @deprecated This should be a constant81 * @deprecated Use the `VERSION` constant instead 55 82 * @see SMTP::VERSION 56 83 */ 57 public $Version = '5.2. 7';84 public $Version = '5.2.10'; 58 85 59 86 /** 60 87 * SMTP server port number. 61 * @type int 62 * @deprecated This is only ever u ed as default value, so should be a constant88 * @type integer 89 * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead 63 90 * @see SMTP::DEFAULT_SMTP_PORT 64 91 */ … … 66 93 67 94 /** 68 * SMTP reply line ending 95 * SMTP reply line ending. 69 96 * @type string 70 * @deprecated Use the classconstant instead97 * @deprecated Use the `CRLF` constant instead 71 98 * @see SMTP::CRLF 72 99 */ … … 75 102 /** 76 103 * Debug output level. 77 * Options: 0 for no output, 1 for commands, 2 for data and commands 78 * @type int 79 */ 80 public $do_debug = 0; 81 82 /** 83 * The function/method to use for debugging output. 84 * Options: 'echo', 'html' or 'error_log' 85 * @type string 104 * Options: 105 * * self::DEBUG_OFF (`0`) No debug output, default 106 * * self::DEBUG_CLIENT (`1`) Client commands 107 * * self::DEBUG_SERVER (`2`) Client commands and server responses 108 * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status 109 * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages 110 * @type integer 111 */ 112 public $do_debug = self::DEBUG_OFF; 113 114 /** 115 * How to handle debug output. 116 * Options: 117 * * `echo` Output plain-text as-is, appropriate for CLI 118 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output 119 * * `error_log` Output to error log as configured in php.ini 120 * 121 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 122 * <code> 123 * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; 124 * </code> 125 * @type string|callable 86 126 */ 87 127 public $Debugoutput = 'echo'; … … 89 129 /** 90 130 * Whether to use VERP. 91 * @type bool 131 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path 132 * @link http://www.postfix.org/VERP_README.html Info on VERP 133 * @type boolean 92 134 */ 93 135 public $do_verp = false; 94 136 95 137 /** 96 * The SMTP timeout value for reads, in seconds. 97 * @type int 98 */ 99 public $Timeout = 15; 100 101 /** 102 * The SMTP timelimit value for reads, in seconds. 103 * @type int 104 */ 105 public $Timelimit = 30; 138 * The timeout value for connection, in seconds. 139 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 140 * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. 141 * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 142 * @type integer 143 */ 144 public $Timeout = 300; 145 146 /** 147 * How long to wait for commands to complete, in seconds. 148 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 149 * @type integer 150 */ 151 public $Timelimit = 300; 106 152 107 153 /** … … 112 158 113 159 /** 114 * Error message, if any, for the last call. 115 * @type string 116 */ 117 protected $error = ''; 160 * Error information, if any, for the last SMTP command. 161 * @type array 162 */ 163 protected $error = array( 164 'error' => '', 165 'detail' => '', 166 'smtp_code' => '', 167 'smtp_code_ex' => '' 168 ); 118 169 119 170 /** 120 171 * The reply the server sent to us for HELO. 121 * @type string 122 */ 123 protected $helo_rply = ''; 172 * If null, no HELO string has yet been received. 173 * @type string|null 174 */ 175 protected $helo_rply = null; 176 177 /** 178 * The set of SMTP extensions sent in reply to EHLO command. 179 * Indexes of the array are extension names. 180 * Value at index 'HELO' or 'EHLO' (according to command that was sent) 181 * represents the server name. In case of HELO it is the only element of the array. 182 * Other values can be boolean TRUE or an array containing extension options. 183 * If null, no HELO/EHLO string has yet been received. 184 * @type array|null 185 */ 186 protected $server_caps = null; 124 187 125 188 /** … … 130 193 131 194 /** 132 * Constructor.133 * @access public134 */135 public function __construct()136 {137 $this->smtp_conn = 0;138 $this->error = null;139 $this->helo_rply = null;140 141 $this->do_debug = 0;142 }143 144 /**145 195 * Output debugging info via a user-selected method. 196 * @see SMTP::$Debugoutput 197 * @see SMTP::$do_debug 146 198 * @param string $str Debug string to output 199 * @param integer $level The debug level of this message; see DEBUG_* constants 147 200 * @return void 148 201 */ 149 protected function edebug($str) 150 { 202 protected function edebug($str, $level = 0) 203 { 204 if ($level > $this->do_debug) { 205 return; 206 } 207 //Avoid clash with built-in function names 208 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); 210 return; 211 } 151 212 switch ($this->Debugoutput) { 152 213 case 'error_log': … … 165 226 case 'echo': 166 227 default: 167 //Just echoes whatever was received 168 echo $str; 228 //Normalize line breaks 229 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 230 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 231 "\n", 232 "\n \t ", 233 trim($str) 234 )."\n"; 169 235 } 170 236 } … … 172 238 /** 173 239 * Connect to an SMTP server. 174 * @param string $host 175 * @param int $portThe port number to connect to176 * @param int $timeout How long to wait for the connection to open240 * @param string $host SMTP server IP or host name 241 * @param integer $port The port number to connect to 242 * @param integer $timeout How long to wait for the connection to open 177 243 * @param array $options An array of options for stream_context_create() 178 244 * @access public 179 * @return bool 245 * @return boolean 180 246 */ 181 247 public function connect($host, $port = null, $timeout = 30, $options = array()) 182 248 { 249 static $streamok; 250 //This is enabled by default since 5.0.0 but some providers disable it 251 //Check this once and cache the result 252 if (is_null($streamok)) { 253 $streamok = function_exists('stream_socket_client'); 254 } 183 255 // Clear errors to avoid confusion 184 $this->error = null; 185 256 $this->setError(''); 186 257 // Make sure we are __not__ connected 187 258 if ($this->connected()) { 188 259 // Already connected, generate error 189 $this-> error = array('error' =>'Already connected to a server');260 $this->setError('Already connected to a server'); 190 261 return false; 191 262 } 192 193 263 if (empty($port)) { 194 264 $port = self::DEFAULT_SMTP_PORT; 195 265 } 196 197 266 // Connect to the SMTP server 267 $this->edebug( 268 "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), 269 self::DEBUG_CONNECTION 270 ); 198 271 $errno = 0; 199 272 $errstr = ''; 200 $socket_context = stream_context_create($options); 201 //Suppress errors; connection failures are handled at a higher level 202 $this->smtp_conn = @stream_socket_client( 203 $host . ":" . $port, 204 $errno, 205 $errstr, 206 $timeout, 207 STREAM_CLIENT_CONNECT, 208 $socket_context 209 ); 210 273 if ($streamok) { 274 $socket_context = stream_context_create($options); 275 //Suppress errors; connection failures are handled at a higher level 276 $this->smtp_conn = @stream_socket_client( 277 $host . ":" . $port, 278 $errno, 279 $errstr, 280 $timeout, 281 STREAM_CLIENT_CONNECT, 282 $socket_context 283 ); 284 } else { 285 //Fall back to fsockopen which should work in more places, but is missing some features 286 $this->edebug( 287 "Connection: stream_socket_client not available, falling back to fsockopen", 288 self::DEBUG_CONNECTION 289 ); 290 $this->smtp_conn = fsockopen( 291 $host, 292 $port, 293 $errno, 294 $errstr, 295 $timeout 296 ); 297 } 211 298 // Verify we connected properly 212 if ( empty($this->smtp_conn)) {213 $this-> error = array(214 ' error' => 'Failed to connect to server',215 'errno' =>$errno,216 'errstr' =>$errstr299 if (!is_resource($this->smtp_conn)) { 300 $this->setError( 301 'Failed to connect to server', 302 $errno, 303 $errstr 217 304 ); 218 if ($this->do_debug >= 1) { 219 $this->edebug( 220 'SMTP -> ERROR: ' . $this->error['error'] 221 . ": $errstr ($errno)" 222 ); 223 } 305 $this->edebug( 306 'SMTP ERROR: ' . $this->error['error'] 307 . ": $errstr ($errno)", 308 self::DEBUG_CLIENT 309 ); 224 310 return false; 225 311 } 226 312 $this->edebug('Connection: opened', self::DEBUG_CONNECTION); 227 313 // SMTP server can take longer to respond, give longer timeout for first read 228 314 // Windows does not have support for this timeout function 229 315 if (substr(PHP_OS, 0, 3) != 'WIN') { 230 316 $max = ini_get('max_execution_time'); 231 if ($max != 0 && $timeout > $max) { // Don't bother if unlimited 317 // Don't bother if unlimited 318 if ($max != 0 && $timeout > $max) { 232 319 @set_time_limit($timeout); 233 320 } 234 321 stream_set_timeout($this->smtp_conn, $timeout, 0); 235 322 } 236 237 323 // Get any announcement 238 324 $announce = $this->get_lines(); 239 240 if ($this->do_debug >= 2) { 241 $this->edebug('SMTP -> FROM SERVER:' . $announce); 242 } 243 325 $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); 244 326 return true; 245 327 } … … 248 330 * Initiate a TLS (encrypted) session. 249 331 * @access public 250 * @return bool 332 * @return boolean 251 333 */ 252 334 public function startTLS() 253 335 { 254 if (!$this->sendCommand( "STARTTLS", "STARTTLS", 220)) {336 if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { 255 337 return false; 256 338 } … … 260 342 true, 261 343 STREAM_CRYPTO_METHOD_TLS_CLIENT 262 ) 263 ) { 344 )) { 264 345 return false; 265 346 } … … 277 358 * @param string $workstation The auth workstation for NTLM 278 359 * @access public 279 * @return bool True if successfully authenticated.360 * @return boolean True if successfully authenticated. 280 361 */ 281 362 public function authenticate( 282 363 $username, 283 364 $password, 284 $authtype = 'LOGIN',365 $authtype = null, 285 366 $realm = '', 286 367 $workstation = '' 287 368 ) { 288 if (empty($authtype)) { 369 if (!$this->server_caps) { 370 $this->setError('Authentication is not allowed before HELO/EHLO'); 371 return false; 372 } 373 374 if (array_key_exists('EHLO', $this->server_caps)) { 375 // SMTP extensions are available. Let's try to find a proper authentication method 376 377 if (!array_key_exists('AUTH', $this->server_caps)) { 378 $this->setError('Authentication is not allowed at this stage'); 379 // 'at this stage' means that auth may be allowed after the stage changes 380 // e.g. after STARTTLS 381 return false; 382 } 383 384 self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); 385 self::edebug( 386 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), 387 self::DEBUG_LOWLEVEL 388 ); 389 390 if (empty($authtype)) { 391 foreach (array('LOGIN', 'CRAM-MD5', 'PLAIN') as $method) { 392 if (in_array($method, $this->server_caps['AUTH'])) { 393 $authtype = $method; 394 break; 395 } 396 } 397 if (empty($authtype)) { 398 $this->setError('No supported authentication methods found'); 399 return false; 400 } 401 self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); 402 } 403 404 if (!in_array($authtype, $this->server_caps['AUTH'])) { 405 $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); 406 return false; 407 } 408 } elseif (empty($authtype)) { 289 409 $authtype = 'LOGIN'; 290 410 } 291 292 411 switch ($authtype) { 293 412 case 'PLAIN': … … 318 437 } 319 438 break; 320 case 'NTLM':321 /*322 * ntlm_sasl_client.php323 * Bundled with Permission324 *325 * How to telnet in windows:326 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx327 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication328 */329 require_once 'extras/ntlm_sasl_client.php';330 $temp = new stdClass();331 $ntlm_client = new ntlm_sasl_client_class;332 //Check that functions are available333 if (!$ntlm_client->Initialize($temp)) {334 $this->error = array('error' => $temp->error);335 if ($this->do_debug >= 1) {336 $this->edebug(337 'You need to enable some modules in your php.ini file: '338 . $this->error['error']339 );340 }341 return false;342 }343 //msg1344 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1345 346 if (!$this->sendCommand(347 'AUTH NTLM',348 'AUTH NTLM ' . base64_encode($msg1),349 334350 )351 ) {352 return false;353 }354 355 //Though 0 based, there is a white space after the 3 digit number356 //msg2357 $challenge = substr($this->last_reply, 3);358 $challenge = base64_decode($challenge);359 $ntlm_res = $ntlm_client->NTLMResponse(360 substr($challenge, 24, 8),361 $password362 );363 //msg3364 $msg3 = $ntlm_client->TypeMsg3(365 $ntlm_res,366 $username,367 $realm,368 $workstation369 );370 // send encoded username371 return $this->sendCommand('Username', base64_encode($msg3), 235);372 break;373 439 case 'CRAM-MD5': 374 440 // Start authentication … … 384 450 // send encoded credentials 385 451 return $this->sendCommand('Username', base64_encode($response), 235); 386 break; 452 default: 453 $this->setError("Authentication method \"$authtype\" is not supported"); 454 return false; 387 455 } 388 456 return true; … … 412 480 // by Lance Rushing 413 481 414 $b = 64; // byte length for md5415 if (strlen($key) > $b ) {482 $bytelen = 64; // byte length for md5 483 if (strlen($key) > $bytelen) { 416 484 $key = pack('H*', md5($key)); 417 485 } 418 $key = str_pad($key, $b , chr(0x00));419 $ipad = str_pad('', $b , chr(0x36));420 $opad = str_pad('', $b , chr(0x5c));486 $key = str_pad($key, $bytelen, chr(0x00)); 487 $ipad = str_pad('', $bytelen, chr(0x36)); 488 $opad = str_pad('', $bytelen, chr(0x5c)); 421 489 $k_ipad = $key ^ $ipad; 422 490 $k_opad = $key ^ $opad; … … 428 496 * Check connection state. 429 497 * @access public 430 * @return bool True if connected.498 * @return boolean True if connected. 431 499 */ 432 500 public function connected() 433 501 { 434 if ( !empty($this->smtp_conn)) {502 if (is_resource($this->smtp_conn)) { 435 503 $sock_status = stream_get_meta_data($this->smtp_conn); 436 504 if ($sock_status['eof']) { 437 // the socket is valid but we are not connected 438 if ($this->do_debug >= 1) { 439 $this->edebug( 440 'SMTP -> NOTICE: EOF caught while checking if connected' 441 ); 442 } 505 // The socket is valid but we are not connected 506 $this->edebug( 507 'SMTP NOTICE: EOF caught while checking if connected', 508 self::DEBUG_CLIENT 509 ); 443 510 $this->close(); 444 511 return false; … … 458 525 public function close() 459 526 { 460 $this->error = null; // so there is no confusion 527 $this->setError(''); 528 $this->server_caps = null; 461 529 $this->helo_rply = null; 462 if ( !empty($this->smtp_conn)) {530 if (is_resource($this->smtp_conn)) { 463 531 // close the connection and cleanup 464 532 fclose($this->smtp_conn); 465 $this->smtp_conn = 0; 533 $this->smtp_conn = null; //Makes for cleaner serialization 534 $this->edebug('Connection: closed', self::DEBUG_CONNECTION); 466 535 } 467 536 } … … 477 546 * @param string $msg_data Message data to send 478 547 * @access public 479 * @return bool 548 * @return boolean 480 549 */ 481 550 public function data($msg_data) 482 551 { 552 //This will use the standard timelimit 483 553 if (!$this->sendCommand('DATA', 'DATA', 354)) { 484 554 return false; … … 486 556 487 557 /* The server is ready to accept data! 488 * according to rfc821 we should not send more than 1000 489 * including the CRLF 490 * characters on a single line so we will break the data up 491 * into lines by \r and/or \n then if needed we will break 492 * each of those into smaller lines to fit within the limit. 493 * in addition we will be looking for lines that start with 494 * a period '.' and append and additional period '.' to that 495 * line. NOTE: this does not count towards limit. 558 * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) 559 * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into 560 * smaller lines to fit within the limit. 561 * We will also look for lines that start with a '.' and prepend an additional '.'. 562 * NOTE: this does not count towards line-length limit. 496 563 */ 497 564 498 // Normalize the line breaks before exploding 499 $msg_data = str_replace("\r\n", "\n", $msg_data); 500 $msg_data = str_replace("\r", "\n", $msg_data); 501 $lines = explode("\n", $msg_data); 502 503 /* We need to find a good way to determine if headers are 504 * in the msg_data or if it is a straight msg body 505 * currently I am assuming rfc822 definitions of msg headers 506 * and if the first field of the first line (':' separated) 507 * does not contain a space then it _should_ be a header 508 * and we can process all lines before a blank "" line as 509 * headers. 565 // Normalize line breaks before exploding 566 $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); 567 568 /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field 569 * of the first line (':' separated) does not contain a space then it _should_ be a header and we will 570 * process all lines before a blank line as headers. 510 571 */ 511 572 512 573 $field = substr($lines[0], 0, strpos($lines[0], ':')); 513 574 $in_headers = false; 514 if (!empty($field) && !strstr($field, ' ')) {575 if (!empty($field) && strpos($field, ' ') === false) { 515 576 $in_headers = true; 516 577 } 517 578 518 //RFC 2822 section 2.1.1 limit519 $max_line_length = 998;520 521 579 foreach ($lines as $line) { 522 $lines_out = null;523 if ($ line == '' && $in_headers) {580 $lines_out = array(); 581 if ($in_headers and $line == '') { 524 582 $in_headers = false; 525 583 } 526 // ok we need to break this line up into several smaller lines 527 while (strlen($line) > $max_line_length) { 528 $pos = strrpos(substr($line, 0, $max_line_length), ' '); 529 530 // Patch to fix DOS attack 584 //Break this line up into several smaller lines if it's too long 585 //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), 586 while (isset($line[self::MAX_LINE_LENGTH])) { 587 //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on 588 //so as to avoid breaking in the middle of a word 589 $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); 590 //Deliberately matches both false and 0 531 591 if (!$pos) { 532 $pos = $max_line_length - 1; 592 //No nice break found, add a hard break 593 $pos = self::MAX_LINE_LENGTH - 1; 533 594 $lines_out[] = substr($line, 0, $pos); 534 595 $line = substr($line, $pos); 535 596 } else { 597 //Break at the found point 536 598 $lines_out[] = substr($line, 0, $pos); 599 //Move along by the amount we dealt with 537 600 $line = substr($line, $pos + 1); 538 601 } 539 540 /* If processing headers add a LWSP-char to the front of new line 541 * rfc822 on long msg headers 542 */ 602 //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 543 603 if ($in_headers) { 544 604 $line = "\t" . $line; … … 547 607 $lines_out[] = $line; 548 608 549 // send the lines to the server 550 while (list(, $line_out) = @each($lines_out)) { 551 if (strlen($line_out) > 0) { 552 if (substr($line_out, 0, 1) == '.') { 553 $line_out = '.' . $line_out; 554 } 609 //Send the lines to the server 610 foreach ($lines_out as $line_out) { 611 //RFC2821 section 4.5.2 612 if (!empty($line_out) and $line_out[0] == '.') { 613 $line_out = '.' . $line_out; 555 614 } 556 615 $this->client_send($line_out . self::CRLF); … … 558 617 } 559 618 560 // Message data has been sent, complete the command 561 return $this->sendCommand('DATA END', '.', 250); 619 //Message data has been sent, complete the command 620 //Increase timelimit for end of DATA command 621 $savetimelimit = $this->Timelimit; 622 $this->Timelimit = $this->Timelimit * 2; 623 $result = $this->sendCommand('DATA END', '.', 250); 624 //Restore timelimit 625 $this->Timelimit = $savetimelimit; 626 return $result; 562 627 } 563 628 … … 566 631 * Used to identify the sending server to the receiving server. 567 632 * This makes sure that client and server are in a known state. 568 * Implements fromRFC 821: HELO <SP> <domain> <CRLF>633 * Implements RFC 821: HELO <SP> <domain> <CRLF> 569 634 * and RFC 2821 EHLO. 570 635 * @param string $host The host name or IP to connect to 571 636 * @access public 572 * @return bool 637 * @return boolean 573 638 */ 574 639 public function hello($host = '') 575 640 { 576 // Try extended hello first (RFC 2821) 577 if (!$this->sendHello('EHLO', $host)) { 578 if (!$this->sendHello('HELO', $host)) { 579 return false; 580 } 581 } 582 583 return true; 641 //Try extended hello first (RFC 2821) 642 return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); 584 643 } 585 644 … … 589 648 * @see hello() 590 649 * @param string $hello The HELO string 591 * @param string $host 650 * @param string $host The hostname to say we are 592 651 * @access protected 593 * @return bool 652 * @return boolean 594 653 */ 595 654 protected function sendHello($hello, $host) … … 597 656 $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); 598 657 $this->helo_rply = $this->last_reply; 658 if ($noerror) { 659 $this->parseHelloFields($hello); 660 } else { 661 $this->server_caps = null; 662 } 599 663 return $noerror; 664 } 665 666 /** 667 * Parse a reply to HELO/EHLO command to discover server extensions. 668 * In case of HELO, the only parameter that can be discovered is a server name. 669 * @access protected 670 * @param string $type - 'HELO' or 'EHLO' 671 */ 672 protected function parseHelloFields($type) 673 { 674 $this->server_caps = array(); 675 $lines = explode("\n", $this->last_reply); 676 foreach ($lines as $n => $s) { 677 $s = trim(substr($s, 4)); 678 if (!$s) { 679 continue; 680 } 681 $fields = explode(' ', $s); 682 if (!empty($fields)) { 683 if (!$n) { 684 $name = $type; 685 $fields = $fields[0]; 686 } else { 687 $name = array_shift($fields); 688 if ($name == 'SIZE') { 689 $fields = ($fields) ? $fields[0] : 0; 690 } 691 } 692 $this->server_caps[$name] = ($fields ? $fields : true); 693 } 694 } 600 695 } 601 696 … … 609 704 * @param string $from Source address of this message 610 705 * @access public 611 * @return bool 706 * @return boolean 612 707 */ 613 708 public function mail($from) … … 625 720 * Closes the socket if there is no error or the $close_on_error argument is true. 626 721 * Implements from rfc 821: QUIT <CRLF> 627 * @param bool $close_on_error Should the connection close if an error occurs?628 * @access public 629 * @return bool 722 * @param boolean $close_on_error Should the connection close if an error occurs? 723 * @access public 724 * @return boolean 630 725 */ 631 726 public function quit($close_on_error = true) 632 727 { 633 728 $noerror = $this->sendCommand('QUIT', 'QUIT', 221); 634 $e = $this->error; //Save any error729 $err = $this->error; //Save any error 635 730 if ($noerror or $close_on_error) { 636 731 $this->close(); 637 $this->error = $e ; //Restore any error from the quit command732 $this->error = $err; //Restore any error from the quit command 638 733 } 639 734 return $noerror; … … 642 737 /** 643 738 * Send an SMTP RCPT command. 644 * Sets the TO argument to $to .739 * Sets the TO argument to $toaddr. 645 740 * Returns true if the recipient was accepted false if it was rejected. 646 741 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> 647 * @param string $to The address the message is being sent to648 * @access public 649 * @return bool 650 */ 651 public function recipient($to )742 * @param string $toaddr The address the message is being sent to 743 * @access public 744 * @return boolean 745 */ 746 public function recipient($toaddr) 652 747 { 653 748 return $this->sendCommand( 654 'RCPT TO 655 'RCPT TO:<' . $to . '>',749 'RCPT TO', 750 'RCPT TO:<' . $toaddr . '>', 656 751 array(250, 251) 657 752 ); … … 663 758 * Implements rfc 821: RSET <CRLF> 664 759 * @access public 665 * @return bool True on success.760 * @return boolean True on success. 666 761 */ 667 762 public function reset() … … 674 769 * @param string $command The command name - not sent to the server 675 770 * @param string $commandstring The actual command to send 676 * @param int |array $expect One or more expected integer success codes771 * @param integer|array $expect One or more expected integer success codes 677 772 * @access protected 678 * @return bool True on success.773 * @return boolean True on success. 679 774 */ 680 775 protected function sendCommand($command, $commandstring, $expect) 681 776 { 682 777 if (!$this->connected()) { 683 $this->error = array( 684 "error" => "Called $command without being connected" 778 $this->setError("Called $command without being connected"); 779 return false; 780 } 781 $this->client_send($commandstring . self::CRLF); 782 783 $this->last_reply = $this->get_lines(); 784 // Fetch SMTP code and possible error code explanation 785 $matches = array(); 786 if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { 787 $code = $matches[1]; 788 $code_ex = (count($matches) > 2 ? $matches[2] : null); 789 // Cut off error code from each response line 790 $detail = preg_replace( 791 "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", 792 '', 793 $this->last_reply 794 ); 795 } else { 796 // Fall back to simple parsing if regex fails 797 $code = substr($this->last_reply, 0, 3); 798 $code_ex = null; 799 $detail = substr($this->last_reply, 4); 800 } 801 802 $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); 803 804 if (!in_array($code, (array)$expect)) { 805 $this->setError( 806 "$command command failed", 807 $detail, 808 $code, 809 $code_ex 810 ); 811 $this->edebug( 812 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, 813 self::DEBUG_CLIENT 685 814 ); 686 815 return false; 687 816 } 688 $this->client_send($commandstring . self::CRLF); 689 690 $reply = $this->get_lines(); 691 $code = substr($reply, 0, 3); 692 693 if ($this->do_debug >= 2) { 694 $this->edebug('SMTP -> FROM SERVER:' . $reply); 695 } 696 697 if (!in_array($code, (array)$expect)) { 698 $this->last_reply = null; 699 $this->error = array( 700 "error" => "$command command failed", 701 "smtp_code" => $code, 702 "detail" => substr($reply, 4) 703 ); 704 if ($this->do_debug >= 1) { 705 $this->edebug( 706 'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply 707 ); 708 } 709 return false; 710 } 711 712 $this->last_reply = $reply; 713 $this->error = null; 817 818 $this->setError(''); 714 819 return true; 715 820 } … … 726 831 * @param string $from The address the message is from 727 832 * @access public 728 * @return bool 833 * @return boolean 729 834 */ 730 835 public function sendAndMail($from) 731 836 { 732 return $this->sendCommand( "SAML", "SAML FROM:$from", 250);837 return $this->sendCommand('SAML', "SAML FROM:$from", 250); 733 838 } 734 839 … … 737 842 * @param string $name The name to verify 738 843 * @access public 739 * @return bool 844 * @return boolean 740 845 */ 741 846 public function verify($name) 742 847 { 743 return $this->sendCommand( "VRFY", "VRFY $name", array(250, 251));848 return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); 744 849 } 745 850 … … 748 853 * Used to keep keep-alives alive, doesn't actually do anything 749 854 * @access public 750 * @return bool 855 * @return boolean 751 856 */ 752 857 public function noop() 753 858 { 754 return $this->sendCommand( "NOOP", "NOOP", 250);859 return $this->sendCommand('NOOP', 'NOOP', 250); 755 860 } 756 861 … … 758 863 * Send an SMTP TURN command. 759 864 * This is an optional command for SMTP that this class does not support. 760 * This method is here to make the RFC821 Definition 761 * complete for this class and __may__ be implemented in future865 * This method is here to make the RFC821 Definition complete for this class 866 * and _may_ be implemented in future 762 867 * Implements from rfc 821: TURN <CRLF> 763 868 * @access public 764 * @return bool 869 * @return boolean 765 870 */ 766 871 public function turn() 767 872 { 768 $this->error = array( 769 'error' => 'The SMTP TURN command is not implemented' 770 ); 771 if ($this->do_debug >= 1) { 772 $this->edebug('SMTP -> NOTICE: ' . $this->error['error']); 773 } 873 $this->setError('The SMTP TURN command is not implemented'); 874 $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); 774 875 return false; 775 876 } … … 779 880 * @param string $data The data to send 780 881 * @access public 781 * @return int |bool The number of bytes sent to the server or FALSEon error882 * @return integer|boolean The number of bytes sent to the server or false on error 782 883 */ 783 884 public function client_send($data) 784 885 { 785 if ($this->do_debug >= 1) { 786 $this->edebug("CLIENT -> SMTP: $data"); 787 } 886 $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); 788 887 return fwrite($this->smtp_conn, $data); 789 888 } … … 797 896 { 798 897 return $this->error; 898 } 899 900 /** 901 * Get SMTP extensions available on the server 902 * @access public 903 * @return array|null 904 */ 905 public function getServerExtList() 906 { 907 return $this->server_caps; 908 } 909 910 /** 911 * A multipurpose method 912 * The method works in three ways, dependent on argument value and current state 913 * 1. HELO/EHLO was not sent - returns null and set up $this->error 914 * 2. HELO was sent 915 * $name = 'HELO': returns server name 916 * $name = 'EHLO': returns boolean false 917 * $name = any string: returns null and set up $this->error 918 * 3. EHLO was sent 919 * $name = 'HELO'|'EHLO': returns server name 920 * $name = any string: if extension $name exists, returns boolean True 921 * or its options. Otherwise returns boolean False 922 * In other words, one can use this method to detect 3 conditions: 923 * - null returned: handshake was not or we don't know about ext (refer to $this->error) 924 * - false returned: the requested feature exactly not exists 925 * - positive value returned: the requested feature exists 926 * @param string $name Name of SMTP extension or 'HELO'|'EHLO' 927 * @return mixed 928 */ 929 public function getServerExt($name) 930 { 931 if (!$this->server_caps) { 932 $this->setError('No HELO/EHLO was sent'); 933 return null; 934 } 935 936 // the tight logic knot ;) 937 if (!array_key_exists($name, $this->server_caps)) { 938 if ($name == 'HELO') { 939 return $this->server_caps['EHLO']; 940 } 941 if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { 942 return false; 943 } 944 $this->setError('HELO handshake was used. Client knows nothing about server extensions'); 945 return null; 946 } 947 948 return $this->server_caps[$name]; 799 949 } 800 950 … … 820 970 protected function get_lines() 821 971 { 972 // If the connection is bad, give up straight away 973 if (!is_resource($this->smtp_conn)) { 974 return ''; 975 } 822 976 $data = ''; 823 977 $endtime = 0; 824 // If the connection is bad, give up now825 if (!is_resource($this->smtp_conn)) {826 return $data;827 }828 978 stream_set_timeout($this->smtp_conn, $this->Timeout); 829 979 if ($this->Timelimit > 0) { … … 832 982 while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 833 983 $str = @fgets($this->smtp_conn, 515); 834 if ($this->do_debug >= 4) { 835 $this->edebug("SMTP -> get_lines(): \$data was \"$data\""); 836 $this->edebug("SMTP -> get_lines(): \$str is \"$str\""); 837 } 984 $this->edebug("SMTP -> get_lines(): \$data was \"$data\"", self::DEBUG_LOWLEVEL); 985 $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); 838 986 $data .= $str; 839 if ($this->do_debug >= 4) { 840 $this->edebug("SMTP -> get_lines(): \$data is \"$data\""); 841 } 842 // if 4th character is a space, we are done reading, break the loop 843 if (substr($str, 3, 1) == ' ') { 987 $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); 988 // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen 989 if ((isset($str[3]) and $str[3] == ' ')) { 844 990 break; 845 991 } … … 847 993 $info = stream_get_meta_data($this->smtp_conn); 848 994 if ($info['timed_out']) { 849 if ($this->do_debug >= 4) { 850 $this->edebug( 851 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)' 852 ); 853 } 995 $this->edebug( 996 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', 997 self::DEBUG_LOWLEVEL 998 ); 854 999 break; 855 1000 } 856 1001 // Now check if reads took too long 857 if ($endtime) { 858 if (time() > $endtime) { 859 if ($this->do_debug >= 4) { 860 $this->edebug( 861 'SMTP -> get_lines(): timelimit reached (' 862 . $this->Timelimit . ' sec)' 863 ); 864 } 865 break; 866 } 1002 if ($endtime and time() > $endtime) { 1003 $this->edebug( 1004 'SMTP -> get_lines(): timelimit reached ('. 1005 $this->Timelimit . ' sec)', 1006 self::DEBUG_LOWLEVEL 1007 ); 1008 break; 867 1009 } 868 1010 } … … 872 1014 /** 873 1015 * Enable or disable VERP address generation. 874 * @param bool $enabled1016 * @param boolean $enabled 875 1017 */ 876 1018 public function setVerp($enabled = false) … … 881 1023 /** 882 1024 * Get VERP address generation mode. 883 * @return bool 1025 * @return boolean 884 1026 */ 885 1027 public function getVerp() … … 889 1031 890 1032 /** 1033 * Set error messages and codes. 1034 * @param string $message The error message 1035 * @param string $detail Further detail on the error 1036 * @param string $smtp_code An associated SMTP error code 1037 * @param string $smtp_code_ex Extended SMTP code 1038 */ 1039 protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') 1040 { 1041 $this->error = array( 1042 'error' => $message, 1043 'detail' => $detail, 1044 'smtp_code' => $smtp_code, 1045 'smtp_code_ex' => $smtp_code_ex 1046 ); 1047 } 1048 1049 /** 891 1050 * Set debug output method. 892 * @param string $method The function/method to use for debugging output.1051 * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. 893 1052 */ 894 1053 public function setDebugOutput($method = 'echo') … … 908 1067 /** 909 1068 * Set debug output level. 910 * @param int $level1069 * @param integer $level 911 1070 */ 912 1071 public function setDebugLevel($level = 0) … … 917 1076 /** 918 1077 * Get debug output level. 919 * @return int 1078 * @return integer 920 1079 */ 921 1080 public function getDebugLevel() … … 926 1085 /** 927 1086 * Set SMTP timeout. 928 * @param int $timeout1087 * @param integer $timeout 929 1088 */ 930 1089 public function setTimeout($timeout = 0) … … 935 1094 /** 936 1095 * Get SMTP timeout. 937 * @return int 1096 * @return integer 938 1097 */ 939 1098 public function getTimeout()
Note: See TracChangeset
for help on using the changeset viewer.