Просмотр файла main_files/phpmailer/class.smtp.php

Размер файла: 38.97Kb
  1. <?php
  2. /**
  3. * PHPMailer RFC821 SMTP email transport class.
  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>
  8. * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
  9. * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
  10. * @author Brent R. Matzelle (original founder)
  11. * @copyright 2014 Marcus Bointon
  12. * @copyright 2010 - 2012 Jim Jagielski
  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.
  18. */
  19.  
  20. /**
  21. * PHPMailer RFC821 SMTP email transport class.
  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>
  26. */
  27. class SMTP
  28. {
  29. /**
  30. * The PHPMailer SMTP version number.
  31. * @var string
  32. */
  33. const VERSION = '5.2.14';
  34.  
  35. /**
  36. * SMTP line break constant.
  37. * @var string
  38. */
  39. const CRLF = "\r\n";
  40.  
  41. /**
  42. * The SMTP port to use if one is not specified.
  43. * @var integer
  44. */
  45. const DEFAULT_SMTP_PORT = 25;
  46.  
  47. /**
  48. * The maximum line length allowed by RFC 2822 section 2.1.1
  49. * @var 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;
  77.  
  78. /**
  79. * The PHPMailer SMTP Version number.
  80. * @var string
  81. * @deprecated Use the `VERSION` constant instead
  82. * @see SMTP::VERSION
  83. */
  84. public $Version = '5.2.14';
  85.  
  86. /**
  87. * SMTP server port number.
  88. * @var integer
  89. * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
  90. * @see SMTP::DEFAULT_SMTP_PORT
  91. */
  92. public $SMTP_PORT = 25;
  93.  
  94. /**
  95. * SMTP reply line ending.
  96. * @var string
  97. * @deprecated Use the `CRLF` constant instead
  98. * @see SMTP::CRLF
  99. */
  100. public $CRLF = "\r\n";
  101.  
  102. /**
  103. * Debug output level.
  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. * @var 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. * @var string|callable
  126. */
  127. public $Debugoutput = 'echo';
  128.  
  129. /**
  130. * Whether to use VERP.
  131. * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
  132. * @link http://www.postfix.org/VERP_README.html Info on VERP
  133. * @var boolean
  134. */
  135. public $do_verp = false;
  136.  
  137. /**
  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. * @var 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. * @var integer
  150. */
  151. public $Timelimit = 300;
  152.  
  153. /**
  154. * The socket for the server connection.
  155. * @var resource
  156. */
  157. protected $smtp_conn;
  158.  
  159. /**
  160. * Error information, if any, for the last SMTP command.
  161. * @var array
  162. */
  163. protected $error = array(
  164. 'error' => '',
  165. 'detail' => '',
  166. 'smtp_code' => '',
  167. 'smtp_code_ex' => ''
  168. );
  169.  
  170. /**
  171. * The reply the server sent to us for HELO.
  172. * If null, no HELO string has yet been received.
  173. * @var 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. * @var array|null
  185. */
  186. protected $server_caps = null;
  187.  
  188. /**
  189. * The most recent reply received from the server.
  190. * @var string
  191. */
  192. protected $last_reply = '';
  193.  
  194. /**
  195. * Output debugging info via a user-selected method.
  196. * @see SMTP::$Debugoutput
  197. * @see SMTP::$do_debug
  198. * @param string $str Debug string to output
  199. * @param integer $level The debug level of this message; see DEBUG_* constants
  200. * @return void
  201. */
  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. }
  212. switch ($this->Debugoutput) {
  213. case 'error_log':
  214. //Don't output, just log
  215. error_log($str);
  216. break;
  217. case 'html':
  218. //Cleans up output a bit for a better looking, HTML-safe output
  219. echo htmlentities(
  220. preg_replace('/[\r\n]+/', '', $str),
  221. ENT_QUOTES,
  222. 'UTF-8'
  223. )
  224. . "<br>\n";
  225. break;
  226. case 'echo':
  227. default:
  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";
  235. }
  236. }
  237.  
  238. /**
  239. * Connect to an SMTP server.
  240. * @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
  243. * @param array $options An array of options for stream_context_create()
  244. * @access public
  245. * @return boolean
  246. */
  247. public function connect($host, $port = null, $timeout = 30, $options = array())
  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. }
  255. // Clear errors to avoid confusion
  256. $this->setError('');
  257. // Make sure we are __not__ connected
  258. if ($this->connected()) {
  259. // Already connected, generate error
  260. $this->setError('Already connected to a server');
  261. return false;
  262. }
  263. if (empty($port)) {
  264. $port = self::DEFAULT_SMTP_PORT;
  265. }
  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. );
  271. $errno = 0;
  272. $errstr = '';
  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. }
  298. // Verify we connected properly
  299. if (!is_resource($this->smtp_conn)) {
  300. $this->setError(
  301. 'Failed to connect to server',
  302. $errno,
  303. $errstr
  304. );
  305. $this->edebug(
  306. 'SMTP ERROR: ' . $this->error['error']
  307. . ": $errstr ($errno)",
  308. self::DEBUG_CLIENT
  309. );
  310. return false;
  311. }
  312. $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
  313. // SMTP server can take longer to respond, give longer timeout for first read
  314. // Windows does not have support for this timeout function
  315. if (substr(PHP_OS, 0, 3) != 'WIN') {
  316. $max = ini_get('max_execution_time');
  317. // Don't bother if unlimited
  318. if ($max != 0 && $timeout > $max) {
  319. @set_time_limit($timeout);
  320. }
  321. stream_set_timeout($this->smtp_conn, $timeout, 0);
  322. }
  323. // Get any announcement
  324. $announce = $this->get_lines();
  325. $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
  326. return true;
  327. }
  328.  
  329. /**
  330. * Initiate a TLS (encrypted) session.
  331. * @access public
  332. * @return boolean
  333. */
  334. public function startTLS()
  335. {
  336. if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
  337. return false;
  338. }
  339. // Begin encrypted connection
  340. if (!stream_socket_enable_crypto(
  341. $this->smtp_conn,
  342. true,
  343. STREAM_CRYPTO_METHOD_TLS_CLIENT
  344. )) {
  345. return false;
  346. }
  347. return true;
  348. }
  349.  
  350. /**
  351. * Perform SMTP authentication.
  352. * Must be run after hello().
  353. * @see hello()
  354. * @param string $username The user name
  355. * @param string $password The password
  356. * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2)
  357. * @param string $realm The auth realm for NTLM
  358. * @param string $workstation The auth workstation for NTLM
  359. * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
  360. * @return bool True if successfully authenticated.* @access public
  361. */
  362. public function authenticate(
  363. $username,
  364. $password,
  365. $authtype = null,
  366. $realm = '',
  367. $workstation = '',
  368. $OAuth = null
  369. ) {
  370. if (!$this->server_caps) {
  371. $this->setError('Authentication is not allowed before HELO/EHLO');
  372. return false;
  373. }
  374.  
  375. if (array_key_exists('EHLO', $this->server_caps)) {
  376. // SMTP extensions are available. Let's try to find a proper authentication method
  377.  
  378. if (!array_key_exists('AUTH', $this->server_caps)) {
  379. $this->setError('Authentication is not allowed at this stage');
  380. // 'at this stage' means that auth may be allowed after the stage changes
  381. // e.g. after STARTTLS
  382. return false;
  383. }
  384.  
  385. self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
  386. self::edebug(
  387. 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
  388. self::DEBUG_LOWLEVEL
  389. );
  390.  
  391. if (empty($authtype)) {
  392. foreach (array('LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN', 'XOAUTH2') as $method) {
  393. if (in_array($method, $this->server_caps['AUTH'])) {
  394. $authtype = $method;
  395. break;
  396. }
  397. }
  398. if (empty($authtype)) {
  399. $this->setError('No supported authentication methods found');
  400. return false;
  401. }
  402. self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL);
  403. }
  404.  
  405. if (!in_array($authtype, $this->server_caps['AUTH'])) {
  406. $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
  407. return false;
  408. }
  409. } elseif (empty($authtype)) {
  410. $authtype = 'LOGIN';
  411. }
  412. switch ($authtype) {
  413. case 'PLAIN':
  414. // Start authentication
  415. if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
  416. return false;
  417. }
  418. // Send encoded username and password
  419. if (!$this->sendCommand(
  420. 'User & Password',
  421. base64_encode("\0" . $username . "\0" . $password),
  422. 235
  423. )
  424. ) {
  425. return false;
  426. }
  427. break;
  428. case 'LOGIN':
  429. // Start authentication
  430. if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
  431. return false;
  432. }
  433. if (!$this->sendCommand("Username", base64_encode($username), 334)) {
  434. return false;
  435. }
  436. if (!$this->sendCommand("Password", base64_encode($password), 235)) {
  437. return false;
  438. }
  439. break;
  440. case 'XOAUTH2':
  441. //If the OAuth Instance is not set. Can be a case when PHPMailer is used
  442. //instead of PHPMailerOAuth
  443. if (is_null($OAuth)) {
  444. return false;
  445. }
  446. $oauth = $OAuth->getOauth64();
  447.  
  448. // Start authentication
  449. if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
  450. return false;
  451. }
  452. break;
  453. case 'NTLM':
  454. /*
  455. * ntlm_sasl_client.php
  456. * Bundled with Permission
  457. *
  458. * How to telnet in windows:
  459. * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
  460. * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
  461. */
  462. require_once 'extras/ntlm_sasl_client.php';
  463. $temp = new stdClass;
  464. $ntlm_client = new ntlm_sasl_client_class;
  465. //Check that functions are available
  466. if (!$ntlm_client->Initialize($temp)) {
  467. $this->setError($temp->error);
  468. $this->edebug(
  469. 'You need to enable some modules in your php.ini file: '
  470. . $this->error['error'],
  471. self::DEBUG_CLIENT
  472. );
  473. return false;
  474. }
  475. //msg1
  476. $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
  477.  
  478. if (!$this->sendCommand(
  479. 'AUTH NTLM',
  480. 'AUTH NTLM ' . base64_encode($msg1),
  481. 334
  482. )
  483. ) {
  484. return false;
  485. }
  486. //Though 0 based, there is a white space after the 3 digit number
  487. //msg2
  488. $challenge = substr($this->last_reply, 3);
  489. $challenge = base64_decode($challenge);
  490. $ntlm_res = $ntlm_client->NTLMResponse(
  491. substr($challenge, 24, 8),
  492. $password
  493. );
  494. //msg3
  495. $msg3 = $ntlm_client->TypeMsg3(
  496. $ntlm_res,
  497. $username,
  498. $realm,
  499. $workstation
  500. );
  501. // send encoded username
  502. return $this->sendCommand('Username', base64_encode($msg3), 235);
  503. case 'CRAM-MD5':
  504. // Start authentication
  505. if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
  506. return false;
  507. }
  508. // Get the challenge
  509. $challenge = base64_decode(substr($this->last_reply, 4));
  510.  
  511. // Build the response
  512. $response = $username . ' ' . $this->hmac($challenge, $password);
  513.  
  514. // send encoded credentials
  515. return $this->sendCommand('Username', base64_encode($response), 235);
  516. default:
  517. $this->setError("Authentication method \"$authtype\" is not supported");
  518. return false;
  519. }
  520. return true;
  521. }
  522.  
  523. /**
  524. * Calculate an MD5 HMAC hash.
  525. * Works like hash_hmac('md5', $data, $key)
  526. * in case that function is not available
  527. * @param string $data The data to hash
  528. * @param string $key The key to hash with
  529. * @access protected
  530. * @return string
  531. */
  532. protected function hmac($data, $key)
  533. {
  534. if (function_exists('hash_hmac')) {
  535. return hash_hmac('md5', $data, $key);
  536. }
  537.  
  538. // The following borrowed from
  539. // http://php.net/manual/en/function.mhash.php#27225
  540.  
  541. // RFC 2104 HMAC implementation for php.
  542. // Creates an md5 HMAC.
  543. // Eliminates the need to install mhash to compute a HMAC
  544. // by Lance Rushing
  545.  
  546. $bytelen = 64; // byte length for md5
  547. if (strlen($key) > $bytelen) {
  548. $key = pack('H*', md5($key));
  549. }
  550. $key = str_pad($key, $bytelen, chr(0x00));
  551. $ipad = str_pad('', $bytelen, chr(0x36));
  552. $opad = str_pad('', $bytelen, chr(0x5c));
  553. $k_ipad = $key ^ $ipad;
  554. $k_opad = $key ^ $opad;
  555.  
  556. return md5($k_opad . pack('H*', md5($k_ipad . $data)));
  557. }
  558.  
  559. /**
  560. * Check connection state.
  561. * @access public
  562. * @return boolean True if connected.
  563. */
  564. public function connected()
  565. {
  566. if (is_resource($this->smtp_conn)) {
  567. $sock_status = stream_get_meta_data($this->smtp_conn);
  568. if ($sock_status['eof']) {
  569. // The socket is valid but we are not connected
  570. $this->edebug(
  571. 'SMTP NOTICE: EOF caught while checking if connected',
  572. self::DEBUG_CLIENT
  573. );
  574. $this->close();
  575. return false;
  576. }
  577. return true; // everything looks good
  578. }
  579. return false;
  580. }
  581.  
  582. /**
  583. * Close the socket and clean up the state of the class.
  584. * Don't use this function without first trying to use QUIT.
  585. * @see quit()
  586. * @access public
  587. * @return void
  588. */
  589. public function close()
  590. {
  591. $this->setError('');
  592. $this->server_caps = null;
  593. $this->helo_rply = null;
  594. if (is_resource($this->smtp_conn)) {
  595. // close the connection and cleanup
  596. fclose($this->smtp_conn);
  597. $this->smtp_conn = null; //Makes for cleaner serialization
  598. $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
  599. }
  600. }
  601.  
  602. /**
  603. * Send an SMTP DATA command.
  604. * Issues a data command and sends the msg_data to the server,
  605. * finializing the mail transaction. $msg_data is the message
  606. * that is to be send with the headers. Each header needs to be
  607. * on a single line followed by a <CRLF> with the message headers
  608. * and the message body being separated by and additional <CRLF>.
  609. * Implements rfc 821: DATA <CRLF>
  610. * @param string $msg_data Message data to send
  611. * @access public
  612. * @return boolean
  613. */
  614. public function data($msg_data)
  615. {
  616. //This will use the standard timelimit
  617. if (!$this->sendCommand('DATA', 'DATA', 354)) {
  618. return false;
  619. }
  620.  
  621. /* The server is ready to accept data!
  622. * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
  623. * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
  624. * smaller lines to fit within the limit.
  625. * We will also look for lines that start with a '.' and prepend an additional '.'.
  626. * NOTE: this does not count towards line-length limit.
  627. */
  628.  
  629. // Normalize line breaks before exploding
  630. $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
  631.  
  632. /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
  633. * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
  634. * process all lines before a blank line as headers.
  635. */
  636.  
  637. $field = substr($lines[0], 0, strpos($lines[0], ':'));
  638. $in_headers = false;
  639. if (!empty($field) && strpos($field, ' ') === false) {
  640. $in_headers = true;
  641. }
  642.  
  643. foreach ($lines as $line) {
  644. $lines_out = array();
  645. if ($in_headers and $line == '') {
  646. $in_headers = false;
  647. }
  648. //Break this line up into several smaller lines if it's too long
  649. //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
  650. while (isset($line[self::MAX_LINE_LENGTH])) {
  651. //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
  652. //so as to avoid breaking in the middle of a word
  653. $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
  654. //Deliberately matches both false and 0
  655. if (!$pos) {
  656. //No nice break found, add a hard break
  657. $pos = self::MAX_LINE_LENGTH - 1;
  658. $lines_out[] = substr($line, 0, $pos);
  659. $line = substr($line, $pos);
  660. } else {
  661. //Break at the found point
  662. $lines_out[] = substr($line, 0, $pos);
  663. //Move along by the amount we dealt with
  664. $line = substr($line, $pos + 1);
  665. }
  666. //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
  667. if ($in_headers) {
  668. $line = "\t" . $line;
  669. }
  670. }
  671. $lines_out[] = $line;
  672.  
  673. //Send the lines to the server
  674. foreach ($lines_out as $line_out) {
  675. //RFC2821 section 4.5.2
  676. if (!empty($line_out) and $line_out[0] == '.') {
  677. $line_out = '.' . $line_out;
  678. }
  679. $this->client_send($line_out . self::CRLF);
  680. }
  681. }
  682.  
  683. //Message data has been sent, complete the command
  684. //Increase timelimit for end of DATA command
  685. $savetimelimit = $this->Timelimit;
  686. $this->Timelimit = $this->Timelimit * 2;
  687. $result = $this->sendCommand('DATA END', '.', 250);
  688. //Restore timelimit
  689. $this->Timelimit = $savetimelimit;
  690. return $result;
  691. }
  692.  
  693. /**
  694. * Send an SMTP HELO or EHLO command.
  695. * Used to identify the sending server to the receiving server.
  696. * This makes sure that client and server are in a known state.
  697. * Implements RFC 821: HELO <SP> <domain> <CRLF>
  698. * and RFC 2821 EHLO.
  699. * @param string $host The host name or IP to connect to
  700. * @access public
  701. * @return boolean
  702. */
  703. public function hello($host = '')
  704. {
  705. //Try extended hello first (RFC 2821)
  706. return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
  707. }
  708.  
  709. /**
  710. * Send an SMTP HELO or EHLO command.
  711. * Low-level implementation used by hello()
  712. * @see hello()
  713. * @param string $hello The HELO string
  714. * @param string $host The hostname to say we are
  715. * @access protected
  716. * @return boolean
  717. */
  718. protected function sendHello($hello, $host)
  719. {
  720. $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
  721. $this->helo_rply = $this->last_reply;
  722. if ($noerror) {
  723. $this->parseHelloFields($hello);
  724. } else {
  725. $this->server_caps = null;
  726. }
  727. return $noerror;
  728. }
  729.  
  730. /**
  731. * Parse a reply to HELO/EHLO command to discover server extensions.
  732. * In case of HELO, the only parameter that can be discovered is a server name.
  733. * @access protected
  734. * @param string $type - 'HELO' or 'EHLO'
  735. */
  736. protected function parseHelloFields($type)
  737. {
  738. $this->server_caps = array();
  739. $lines = explode("\n", $this->last_reply);
  740.  
  741. foreach ($lines as $n => $s) {
  742. //First 4 chars contain response code followed by - or space
  743. $s = trim(substr($s, 4));
  744. if (empty($s)) {
  745. continue;
  746. }
  747. $fields = explode(' ', $s);
  748. if (!empty($fields)) {
  749. if (!$n) {
  750. $name = $type;
  751. $fields = $fields[0];
  752. } else {
  753. $name = array_shift($fields);
  754. switch ($name) {
  755. case 'SIZE':
  756. $fields = ($fields ? $fields[0] : 0);
  757. break;
  758. case 'AUTH':
  759. if (!is_array($fields)) {
  760. $fields = array();
  761. }
  762. break;
  763. default:
  764. $fields = true;
  765. }
  766. }
  767. $this->server_caps[$name] = $fields;
  768. }
  769. }
  770. }
  771.  
  772. /**
  773. * Send an SMTP MAIL command.
  774. * Starts a mail transaction from the email address specified in
  775. * $from. Returns true if successful or false otherwise. If True
  776. * the mail transaction is started and then one or more recipient
  777. * commands may be called followed by a data command.
  778. * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
  779. * @param string $from Source address of this message
  780. * @access public
  781. * @return boolean
  782. */
  783. public function mail($from)
  784. {
  785. $useVerp = ($this->do_verp ? ' XVERP' : '');
  786. return $this->sendCommand(
  787. 'MAIL FROM',
  788. 'MAIL FROM:<' . $from . '>' . $useVerp,
  789. 250
  790. );
  791. }
  792.  
  793. /**
  794. * Send an SMTP QUIT command.
  795. * Closes the socket if there is no error or the $close_on_error argument is true.
  796. * Implements from rfc 821: QUIT <CRLF>
  797. * @param boolean $close_on_error Should the connection close if an error occurs?
  798. * @access public
  799. * @return boolean
  800. */
  801. public function quit($close_on_error = true)
  802. {
  803. $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
  804. $err = $this->error; //Save any error
  805. if ($noerror or $close_on_error) {
  806. $this->close();
  807. $this->error = $err; //Restore any error from the quit command
  808. }
  809. return $noerror;
  810. }
  811.  
  812. /**
  813. * Send an SMTP RCPT command.
  814. * Sets the TO argument to $toaddr.
  815. * Returns true if the recipient was accepted false if it was rejected.
  816. * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
  817. * @param string $address The address the message is being sent to
  818. * @access public
  819. * @return boolean
  820. */
  821. public function recipient($address)
  822. {
  823. return $this->sendCommand(
  824. 'RCPT TO',
  825. 'RCPT TO:<' . $address . '>',
  826. array(250, 251)
  827. );
  828. }
  829.  
  830. /**
  831. * Send an SMTP RSET command.
  832. * Abort any transaction that is currently in progress.
  833. * Implements rfc 821: RSET <CRLF>
  834. * @access public
  835. * @return boolean True on success.
  836. */
  837. public function reset()
  838. {
  839. return $this->sendCommand('RSET', 'RSET', 250);
  840. }
  841.  
  842. /**
  843. * Send a command to an SMTP server and check its return code.
  844. * @param string $command The command name - not sent to the server
  845. * @param string $commandstring The actual command to send
  846. * @param integer|array $expect One or more expected integer success codes
  847. * @access protected
  848. * @return boolean True on success.
  849. */
  850. protected function sendCommand($command, $commandstring, $expect)
  851. {
  852. if (!$this->connected()) {
  853. $this->setError("Called $command without being connected");
  854. return false;
  855. }
  856. //Reject line breaks in all commands
  857. if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
  858. $this->setError("Command '$command' contained line breaks");
  859. return false;
  860. }
  861. $this->client_send($commandstring . self::CRLF);
  862.  
  863. $this->last_reply = $this->get_lines();
  864. // Fetch SMTP code and possible error code explanation
  865. $matches = array();
  866. if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) {
  867. $code = $matches[1];
  868. $code_ex = (count($matches) > 2 ? $matches[2] : null);
  869. // Cut off error code from each response line
  870. $detail = preg_replace(
  871. "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m",
  872. '',
  873. $this->last_reply
  874. );
  875. } else {
  876. // Fall back to simple parsing if regex fails
  877. $code = substr($this->last_reply, 0, 3);
  878. $code_ex = null;
  879. $detail = substr($this->last_reply, 4);
  880. }
  881.  
  882. $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
  883.  
  884. if (!in_array($code, (array)$expect)) {
  885. $this->setError(
  886. "$command command failed",
  887. $detail,
  888. $code,
  889. $code_ex
  890. );
  891. $this->edebug(
  892. 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
  893. self::DEBUG_CLIENT
  894. );
  895. return false;
  896. }
  897.  
  898. $this->setError('');
  899. return true;
  900. }
  901.  
  902. /**
  903. * Send an SMTP SAML command.
  904. * Starts a mail transaction from the email address specified in $from.
  905. * Returns true if successful or false otherwise. If True
  906. * the mail transaction is started and then one or more recipient
  907. * commands may be called followed by a data command. This command
  908. * will send the message to the users terminal if they are logged
  909. * in and send them an email.
  910. * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
  911. * @param string $from The address the message is from
  912. * @access public
  913. * @return boolean
  914. */
  915. public function sendAndMail($from)
  916. {
  917. return $this->sendCommand('SAML', "SAML FROM:$from", 250);
  918. }
  919.  
  920. /**
  921. * Send an SMTP VRFY command.
  922. * @param string $name The name to verify
  923. * @access public
  924. * @return boolean
  925. */
  926. public function verify($name)
  927. {
  928. return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
  929. }
  930.  
  931. /**
  932. * Send an SMTP NOOP command.
  933. * Used to keep keep-alives alive, doesn't actually do anything
  934. * @access public
  935. * @return boolean
  936. */
  937. public function noop()
  938. {
  939. return $this->sendCommand('NOOP', 'NOOP', 250);
  940. }
  941.  
  942. /**
  943. * Send an SMTP TURN command.
  944. * This is an optional command for SMTP that this class does not support.
  945. * This method is here to make the RFC821 Definition complete for this class
  946. * and _may_ be implemented in future
  947. * Implements from rfc 821: TURN <CRLF>
  948. * @access public
  949. * @return boolean
  950. */
  951. public function turn()
  952. {
  953. $this->setError('The SMTP TURN command is not implemented');
  954. $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
  955. return false;
  956. }
  957.  
  958. /**
  959. * Send raw data to the server.
  960. * @param string $data The data to send
  961. * @access public
  962. * @return integer|boolean The number of bytes sent to the server or false on error
  963. */
  964. public function client_send($data)
  965. {
  966. $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
  967. return fwrite($this->smtp_conn, $data);
  968. }
  969.  
  970. /**
  971. * Get the latest error.
  972. * @access public
  973. * @return array
  974. */
  975. public function getError()
  976. {
  977. return $this->error;
  978. }
  979.  
  980. /**
  981. * Get SMTP extensions available on the server
  982. * @access public
  983. * @return array|null
  984. */
  985. public function getServerExtList()
  986. {
  987. return $this->server_caps;
  988. }
  989.  
  990. /**
  991. * A multipurpose method
  992. * The method works in three ways, dependent on argument value and current state
  993. * 1. HELO/EHLO was not sent - returns null and set up $this->error
  994. * 2. HELO was sent
  995. * $name = 'HELO': returns server name
  996. * $name = 'EHLO': returns boolean false
  997. * $name = any string: returns null and set up $this->error
  998. * 3. EHLO was sent
  999. * $name = 'HELO'|'EHLO': returns server name
  1000. * $name = any string: if extension $name exists, returns boolean True
  1001. * or its options. Otherwise returns boolean False
  1002. * In other words, one can use this method to detect 3 conditions:
  1003. * - null returned: handshake was not or we don't know about ext (refer to $this->error)
  1004. * - false returned: the requested feature exactly not exists
  1005. * - positive value returned: the requested feature exists
  1006. * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
  1007. * @return mixed
  1008. */
  1009. public function getServerExt($name)
  1010. {
  1011. if (!$this->server_caps) {
  1012. $this->setError('No HELO/EHLO was sent');
  1013. return null;
  1014. }
  1015.  
  1016. // the tight logic knot ;)
  1017. if (!array_key_exists($name, $this->server_caps)) {
  1018. if ($name == 'HELO') {
  1019. return $this->server_caps['EHLO'];
  1020. }
  1021. if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) {
  1022. return false;
  1023. }
  1024. $this->setError('HELO handshake was used. Client knows nothing about server extensions');
  1025. return null;
  1026. }
  1027.  
  1028. return $this->server_caps[$name];
  1029. }
  1030.  
  1031. /**
  1032. * Get the last reply from the server.
  1033. * @access public
  1034. * @return string
  1035. */
  1036. public function getLastReply()
  1037. {
  1038. return $this->last_reply;
  1039. }
  1040.  
  1041. /**
  1042. * Read the SMTP server's response.
  1043. * Either before eof or socket timeout occurs on the operation.
  1044. * With SMTP we can tell if we have more lines to read if the
  1045. * 4th character is '-' symbol. If it is a space then we don't
  1046. * need to read anything else.
  1047. * @access protected
  1048. * @return string
  1049. */
  1050. protected function get_lines()
  1051. {
  1052. // If the connection is bad, give up straight away
  1053. if (!is_resource($this->smtp_conn)) {
  1054. return '';
  1055. }
  1056. $data = '';
  1057. $endtime = 0;
  1058. stream_set_timeout($this->smtp_conn, $this->Timeout);
  1059. if ($this->Timelimit > 0) {
  1060. $endtime = time() + $this->Timelimit;
  1061. }
  1062. while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
  1063. $str = @fgets($this->smtp_conn, 515);
  1064. $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL);
  1065. $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL);
  1066. $data .= $str;
  1067. // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
  1068. if ((isset($str[3]) and $str[3] == ' ')) {
  1069. break;
  1070. }
  1071. // Timed-out? Log and break
  1072. $info = stream_get_meta_data($this->smtp_conn);
  1073. if ($info['timed_out']) {
  1074. $this->edebug(
  1075. 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
  1076. self::DEBUG_LOWLEVEL
  1077. );
  1078. break;
  1079. }
  1080. // Now check if reads took too long
  1081. if ($endtime and time() > $endtime) {
  1082. $this->edebug(
  1083. 'SMTP -> get_lines(): timelimit reached ('.
  1084. $this->Timelimit . ' sec)',
  1085. self::DEBUG_LOWLEVEL
  1086. );
  1087. break;
  1088. }
  1089. }
  1090. return $data;
  1091. }
  1092.  
  1093. /**
  1094. * Enable or disable VERP address generation.
  1095. * @param boolean $enabled
  1096. */
  1097. public function setVerp($enabled = false)
  1098. {
  1099. $this->do_verp = $enabled;
  1100. }
  1101.  
  1102. /**
  1103. * Get VERP address generation mode.
  1104. * @return boolean
  1105. */
  1106. public function getVerp()
  1107. {
  1108. return $this->do_verp;
  1109. }
  1110.  
  1111. /**
  1112. * Set error messages and codes.
  1113. * @param string $message The error message
  1114. * @param string $detail Further detail on the error
  1115. * @param string $smtp_code An associated SMTP error code
  1116. * @param string $smtp_code_ex Extended SMTP code
  1117. */
  1118. protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
  1119. {
  1120. $this->error = array(
  1121. 'error' => $message,
  1122. 'detail' => $detail,
  1123. 'smtp_code' => $smtp_code,
  1124. 'smtp_code_ex' => $smtp_code_ex
  1125. );
  1126. }
  1127.  
  1128. /**
  1129. * Set debug output method.
  1130. * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
  1131. */
  1132. public function setDebugOutput($method = 'echo')
  1133. {
  1134. $this->Debugoutput = $method;
  1135. }
  1136.  
  1137. /**
  1138. * Get debug output method.
  1139. * @return string
  1140. */
  1141. public function getDebugOutput()
  1142. {
  1143. return $this->Debugoutput;
  1144. }
  1145.  
  1146. /**
  1147. * Set debug output level.
  1148. * @param integer $level
  1149. */
  1150. public function setDebugLevel($level = 0)
  1151. {
  1152. $this->do_debug = $level;
  1153. }
  1154.  
  1155. /**
  1156. * Get debug output level.
  1157. * @return integer
  1158. */
  1159. public function getDebugLevel()
  1160. {
  1161. return $this->do_debug;
  1162. }
  1163.  
  1164. /**
  1165. * Set SMTP timeout.
  1166. * @param integer $timeout
  1167. */
  1168. public function setTimeout($timeout = 0)
  1169. {
  1170. $this->Timeout = $timeout;
  1171. }
  1172.  
  1173. /**
  1174. * Get SMTP timeout.
  1175. * @return integer
  1176. */
  1177. public function getTimeout()
  1178. {
  1179. return $this->Timeout;
  1180. }
  1181. }