SMTP

PHPMailer RFC821 SMTP email transport class.

Defined (1)

The class is defined in the following location(s).

/wp-includes/class-smtp.php  
  1. class SMTP 
  2. /** 
  3. * The PHPMailer SMTP version number. 
  4. * @var string 
  5. */ 
  6. const VERSION = '5.2.22'; 
  7.  
  8. /** 
  9. * SMTP line break constant. 
  10. * @var string 
  11. */ 
  12. const CRLF = "\r\n"; 
  13.  
  14. /** 
  15. * The SMTP port to use if one is not specified. 
  16. * @var integer 
  17. */ 
  18. const DEFAULT_SMTP_PORT = 25; 
  19.  
  20. /** 
  21. * The maximum line length allowed by RFC 2822 section 2.1.1 
  22. * @var integer 
  23. */ 
  24. const MAX_LINE_LENGTH = 998; 
  25.  
  26. /** 
  27. * Debug level for no output 
  28. */ 
  29. const DEBUG_OFF = 0; 
  30.  
  31. /** 
  32. * Debug level to show client -> server messages 
  33. */ 
  34. const DEBUG_CLIENT = 1; 
  35.  
  36. /** 
  37. * Debug level to show client -> server and server -> client messages 
  38. */ 
  39. const DEBUG_SERVER = 2; 
  40.  
  41. /** 
  42. * Debug level to show connection status, client -> server and server -> client messages 
  43. */ 
  44. const DEBUG_CONNECTION = 3; 
  45.  
  46. /** 
  47. * Debug level to show all messages 
  48. */ 
  49. const DEBUG_LOWLEVEL = 4; 
  50.  
  51. /** 
  52. * The PHPMailer SMTP Version number. 
  53. * @var string 
  54. * @deprecated Use the `VERSION` constant instead 
  55. * @see SMTP::VERSION 
  56. */ 
  57. public $Version = '5.2.22'; 
  58.  
  59. /** 
  60. * SMTP server port number. 
  61. * @var integer 
  62. * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead 
  63. * @see SMTP::DEFAULT_SMTP_PORT 
  64. */ 
  65. public $SMTP_PORT = 25; 
  66.  
  67. /** 
  68. * SMTP reply line ending. 
  69. * @var string 
  70. * @deprecated Use the `CRLF` constant instead 
  71. * @see SMTP::CRLF 
  72. */ 
  73. public $CRLF = "\r\n"; 
  74.  
  75. /** 
  76. * Debug output level. 
  77. * Options: 
  78. * * self::DEBUG_OFF (`0`) No debug output, default 
  79. * * self::DEBUG_CLIENT (`1`) Client commands 
  80. * * self::DEBUG_SERVER (`2`) Client commands and server responses 
  81. * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status 
  82. * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages 
  83. * @var integer 
  84. */ 
  85. public $do_debug = self::DEBUG_OFF; 
  86.  
  87. /** 
  88. * How to handle debug output. 
  89. * Options: 
  90. * * `echo` Output plain-text as-is, appropriate for CLI 
  91. * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output 
  92. * * `error_log` Output to error log as configured in php.ini 
  93. * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 
  94. * <code> 
  95. * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; 
  96. * </code> 
  97. * @var string|callable 
  98. */ 
  99. public $Debugoutput = 'echo'; 
  100.  
  101. /** 
  102. * Whether to use VERP. 
  103. * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path 
  104. * @link http://www.postfix.org/VERP_README.html Info on VERP 
  105. * @var boolean 
  106. */ 
  107. public $do_verp = false; 
  108.  
  109. /** 
  110. * The timeout value for connection, in seconds. 
  111. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 
  112. * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. 
  113. * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 
  114. * @var integer 
  115. */ 
  116. public $Timeout = 300; 
  117.  
  118. /** 
  119. * How long to wait for commands to complete, in seconds. 
  120. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 
  121. * @var integer 
  122. */ 
  123. public $Timelimit = 300; 
  124.  
  125. /** 
  126. * @var array patterns to extract smtp transaction id from smtp reply 
  127. * Only first capture group will be use, use non-capturing group to deal with it 
  128. * Extend this class to override this property to fulfil your needs. 
  129. */ 
  130. protected $smtp_transaction_id_patterns = array( 
  131. 'exim' => '/[0-9]{3} OK id=(.*)/',  
  132. 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/',  
  133. 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' 
  134. ); 
  135.  
  136. /** 
  137. * The socket for the server connection. 
  138. * @var resource 
  139. */ 
  140. protected $smtp_conn; 
  141.  
  142. /** 
  143. * Error information, if any, for the last SMTP command. 
  144. * @var array 
  145. */ 
  146. protected $error = array( 
  147. 'error' => '',  
  148. 'detail' => '',  
  149. 'smtp_code' => '',  
  150. 'smtp_code_ex' => '' 
  151. ); 
  152.  
  153. /** 
  154. * The reply the server sent to us for HELO. 
  155. * If null, no HELO string has yet been received. 
  156. * @var string|null 
  157. */ 
  158. protected $helo_rply = null; 
  159.  
  160. /** 
  161. * The set of SMTP extensions sent in reply to EHLO command. 
  162. * Indexes of the array are extension names. 
  163. * Value at index 'HELO' or 'EHLO' (according to command that was sent) 
  164. * represents the server name. In case of HELO it is the only element of the array. 
  165. * Other values can be boolean TRUE or an array containing extension options. 
  166. * If null, no HELO/EHLO string has yet been received. 
  167. * @var array|null 
  168. */ 
  169. protected $server_caps = null; 
  170.  
  171. /** 
  172. * The most recent reply received from the server. 
  173. * @var string 
  174. */ 
  175. protected $last_reply = ''; 
  176.  
  177. /** 
  178. * Output debugging info via a user-selected method. 
  179. * @see SMTP::$Debugoutput 
  180. * @see SMTP::$do_debug 
  181. * @param string $str Debug string to output 
  182. * @param integer $level The debug level of this message; see DEBUG_* constants 
  183. * @return void 
  184. */ 
  185. protected function edebug($str, $level = 0) 
  186. if ($level > $this->do_debug) { 
  187. return; 
  188. //Avoid clash with built-in function names 
  189. if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { 
  190. call_user_func($this->Debugoutput, $str, $level); 
  191. return; 
  192. switch ($this->Debugoutput) { 
  193. case 'error_log': 
  194. //Don't output, just log 
  195. error_log($str); 
  196. break; 
  197. case 'html': 
  198. //Cleans up output a bit for a better looking, HTML-safe output 
  199. echo htmlentities( 
  200. preg_replace('/[\r\n]+/', '', $str),  
  201. ENT_QUOTES,  
  202. 'UTF-8' 
  203. . "<br>\n"; 
  204. break; 
  205. case 'echo': 
  206. default: 
  207. //Normalize line breaks 
  208. $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 
  209. echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 
  210. "\n",  
  211. "\n \t ",  
  212. trim($str) 
  213. )."\n"; 
  214.  
  215. /** 
  216. * Connect to an SMTP server. 
  217. * @param string $host SMTP server IP or host name 
  218. * @param integer $port The port number to connect to 
  219. * @param integer $timeout How long to wait for the connection to open 
  220. * @param array $options An array of options for stream_context_create() 
  221. * @access public 
  222. * @return boolean 
  223. */ 
  224. public function connect($host, $port = null, $timeout = 30, $options = array()) 
  225. static $streamok; 
  226. //This is enabled by default since 5.0.0 but some providers disable it 
  227. //Check this once and cache the result 
  228. if (is_null($streamok)) { 
  229. $streamok = function_exists('stream_socket_client'); 
  230. // Clear errors to avoid confusion 
  231. $this->setError(''); 
  232. // Make sure we are __not__ connected 
  233. if ($this->connected()) { 
  234. // Already connected, generate error 
  235. $this->setError('Already connected to a server'); 
  236. return false; 
  237. if (empty($port)) { 
  238. $port = self::DEFAULT_SMTP_PORT; 
  239. // Connect to the SMTP server 
  240. $this->edebug( 
  241. "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true),  
  242. self::DEBUG_CONNECTION 
  243. ); 
  244. $errno = 0; 
  245. $errstr = ''; 
  246. if ($streamok) { 
  247. $socket_context = stream_context_create($options); 
  248. set_error_handler(array($this, 'errorHandler')); 
  249. $this->smtp_conn = stream_socket_client( 
  250. $host . ":" . $port,  
  251. $errno,  
  252. $errstr,  
  253. $timeout,  
  254. STREAM_CLIENT_CONNECT,  
  255. $socket_context 
  256. ); 
  257. restore_error_handler(); 
  258. } else { 
  259. //Fall back to fsockopen which should work in more places, but is missing some features 
  260. $this->edebug( 
  261. "Connection: stream_socket_client not available, falling back to fsockopen",  
  262. self::DEBUG_CONNECTION 
  263. ); 
  264. set_error_handler(array($this, 'errorHandler')); 
  265. $this->smtp_conn = fsockopen( 
  266. $host,  
  267. $port,  
  268. $errno,  
  269. $errstr,  
  270. $timeout 
  271. ); 
  272. restore_error_handler(); 
  273. // Verify we connected properly 
  274. if (!is_resource($this->smtp_conn)) { 
  275. $this->setError( 
  276. 'Failed to connect to server',  
  277. $errno,  
  278. $errstr 
  279. ); 
  280. $this->edebug( 
  281. 'SMTP ERROR: ' . $this->error['error'] 
  282. . ": $errstr ($errno)",  
  283. self::DEBUG_CLIENT 
  284. ); 
  285. return false; 
  286. $this->edebug('Connection: opened', self::DEBUG_CONNECTION); 
  287. // SMTP server can take longer to respond, give longer timeout for first read 
  288. // Windows does not have support for this timeout function 
  289. if (substr(PHP_OS, 0, 3) != 'WIN') { 
  290. $max = ini_get('max_execution_time'); 
  291. // Don't bother if unlimited 
  292. if ($max != 0 && $timeout > $max) { 
  293. @set_time_limit($timeout); 
  294. stream_set_timeout($this->smtp_conn, $timeout, 0); 
  295. // Get any announcement 
  296. $announce = $this->get_lines(); 
  297. $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); 
  298. return true; 
  299.  
  300. /** 
  301. * Initiate a TLS (encrypted) session. 
  302. * @access public 
  303. * @return boolean 
  304. */ 
  305. public function startTLS() 
  306. if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { 
  307. return false; 
  308.  
  309. //Allow the best TLS version(s) we can 
  310. $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; 
  311.  
  312. //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT 
  313. //so add them back in manually if we can 
  314. if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { 
  315. $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; 
  316. $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; 
  317.  
  318. // Begin encrypted connection 
  319. if (!stream_socket_enable_crypto( 
  320. $this->smtp_conn,  
  321. true,  
  322. $crypto_method 
  323. )) { 
  324. return false; 
  325. return true; 
  326.  
  327. /** 
  328. * Perform SMTP authentication. 
  329. * Must be run after hello(). 
  330. * @see hello() 
  331. * @param string $username The user name 
  332. * @param string $password The password 
  333. * @param string $authtype The auth type (PLAIN, LOGIN, CRAM-MD5) 
  334. * @param string $realm The auth realm for NTLM 
  335. * @param string $workstation The auth workstation for NTLM 
  336. * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) 
  337. * @return bool True if successfully authenticated.* @access public 
  338. */ 
  339. public function authenticate( 
  340. $username,  
  341. $password,  
  342. $authtype = null,  
  343. $realm = '',  
  344. $workstation = '',  
  345. $OAuth = null 
  346. ) { 
  347. if (!$this->server_caps) { 
  348. $this->setError('Authentication is not allowed before HELO/EHLO'); 
  349. return false; 
  350.  
  351. if (array_key_exists('EHLO', $this->server_caps)) { 
  352. // SMTP extensions are available. Let's try to find a proper authentication method 
  353.  
  354. if (!array_key_exists('AUTH', $this->server_caps)) { 
  355. $this->setError('Authentication is not allowed at this stage'); 
  356. // 'at this stage' means that auth may be allowed after the stage changes 
  357. // e.g. after STARTTLS 
  358. return false; 
  359.  
  360. self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); 
  361. self::edebug( 
  362. 'Auth methods available on the server: ' . implode(', ', $this->server_caps['AUTH']),  
  363. self::DEBUG_LOWLEVEL 
  364. ); 
  365.  
  366. if (empty($authtype)) { 
  367. foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN') as $method) { 
  368. if (in_array($method, $this->server_caps['AUTH'])) { 
  369. $authtype = $method; 
  370. break; 
  371. if (empty($authtype)) { 
  372. $this->setError('No supported authentication methods found'); 
  373. return false; 
  374. self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); 
  375.  
  376. if (!in_array($authtype, $this->server_caps['AUTH'])) { 
  377. $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); 
  378. return false; 
  379. } elseif (empty($authtype)) { 
  380. $authtype = 'LOGIN'; 
  381. switch ($authtype) { 
  382. case 'PLAIN': 
  383. // Start authentication 
  384. if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { 
  385. return false; 
  386. // Send encoded username and password 
  387. if (!$this->sendCommand( 
  388. 'User & Password',  
  389. base64_encode("\0" . $username . "\0" . $password),  
  390. 235 
  391. ) { 
  392. return false; 
  393. break; 
  394. case 'LOGIN': 
  395. // Start authentication 
  396. if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { 
  397. return false; 
  398. if (!$this->sendCommand("Username", base64_encode($username), 334)) { 
  399. return false; 
  400. if (!$this->sendCommand("Password", base64_encode($password), 235)) { 
  401. return false; 
  402. break; 
  403. case 'CRAM-MD5': 
  404. // Start authentication 
  405. if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { 
  406. return false; 
  407. // Get the challenge 
  408. $challenge = base64_decode(substr($this->last_reply, 4)); 
  409.  
  410. // Build the response 
  411. $response = $username . ' ' . $this->hmac($challenge, $password); 
  412.  
  413. // send encoded credentials 
  414. return $this->sendCommand('Username', base64_encode($response), 235); 
  415. default: 
  416. $this->setError("Authentication method \"$authtype\" is not supported"); 
  417. return false; 
  418. return true; 
  419.  
  420. /** 
  421. * Calculate an MD5 HMAC hash. 
  422. * Works like hash_hmac('md5', $data, $key) 
  423. * in case that function is not available 
  424. * @param string $data The data to hash 
  425. * @param string $key The key to hash with 
  426. * @access protected 
  427. * @return string 
  428. */ 
  429. protected function hmac($data, $key) 
  430. if (function_exists('hash_hmac')) { 
  431. return hash_hmac('md5', $data, $key); 
  432.  
  433. // The following borrowed from 
  434. // http://php.net/manual/en/function.mhash.php#27225 
  435.  
  436. // RFC 2104 HMAC implementation for php. 
  437. // Creates an md5 HMAC. 
  438. // Eliminates the need to install mhash to compute a HMAC 
  439. // by Lance Rushing 
  440.  
  441. $bytelen = 64; // byte length for md5 
  442. if (strlen($key) > $bytelen) { 
  443. $key = pack('H*', md5($key)); 
  444. $key = str_pad($key, $bytelen, chr(0x00)); 
  445. $ipad = str_pad('', $bytelen, chr(0x36)); 
  446. $opad = str_pad('', $bytelen, chr(0x5c)); 
  447. $k_ipad = $key ^ $ipad; 
  448. $k_opad = $key ^ $opad; 
  449.  
  450. return md5($k_opad . pack('H*', md5($k_ipad . $data))); 
  451.  
  452. /** 
  453. * Check connection state. 
  454. * @access public 
  455. * @return boolean True if connected. 
  456. */ 
  457. public function connected() 
  458. if (is_resource($this->smtp_conn)) { 
  459. $sock_status = stream_get_meta_data($this->smtp_conn); 
  460. if ($sock_status['eof']) { 
  461. // The socket is valid but we are not connected 
  462. $this->edebug( 
  463. 'SMTP NOTICE: EOF caught while checking if connected',  
  464. self::DEBUG_CLIENT 
  465. ); 
  466. $this->close(); 
  467. return false; 
  468. return true; // everything looks good 
  469. return false; 
  470.  
  471. /** 
  472. * Close the socket and clean up the state of the class. 
  473. * Don't use this function without first trying to use QUIT. 
  474. * @see quit() 
  475. * @access public 
  476. * @return void 
  477. */ 
  478. public function close() 
  479. $this->setError(''); 
  480. $this->server_caps = null; 
  481. $this->helo_rply = null; 
  482. if (is_resource($this->smtp_conn)) { 
  483. // close the connection and cleanup 
  484. fclose($this->smtp_conn); 
  485. $this->smtp_conn = null; //Makes for cleaner serialization 
  486. $this->edebug('Connection: closed', self::DEBUG_CONNECTION); 
  487.  
  488. /** 
  489. * Send an SMTP DATA command. 
  490. * Issues a data command and sends the msg_data to the server,  
  491. * finializing the mail transaction. $msg_data is the message 
  492. * that is to be send with the headers. Each header needs to be 
  493. * on a single line followed by a <CRLF> with the message headers 
  494. * and the message body being separated by and additional <CRLF>. 
  495. * Implements rfc 821: DATA <CRLF> 
  496. * @param string $msg_data Message data to send 
  497. * @access public 
  498. * @return boolean 
  499. */ 
  500. public function data($msg_data) 
  501. //This will use the standard timelimit 
  502. if (!$this->sendCommand('DATA', 'DATA', 354)) { 
  503. return false; 
  504.  
  505. /** The server is ready to accept data! 
  506. * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) 
  507. * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into 
  508. * smaller lines to fit within the limit. 
  509. * We will also look for lines that start with a '.' and prepend an additional '.'. 
  510. * NOTE: this does not count towards line-length limit. 
  511. */ 
  512.  
  513. // Normalize line breaks before exploding 
  514. $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); 
  515.  
  516. /** To distinguish between a complete RFC822 message and a plain message body, we check if the first field 
  517. * of the first line (':' separated) does not contain a space then it _should_ be a header and we will 
  518. * process all lines before a blank line as headers. 
  519. */ 
  520.  
  521. $field = substr($lines[0], 0, strpos($lines[0], ':')); 
  522. $in_headers = false; 
  523. if (!empty($field) && strpos($field, ' ') === false) { 
  524. $in_headers = true; 
  525.  
  526. foreach ($lines as $line) { 
  527. $lines_out = array(); 
  528. if ($in_headers and $line == '') { 
  529. $in_headers = false; 
  530. //Break this line up into several smaller lines if it's too long 
  531. //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),  
  532. while (isset($line[self::MAX_LINE_LENGTH])) { 
  533. //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on 
  534. //so as to avoid breaking in the middle of a word 
  535. $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); 
  536. //Deliberately matches both false and 0 
  537. if (!$pos) { 
  538. //No nice break found, add a hard break 
  539. $pos = self::MAX_LINE_LENGTH - 1; 
  540. $lines_out[] = substr($line, 0, $pos); 
  541. $line = substr($line, $pos); 
  542. } else { 
  543. //Break at the found point 
  544. $lines_out[] = substr($line, 0, $pos); 
  545. //Move along by the amount we dealt with 
  546. $line = substr($line, $pos + 1); 
  547. //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 
  548. if ($in_headers) { 
  549. $line = "\t" . $line; 
  550. $lines_out[] = $line; 
  551.  
  552. //Send the lines to the server 
  553. foreach ($lines_out as $line_out) { 
  554. //RFC2821 section 4.5.2 
  555. if (!empty($line_out) and $line_out[0] == '.') { 
  556. $line_out = '.' . $line_out; 
  557. $this->client_send($line_out . self::CRLF); 
  558.  
  559. //Message data has been sent, complete the command 
  560. //Increase timelimit for end of DATA command 
  561. $savetimelimit = $this->Timelimit; 
  562. $this->Timelimit = $this->Timelimit * 2; 
  563. $result = $this->sendCommand('DATA END', '.', 250); 
  564. //Restore timelimit 
  565. $this->Timelimit = $savetimelimit; 
  566. return $result; 
  567.  
  568. /** 
  569. * Send an SMTP HELO or EHLO command. 
  570. * Used to identify the sending server to the receiving server. 
  571. * This makes sure that client and server are in a known state. 
  572. * Implements RFC 821: HELO <SP> <domain> <CRLF> 
  573. * and RFC 2821 EHLO. 
  574. * @param string $host The host name or IP to connect to 
  575. * @access public 
  576. * @return boolean 
  577. */ 
  578. public function hello($host = '') 
  579. //Try extended hello first (RFC 2821) 
  580. return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); 
  581.  
  582. /** 
  583. * Send an SMTP HELO or EHLO command. 
  584. * Low-level implementation used by hello() 
  585. * @see hello() 
  586. * @param string $hello The HELO string 
  587. * @param string $host The hostname to say we are 
  588. * @access protected 
  589. * @return boolean 
  590. */ 
  591. protected function sendHello($hello, $host) 
  592. $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); 
  593. $this->helo_rply = $this->last_reply; 
  594. if ($noerror) { 
  595. $this->parseHelloFields($hello); 
  596. } else { 
  597. $this->server_caps = null; 
  598. return $noerror; 
  599.  
  600. /** 
  601. * Parse a reply to HELO/EHLO command to discover server extensions. 
  602. * In case of HELO, the only parameter that can be discovered is a server name. 
  603. * @access protected 
  604. * @param string $type - 'HELO' or 'EHLO' 
  605. */ 
  606. protected function parseHelloFields($type) 
  607. $this->server_caps = array(); 
  608. $lines = explode("\n", $this->helo_rply); 
  609.  
  610. foreach ($lines as $n => $s) { 
  611. //First 4 chars contain response code followed by - or space 
  612. $s = trim(substr($s, 4)); 
  613. if (empty($s)) { 
  614. continue; 
  615. $fields = explode(' ', $s); 
  616. if (!empty($fields)) { 
  617. if (!$n) { 
  618. $name = $type; 
  619. $fields = $fields[0]; 
  620. } else { 
  621. $name = array_shift($fields); 
  622. switch ($name) { 
  623. case 'SIZE': 
  624. $fields = ($fields ? $fields[0] : 0); 
  625. break; 
  626. case 'AUTH': 
  627. if (!is_array($fields)) { 
  628. $fields = array(); 
  629. break; 
  630. default: 
  631. $fields = true; 
  632. $this->server_caps[$name] = $fields; 
  633.  
  634. /** 
  635. * Send an SMTP MAIL command. 
  636. * Starts a mail transaction from the email address specified in 
  637. * $from. Returns true if successful or false otherwise. If True 
  638. * the mail transaction is started and then one or more recipient 
  639. * commands may be called followed by a data command. 
  640. * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> 
  641. * @param string $from Source address of this message 
  642. * @access public 
  643. * @return boolean 
  644. */ 
  645. public function mail($from) 
  646. $useVerp = ($this->do_verp ? ' XVERP' : ''); 
  647. return $this->sendCommand( 
  648. 'MAIL FROM',  
  649. 'MAIL FROM:<' . $from . '>' . $useVerp,  
  650. 250 
  651. ); 
  652.  
  653. /** 
  654. * Send an SMTP QUIT command. 
  655. * Closes the socket if there is no error or the $close_on_error argument is true. 
  656. * Implements from rfc 821: QUIT <CRLF> 
  657. * @param boolean $close_on_error Should the connection close if an error occurs? 
  658. * @access public 
  659. * @return boolean 
  660. */ 
  661. public function quit($close_on_error = true) 
  662. $noerror = $this->sendCommand('QUIT', 'QUIT', 221); 
  663. $err = $this->error; //Save any error 
  664. if ($noerror or $close_on_error) { 
  665. $this->close(); 
  666. $this->error = $err; //Restore any error from the quit command 
  667. return $noerror; 
  668.  
  669. /** 
  670. * Send an SMTP RCPT command. 
  671. * Sets the TO argument to $toaddr. 
  672. * Returns true if the recipient was accepted false if it was rejected. 
  673. * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> 
  674. * @param string $address The address the message is being sent to 
  675. * @access public 
  676. * @return boolean 
  677. */ 
  678. public function recipient($address) 
  679. return $this->sendCommand( 
  680. 'RCPT TO',  
  681. 'RCPT TO:<' . $address . '>',  
  682. array(250, 251) 
  683. ); 
  684.  
  685. /** 
  686. * Send an SMTP RSET command. 
  687. * Abort any transaction that is currently in progress. 
  688. * Implements rfc 821: RSET <CRLF> 
  689. * @access public 
  690. * @return boolean True on success. 
  691. */ 
  692. public function reset() 
  693. return $this->sendCommand('RSET', 'RSET', 250); 
  694.  
  695. /** 
  696. * Send a command to an SMTP server and check its return code. 
  697. * @param string $command The command name - not sent to the server 
  698. * @param string $commandstring The actual command to send 
  699. * @param integer|array $expect One or more expected integer success codes 
  700. * @access protected 
  701. * @return boolean True on success. 
  702. */ 
  703. protected function sendCommand($command, $commandstring, $expect) 
  704. if (!$this->connected()) { 
  705. $this->setError("Called $command without being connected"); 
  706. return false; 
  707. //Reject line breaks in all commands 
  708. if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { 
  709. $this->setError("Command '$command' contained line breaks"); 
  710. return false; 
  711. $this->client_send($commandstring . self::CRLF); 
  712.  
  713. $this->last_reply = $this->get_lines(); 
  714. // Fetch SMTP code and possible error code explanation 
  715. $matches = array(); 
  716. if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { 
  717. $code = $matches[1]; 
  718. $code_ex = (count($matches) > 2 ? $matches[2] : null); 
  719. // Cut off error code from each response line 
  720. $detail = preg_replace( 
  721. "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m",  
  722. '',  
  723. $this->last_reply 
  724. ); 
  725. } else { 
  726. // Fall back to simple parsing if regex fails 
  727. $code = substr($this->last_reply, 0, 3); 
  728. $code_ex = null; 
  729. $detail = substr($this->last_reply, 4); 
  730.  
  731. $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); 
  732.  
  733. if (!in_array($code, (array)$expect)) { 
  734. $this->setError( 
  735. "$command command failed",  
  736. $detail,  
  737. $code,  
  738. $code_ex 
  739. ); 
  740. $this->edebug( 
  741. 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,  
  742. self::DEBUG_CLIENT 
  743. ); 
  744. return false; 
  745.  
  746. $this->setError(''); 
  747. return true; 
  748.  
  749. /** 
  750. * Send an SMTP SAML command. 
  751. * Starts a mail transaction from the email address specified in $from. 
  752. * Returns true if successful or false otherwise. If True 
  753. * the mail transaction is started and then one or more recipient 
  754. * commands may be called followed by a data command. This command 
  755. * will send the message to the users terminal if they are logged 
  756. * in and send them an email. 
  757. * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> 
  758. * @param string $from The address the message is from 
  759. * @access public 
  760. * @return boolean 
  761. */ 
  762. public function sendAndMail($from) 
  763. return $this->sendCommand('SAML', "SAML FROM:$from", 250); 
  764.  
  765. /** 
  766. * Send an SMTP VRFY command. 
  767. * @param string $name The name to verify 
  768. * @access public 
  769. * @return boolean 
  770. */ 
  771. public function verify($name) 
  772. return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); 
  773.  
  774. /** 
  775. * Send an SMTP NOOP command. 
  776. * Used to keep keep-alives alive, doesn't actually do anything 
  777. * @access public 
  778. * @return boolean 
  779. */ 
  780. public function noop() 
  781. return $this->sendCommand('NOOP', 'NOOP', 250); 
  782.  
  783. /** 
  784. * Send an SMTP TURN command. 
  785. * This is an optional command for SMTP that this class does not support. 
  786. * This method is here to make the RFC821 Definition complete for this class 
  787. * and _may_ be implemented in future 
  788. * Implements from rfc 821: TURN <CRLF> 
  789. * @access public 
  790. * @return boolean 
  791. */ 
  792. public function turn() 
  793. $this->setError('The SMTP TURN command is not implemented'); 
  794. $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); 
  795. return false; 
  796.  
  797. /** 
  798. * Send raw data to the server. 
  799. * @param string $data The data to send 
  800. * @access public 
  801. * @return integer|boolean The number of bytes sent to the server or false on error 
  802. */ 
  803. public function client_send($data) 
  804. $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); 
  805. return fwrite($this->smtp_conn, $data); 
  806.  
  807. /** 
  808. * Get the latest error. 
  809. * @access public 
  810. * @return array 
  811. */ 
  812. public function getError() 
  813. return $this->error; 
  814.  
  815. /** 
  816. * Get SMTP extensions available on the server 
  817. * @access public 
  818. * @return array|null 
  819. */ 
  820. public function getServerExtList() 
  821. return $this->server_caps; 
  822.  
  823. /** 
  824. * A multipurpose method 
  825. * The method works in three ways, dependent on argument value and current state 
  826. * 1. HELO/EHLO was not sent - returns null and set up $this->error 
  827. * 2. HELO was sent 
  828. * $name = 'HELO': returns server name 
  829. * $name = 'EHLO': returns boolean false 
  830. * $name = any string: returns null and set up $this->error 
  831. * 3. EHLO was sent 
  832. * $name = 'HELO'|'EHLO': returns server name 
  833. * $name = any string: if extension $name exists, returns boolean True 
  834. * or its options. Otherwise returns boolean False 
  835. * In other words, one can use this method to detect 3 conditions: 
  836. * - null returned: handshake was not or we don't know about ext (refer to $this->error) 
  837. * - false returned: the requested feature exactly not exists 
  838. * - positive value returned: the requested feature exists 
  839. * @param string $name Name of SMTP extension or 'HELO'|'EHLO' 
  840. * @return mixed 
  841. */ 
  842. public function getServerExt($name) 
  843. if (!$this->server_caps) { 
  844. $this->setError('No HELO/EHLO was sent'); 
  845. return null; 
  846.  
  847. // the tight logic knot ;) 
  848. if (!array_key_exists($name, $this->server_caps)) { 
  849. if ($name == 'HELO') { 
  850. return $this->server_caps['EHLO']; 
  851. if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { 
  852. return false; 
  853. $this->setError('HELO handshake was used. Client knows nothing about server extensions'); 
  854. return null; 
  855.  
  856. return $this->server_caps[$name]; 
  857.  
  858. /** 
  859. * Get the last reply from the server. 
  860. * @access public 
  861. * @return string 
  862. */ 
  863. public function getLastReply() 
  864. return $this->last_reply; 
  865.  
  866. /** 
  867. * Read the SMTP server's response. 
  868. * Either before eof or socket timeout occurs on the operation. 
  869. * With SMTP we can tell if we have more lines to read if the 
  870. * 4th character is '-' symbol. If it is a space then we don't 
  871. * need to read anything else. 
  872. * @access protected 
  873. * @return string 
  874. */ 
  875. protected function get_lines() 
  876. // If the connection is bad, give up straight away 
  877. if (!is_resource($this->smtp_conn)) { 
  878. return ''; 
  879. $data = ''; 
  880. $endtime = 0; 
  881. stream_set_timeout($this->smtp_conn, $this->Timeout); 
  882. if ($this->Timelimit > 0) { 
  883. $endtime = time() + $this->Timelimit; 
  884. while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 
  885. $str = @fgets($this->smtp_conn, 515); 
  886. $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); 
  887. $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); 
  888. $data .= $str; 
  889. // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen 
  890. if ((isset($str[3]) and $str[3] == ' ')) { 
  891. break; 
  892. // Timed-out? Log and break 
  893. $info = stream_get_meta_data($this->smtp_conn); 
  894. if ($info['timed_out']) { 
  895. $this->edebug( 
  896. 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',  
  897. self::DEBUG_LOWLEVEL 
  898. ); 
  899. break; 
  900. // Now check if reads took too long 
  901. if ($endtime and time() > $endtime) { 
  902. $this->edebug( 
  903. 'SMTP -> get_lines(): timelimit reached ('. 
  904. $this->Timelimit . ' sec)',  
  905. self::DEBUG_LOWLEVEL 
  906. ); 
  907. break; 
  908. return $data; 
  909.  
  910. /** 
  911. * Enable or disable VERP address generation. 
  912. * @param boolean $enabled 
  913. */ 
  914. public function setVerp($enabled = false) 
  915. $this->do_verp = $enabled; 
  916.  
  917. /** 
  918. * Get VERP address generation mode. 
  919. * @return boolean 
  920. */ 
  921. public function getVerp() 
  922. return $this->do_verp; 
  923.  
  924. /** 
  925. * Set error messages and codes. 
  926. * @param string $message The error message 
  927. * @param string $detail Further detail on the error 
  928. * @param string $smtp_code An associated SMTP error code 
  929. * @param string $smtp_code_ex Extended SMTP code 
  930. */ 
  931. protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') 
  932. $this->error = array( 
  933. 'error' => $message,  
  934. 'detail' => $detail,  
  935. 'smtp_code' => $smtp_code,  
  936. 'smtp_code_ex' => $smtp_code_ex 
  937. ); 
  938.  
  939. /** 
  940. * Set debug output method. 
  941. * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. 
  942. */ 
  943. public function setDebugOutput($method = 'echo') 
  944. $this->Debugoutput = $method; 
  945.  
  946. /** 
  947. * Get debug output method. 
  948. * @return string 
  949. */ 
  950. public function getDebugOutput() 
  951. return $this->Debugoutput; 
  952.  
  953. /** 
  954. * Set debug output level. 
  955. * @param integer $level 
  956. */ 
  957. public function setDebugLevel($level = 0) 
  958. $this->do_debug = $level; 
  959.  
  960. /** 
  961. * Get debug output level. 
  962. * @return integer 
  963. */ 
  964. public function getDebugLevel() 
  965. return $this->do_debug; 
  966.  
  967. /** 
  968. * Set SMTP timeout. 
  969. * @param integer $timeout 
  970. */ 
  971. public function setTimeout($timeout = 0) 
  972. $this->Timeout = $timeout; 
  973.  
  974. /** 
  975. * Get SMTP timeout. 
  976. * @return integer 
  977. */ 
  978. public function getTimeout() 
  979. return $this->Timeout; 
  980.  
  981. /** 
  982. * Reports an error number and string. 
  983. * @param integer $errno The error number returned by PHP. 
  984. * @param string $errmsg The error message returned by PHP. 
  985. */ 
  986. protected function errorHandler($errno, $errmsg) 
  987. $notice = 'Connection: Failed to connect to server.'; 
  988. $this->setError( 
  989. $notice,  
  990. $errno,  
  991. $errmsg 
  992. ); 
  993. $this->edebug( 
  994. $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg,  
  995. self::DEBUG_CONNECTION 
  996. ); 
  997.  
  998. /** 
  999. * Will return the ID of the last smtp transaction based on a list of patterns provided 
  1000. * in SMTP::$smtp_transaction_id_patterns. 
  1001. * If no reply has been received yet, it will return null. 
  1002. * If no pattern has been matched, it will return false. 
  1003. * @return bool|null|string 
  1004. */ 
  1005. public function getLastTransactionID() 
  1006. $reply = $this->getLastReply(); 
  1007.  
  1008. if (empty($reply)) { 
  1009. return null; 
  1010.  
  1011. foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { 
  1012. if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { 
  1013. return $matches[1]; 
  1014.  
  1015. return false;