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