class.smtp.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  1. <?php
  2. /**
  3. * PHPMailer RFC821 SMTP email transport class.
  4. * Version 5.2.7
  5. * PHP version 5.0.0
  6. * @category PHP
  7. * @package PHPMailer
  8. * @link https://github.com/PHPMailer/PHPMailer/
  9. * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
  10. * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
  11. * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
  12. * @copyright 2013 Marcus Bointon
  13. * @copyright 2004 - 2008 Andy Prevost
  14. * @copyright 2010 - 2012 Jim Jagielski
  15. * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
  16. */
  17. /**
  18. * PHPMailer RFC821 SMTP email transport class.
  19. *
  20. * Implements RFC 821 SMTP commands
  21. * and provides some utility methods for sending mail to an SMTP server.
  22. *
  23. * PHP Version 5.0.0
  24. *
  25. * @category PHP
  26. * @package PHPMailer
  27. * @link https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php
  28. * @author Chris Ryan <unknown@example.com>
  29. * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
  30. * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
  31. */
  32. class SMTP
  33. {
  34. /**
  35. * The PHPMailer SMTP Version number.
  36. */
  37. const VERSION = '5.2.7';
  38. /**
  39. * SMTP line break constant.
  40. */
  41. const CRLF = "\r\n";
  42. /**
  43. * The SMTP port to use if one is not specified.
  44. */
  45. const DEFAULT_SMTP_PORT = 25;
  46. /**
  47. * The maximum line length allowed by RFC 2822 section 2.1.1
  48. */
  49. const MAX_LINE_LENGTH = 998;
  50. /**
  51. * The PHPMailer SMTP Version number.
  52. * @type string
  53. * @deprecated This should be a constant
  54. * @see SMTP::VERSION
  55. */
  56. public $Version = '5.2.7';
  57. /**
  58. * SMTP server port number.
  59. * @type int
  60. * @deprecated This is only ever ued as default value, so should be a constant
  61. * @see SMTP::DEFAULT_SMTP_PORT
  62. */
  63. public $SMTP_PORT = 25;
  64. /**
  65. * SMTP reply line ending
  66. * @type string
  67. * @deprecated Use the class constant instead
  68. * @see SMTP::CRLF
  69. */
  70. public $CRLF = "\r\n";
  71. /**
  72. * Debug output level.
  73. * Options:
  74. * 0: no output
  75. * 1: commands
  76. * 2: data and commands
  77. * 3: as 2 plus connection status
  78. * 4: low level data output
  79. * @type int
  80. */
  81. public $do_debug = 0;
  82. /**
  83. * The function/method to use for debugging output.
  84. * Options: 'echo', 'html' or 'error_log'
  85. * @type string
  86. */
  87. public $Debugoutput = 'echo';
  88. /**
  89. * Whether to use VERP.
  90. * @type bool
  91. */
  92. public $do_verp = false;
  93. /**
  94. * The timeout value for connection, in seconds.
  95. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
  96. * @type int
  97. */
  98. public $Timeout = 300;
  99. /**
  100. * The SMTP timelimit value for reads, in seconds.
  101. * @type int
  102. */
  103. public $Timelimit = 30;
  104. /**
  105. * The socket for the server connection.
  106. * @type resource
  107. */
  108. protected $smtp_conn;
  109. /**
  110. * Error message, if any, for the last call.
  111. * @type string
  112. */
  113. protected $error = '';
  114. /**
  115. * The reply the server sent to us for HELO.
  116. * @type string
  117. */
  118. protected $helo_rply = '';
  119. /**
  120. * The most recent reply received from the server.
  121. * @type string
  122. */
  123. protected $last_reply = '';
  124. /**
  125. * Constructor.
  126. * @access public
  127. */
  128. public function __construct()
  129. {
  130. $this->smtp_conn = 0;
  131. $this->error = null;
  132. $this->helo_rply = null;
  133. $this->do_debug = 0;
  134. }
  135. /**
  136. * Output debugging info via a user-selected method.
  137. * @param string $str Debug string to output
  138. * @return void
  139. */
  140. protected function edebug($str)
  141. {
  142. switch ($this->Debugoutput) {
  143. case 'error_log':
  144. //Don't output, just log
  145. error_log($str);
  146. break;
  147. case 'html':
  148. //Cleans up output a bit for a better looking, HTML-safe output
  149. echo htmlentities(
  150. preg_replace('/[\r\n]+/', '', $str),
  151. ENT_QUOTES,
  152. 'UTF-8'
  153. )
  154. . "<br>\n";
  155. break;
  156. case 'echo':
  157. default:
  158. echo gmdate('Y-m-d H:i:s')."\t".trim($str)."\n";
  159. }
  160. }
  161. /**
  162. * Connect to an SMTP server.
  163. * @param string $host SMTP server IP or host name
  164. * @param int $port The port number to connect to
  165. * @param int $timeout How long to wait for the connection to open
  166. * @param array $options An array of options for stream_context_create()
  167. * @access public
  168. * @return bool
  169. */
  170. public function connect($host, $port = null, $timeout = 30, $options = array())
  171. {
  172. // Clear errors to avoid confusion
  173. $this->error = null;
  174. // Make sure we are __not__ connected
  175. if ($this->connected()) {
  176. // Already connected, generate error
  177. $this->error = array('error' => 'Already connected to a server');
  178. return false;
  179. }
  180. if (empty($port)) {
  181. $port = self::DEFAULT_SMTP_PORT;
  182. }
  183. // Connect to the SMTP server
  184. if ($this->do_debug >= 3) {
  185. $this->edebug('Connection: opening');
  186. }
  187. $errno = 0;
  188. $errstr = '';
  189. $socket_context = stream_context_create($options);
  190. //Suppress errors; connection failures are handled at a higher level
  191. $this->smtp_conn = @stream_socket_client(
  192. $host . ":" . $port,
  193. $errno,
  194. $errstr,
  195. $timeout,
  196. STREAM_CLIENT_CONNECT,
  197. $socket_context
  198. );
  199. // Verify we connected properly
  200. if (empty($this->smtp_conn)) {
  201. $this->error = array(
  202. 'error' => 'Failed to connect to server',
  203. 'errno' => $errno,
  204. 'errstr' => $errstr
  205. );
  206. if ($this->do_debug >= 1) {
  207. $this->edebug(
  208. 'SMTP ERROR: ' . $this->error['error']
  209. . ": $errstr ($errno)"
  210. );
  211. }
  212. return false;
  213. }
  214. if ($this->do_debug >= 3) {
  215. $this->edebug('Connection: opened');
  216. }
  217. // SMTP server can take longer to respond, give longer timeout for first read
  218. // Windows does not have support for this timeout function
  219. if (substr(PHP_OS, 0, 3) != 'WIN') {
  220. $max = ini_get('max_execution_time');
  221. if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
  222. @set_time_limit($timeout);
  223. }
  224. stream_set_timeout($this->smtp_conn, $timeout, 0);
  225. }
  226. // Get any announcement
  227. $announce = $this->get_lines();
  228. if ($this->do_debug >= 2) {
  229. $this->edebug('SERVER -> CLIENT: ' . $announce);
  230. }
  231. return true;
  232. }
  233. /**
  234. * Initiate a TLS (encrypted) session.
  235. * @access public
  236. * @return bool
  237. */
  238. public function startTLS()
  239. {
  240. if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
  241. return false;
  242. }
  243. // Begin encrypted connection
  244. if (!stream_socket_enable_crypto(
  245. $this->smtp_conn,
  246. true,
  247. STREAM_CRYPTO_METHOD_TLS_CLIENT
  248. )) {
  249. return false;
  250. }
  251. return true;
  252. }
  253. /**
  254. * Perform SMTP authentication.
  255. * Must be run after hello().
  256. * @see hello()
  257. * @param string $username The user name
  258. * @param string $password The password
  259. * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
  260. * @param string $realm The auth realm for NTLM
  261. * @param string $workstation The auth workstation for NTLM
  262. * @access public
  263. * @return bool True if successfully authenticated.
  264. */
  265. public function authenticate(
  266. $username,
  267. $password,
  268. $authtype = 'LOGIN',
  269. $realm = '',
  270. $workstation = ''
  271. ) {
  272. if (empty($authtype)) {
  273. $authtype = 'LOGIN';
  274. }
  275. switch ($authtype) {
  276. case 'PLAIN':
  277. // Start authentication
  278. if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
  279. return false;
  280. }
  281. // Send encoded username and password
  282. if (!$this->sendCommand(
  283. 'User & Password',
  284. base64_encode("\0" . $username . "\0" . $password),
  285. 235
  286. )
  287. ) {
  288. return false;
  289. }
  290. break;
  291. case 'LOGIN':
  292. // Start authentication
  293. if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
  294. return false;
  295. }
  296. if (!$this->sendCommand("Username", base64_encode($username), 334)) {
  297. return false;
  298. }
  299. if (!$this->sendCommand("Password", base64_encode($password), 235)) {
  300. return false;
  301. }
  302. break;
  303. case 'NTLM':
  304. /*
  305. * ntlm_sasl_client.php
  306. * Bundled with Permission
  307. *
  308. * How to telnet in windows:
  309. * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
  310. * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
  311. */
  312. require_once 'extras/ntlm_sasl_client.php';
  313. $temp = new stdClass();
  314. $ntlm_client = new ntlm_sasl_client_class;
  315. //Check that functions are available
  316. if (!$ntlm_client->Initialize($temp)) {
  317. $this->error = array('error' => $temp->error);
  318. if ($this->do_debug >= 1) {
  319. $this->edebug(
  320. 'You need to enable some modules in your php.ini file: '
  321. . $this->error['error']
  322. );
  323. }
  324. return false;
  325. }
  326. //msg1
  327. $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
  328. if (!$this->sendCommand(
  329. 'AUTH NTLM',
  330. 'AUTH NTLM ' . base64_encode($msg1),
  331. 334
  332. )
  333. ) {
  334. return false;
  335. }
  336. //Though 0 based, there is a white space after the 3 digit number
  337. //msg2
  338. $challenge = substr($this->last_reply, 3);
  339. $challenge = base64_decode($challenge);
  340. $ntlm_res = $ntlm_client->NTLMResponse(
  341. substr($challenge, 24, 8),
  342. $password
  343. );
  344. //msg3
  345. $msg3 = $ntlm_client->TypeMsg3(
  346. $ntlm_res,
  347. $username,
  348. $realm,
  349. $workstation
  350. );
  351. // send encoded username
  352. return $this->sendCommand('Username', base64_encode($msg3), 235);
  353. break;
  354. case 'CRAM-MD5':
  355. // Start authentication
  356. if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
  357. return false;
  358. }
  359. // Get the challenge
  360. $challenge = base64_decode(substr($this->last_reply, 4));
  361. // Build the response
  362. $response = $username . ' ' . $this->hmac($challenge, $password);
  363. // send encoded credentials
  364. return $this->sendCommand('Username', base64_encode($response), 235);
  365. break;
  366. }
  367. return true;
  368. }
  369. /**
  370. * Calculate an MD5 HMAC hash.
  371. * Works like hash_hmac('md5', $data, $key)
  372. * in case that function is not available
  373. * @param string $data The data to hash
  374. * @param string $key The key to hash with
  375. * @access protected
  376. * @return string
  377. */
  378. protected function hmac($data, $key)
  379. {
  380. if (function_exists('hash_hmac')) {
  381. return hash_hmac('md5', $data, $key);
  382. }
  383. // The following borrowed from
  384. // http://php.net/manual/en/function.mhash.php#27225
  385. // RFC 2104 HMAC implementation for php.
  386. // Creates an md5 HMAC.
  387. // Eliminates the need to install mhash to compute a HMAC
  388. // Hacked by Lance Rushing
  389. $b = 64; // byte length for md5
  390. if (strlen($key) > $b) {
  391. $key = pack('H*', md5($key));
  392. }
  393. $key = str_pad($key, $b, chr(0x00));
  394. $ipad = str_pad('', $b, chr(0x36));
  395. $opad = str_pad('', $b, chr(0x5c));
  396. $k_ipad = $key ^ $ipad;
  397. $k_opad = $key ^ $opad;
  398. return md5($k_opad . pack('H*', md5($k_ipad . $data)));
  399. }
  400. /**
  401. * Check connection state.
  402. * @access public
  403. * @return bool True if connected.
  404. */
  405. public function connected()
  406. {
  407. if (!empty($this->smtp_conn)) {
  408. $sock_status = stream_get_meta_data($this->smtp_conn);
  409. if ($sock_status['eof']) {
  410. // the socket is valid but we are not connected
  411. if ($this->do_debug >= 1) {
  412. $this->edebug(
  413. 'SMTP NOTICE: EOF caught while checking if connected'
  414. );
  415. }
  416. $this->close();
  417. return false;
  418. }
  419. return true; // everything looks good
  420. }
  421. return false;
  422. }
  423. /**
  424. * Close the socket and clean up the state of the class.
  425. * Don't use this function without first trying to use QUIT.
  426. * @see quit()
  427. * @access public
  428. * @return void
  429. */
  430. public function close()
  431. {
  432. $this->error = null; // so there is no confusion
  433. $this->helo_rply = null;
  434. if (!empty($this->smtp_conn)) {
  435. // close the connection and cleanup
  436. fclose($this->smtp_conn);
  437. if ($this->do_debug >= 3) {
  438. $this->edebug('Connection: closed');
  439. }
  440. $this->smtp_conn = 0;
  441. }
  442. }
  443. /**
  444. * Send an SMTP DATA command.
  445. * Issues a data command and sends the msg_data to the server,
  446. * finializing the mail transaction. $msg_data is the message
  447. * that is to be send with the headers. Each header needs to be
  448. * on a single line followed by a <CRLF> with the message headers
  449. * and the message body being separated by and additional <CRLF>.
  450. * Implements rfc 821: DATA <CRLF>
  451. * @param string $msg_data Message data to send
  452. * @access public
  453. * @return bool
  454. */
  455. public function data($msg_data)
  456. {
  457. if (!$this->sendCommand('DATA', 'DATA', 354)) {
  458. return false;
  459. }
  460. /* The server is ready to accept data!
  461. * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
  462. * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
  463. * smaller lines to fit within the limit.
  464. * We will also look for lines that start with a '.' and prepend an additional '.'.
  465. * NOTE: this does not count towards line-length limit.
  466. */
  467. // Normalize line breaks before exploding
  468. $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
  469. /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
  470. * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
  471. * process all lines before a blank line as headers.
  472. */
  473. $field = substr($lines[0], 0, strpos($lines[0], ':'));
  474. $in_headers = false;
  475. if (!empty($field) && strpos($field, ' ') === false) {
  476. $in_headers = true;
  477. }
  478. foreach ($lines as $line) {
  479. $lines_out = array();
  480. if ($in_headers and $line == '') {
  481. $in_headers = false;
  482. }
  483. // ok we need to break this line up into several smaller lines
  484. //This is a small micro-optimisation: isset($str[$len]) is equivalent to (strlen($str) > $len)
  485. while (isset($line[self::MAX_LINE_LENGTH])) {
  486. //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
  487. //so as to avoid breaking in the middle of a word
  488. $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
  489. if (!$pos) { //Deliberately matches both false and 0
  490. //No nice break found, add a hard break
  491. $pos = self::MAX_LINE_LENGTH - 1;
  492. $lines_out[] = substr($line, 0, $pos);
  493. $line = substr($line, $pos);
  494. } else {
  495. //Break at the found point
  496. $lines_out[] = substr($line, 0, $pos);
  497. //Move along by the amount we dealt with
  498. $line = substr($line, $pos + 1);
  499. }
  500. /* If processing headers add a LWSP-char to the front of new line
  501. * RFC822 section 3.1.1
  502. */
  503. if ($in_headers) {
  504. $line = "\t" . $line;
  505. }
  506. }
  507. $lines_out[] = $line;
  508. // Send the lines to the server
  509. foreach ($lines_out as $line_out) {
  510. //RFC2821 section 4.5.2
  511. if (!empty($line_out) and $line_out[0] == '.') {
  512. $line_out = '.' . $line_out;
  513. }
  514. $this->client_send($line_out . self::CRLF);
  515. }
  516. }
  517. // Message data has been sent, complete the command
  518. return $this->sendCommand('DATA END', '.', 250);
  519. }
  520. /**
  521. * Send an SMTP HELO or EHLO command.
  522. * Used to identify the sending server to the receiving server.
  523. * This makes sure that client and server are in a known state.
  524. * Implements RFC 821: HELO <SP> <domain> <CRLF>
  525. * and RFC 2821 EHLO.
  526. * @param string $host The host name or IP to connect to
  527. * @access public
  528. * @return bool
  529. */
  530. public function hello($host = '')
  531. {
  532. // Try extended hello first (RFC 2821)
  533. return (bool)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
  534. }
  535. /**
  536. * Send an SMTP HELO or EHLO command.
  537. * Low-level implementation used by hello()
  538. * @see hello()
  539. * @param string $hello The HELO string
  540. * @param string $host The hostname to say we are
  541. * @access protected
  542. * @return bool
  543. */
  544. protected function sendHello($hello, $host)
  545. {
  546. $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
  547. $this->helo_rply = $this->last_reply;
  548. return $noerror;
  549. }
  550. /**
  551. * Send an SMTP MAIL command.
  552. * Starts a mail transaction from the email address specified in
  553. * $from. Returns true if successful or false otherwise. If True
  554. * the mail transaction is started and then one or more recipient
  555. * commands may be called followed by a data command.
  556. * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
  557. * @param string $from Source address of this message
  558. * @access public
  559. * @return bool
  560. */
  561. public function mail($from)
  562. {
  563. $useVerp = ($this->do_verp ? ' XVERP' : '');
  564. return $this->sendCommand(
  565. 'MAIL FROM',
  566. 'MAIL FROM:<' . $from . '>' . $useVerp,
  567. 250
  568. );
  569. }
  570. /**
  571. * Send an SMTP QUIT command.
  572. * Closes the socket if there is no error or the $close_on_error argument is true.
  573. * Implements from rfc 821: QUIT <CRLF>
  574. * @param bool $close_on_error Should the connection close if an error occurs?
  575. * @access public
  576. * @return bool
  577. */
  578. public function quit($close_on_error = true)
  579. {
  580. $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
  581. $e = $this->error; //Save any error
  582. if ($noerror or $close_on_error) {
  583. $this->close();
  584. $this->error = $e; //Restore any error from the quit command
  585. }
  586. return $noerror;
  587. }
  588. /**
  589. * Send an SMTP RCPT command.
  590. * Sets the TO argument to $to.
  591. * Returns true if the recipient was accepted false if it was rejected.
  592. * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
  593. * @param string $to The address the message is being sent to
  594. * @access public
  595. * @return bool
  596. */
  597. public function recipient($to)
  598. {
  599. return $this->sendCommand(
  600. 'RCPT TO',
  601. 'RCPT TO:<' . $to . '>',
  602. array(250, 251)
  603. );
  604. }
  605. /**
  606. * Send an SMTP RSET command.
  607. * Abort any transaction that is currently in progress.
  608. * Implements rfc 821: RSET <CRLF>
  609. * @access public
  610. * @return bool True on success.
  611. */
  612. public function reset()
  613. {
  614. return $this->sendCommand('RSET', 'RSET', 250);
  615. }
  616. /**
  617. * Send a command to an SMTP server and check its return code.
  618. * @param string $command The command name - not sent to the server
  619. * @param string $commandstring The actual command to send
  620. * @param int|array $expect One or more expected integer success codes
  621. * @access protected
  622. * @return bool True on success.
  623. */
  624. protected function sendCommand($command, $commandstring, $expect)
  625. {
  626. if (!$this->connected()) {
  627. $this->error = array(
  628. 'error' => "Called $command without being connected"
  629. );
  630. return false;
  631. }
  632. $this->client_send($commandstring . self::CRLF);
  633. $reply = $this->get_lines();
  634. $code = substr($reply, 0, 3);
  635. if ($this->do_debug >= 2) {
  636. $this->edebug('SERVER -> CLIENT: ' . $reply);
  637. }
  638. if (!in_array($code, (array)$expect)) {
  639. $this->last_reply = null;
  640. $this->error = array(
  641. 'error' => "$command command failed",
  642. 'smtp_code' => $code,
  643. 'detail' => substr($reply, 4)
  644. );
  645. if ($this->do_debug >= 1) {
  646. $this->edebug(
  647. 'SMTP ERROR: ' . $this->error['error'] . ': ' . $reply
  648. );
  649. }
  650. return false;
  651. }
  652. $this->last_reply = $reply;
  653. $this->error = null;
  654. return true;
  655. }
  656. /**
  657. * Send an SMTP SAML command.
  658. * Starts a mail transaction from the email address specified in $from.
  659. * Returns true if successful or false otherwise. If True
  660. * the mail transaction is started and then one or more recipient
  661. * commands may be called followed by a data command. This command
  662. * will send the message to the users terminal if they are logged
  663. * in and send them an email.
  664. * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
  665. * @param string $from The address the message is from
  666. * @access public
  667. * @return bool
  668. */
  669. public function sendAndMail($from)
  670. {
  671. return $this->sendCommand('SAML', "SAML FROM:$from", 250);
  672. }
  673. /**
  674. * Send an SMTP VRFY command.
  675. * @param string $name The name to verify
  676. * @access public
  677. * @return bool
  678. */
  679. public function verify($name)
  680. {
  681. return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
  682. }
  683. /**
  684. * Send an SMTP NOOP command.
  685. * Used to keep keep-alives alive, doesn't actually do anything
  686. * @access public
  687. * @return bool
  688. */
  689. public function noop()
  690. {
  691. return $this->sendCommand('NOOP', 'NOOP', 250);
  692. }
  693. /**
  694. * Send an SMTP TURN command.
  695. * This is an optional command for SMTP that this class does not support.
  696. * This method is here to make the RFC821 Definition complete for this class
  697. * and _may_ be implemented in future
  698. * Implements from rfc 821: TURN <CRLF>
  699. * @access public
  700. * @return bool
  701. */
  702. public function turn()
  703. {
  704. $this->error = array(
  705. 'error' => 'The SMTP TURN command is not implemented'
  706. );
  707. if ($this->do_debug >= 1) {
  708. $this->edebug('SMTP NOTICE: ' . $this->error['error']);
  709. }
  710. return false;
  711. }
  712. /**
  713. * Send raw data to the server.
  714. * @param string $data The data to send
  715. * @access public
  716. * @return int|bool The number of bytes sent to the server or false on error
  717. */
  718. public function client_send($data)
  719. {
  720. if ($this->do_debug >= 1) {
  721. $this->edebug("CLIENT -> SERVER: $data");
  722. }
  723. return fwrite($this->smtp_conn, $data);
  724. }
  725. /**
  726. * Get the latest error.
  727. * @access public
  728. * @return array
  729. */
  730. public function getError()
  731. {
  732. return $this->error;
  733. }
  734. /**
  735. * Get the last reply from the server.
  736. * @access public
  737. * @return string
  738. */
  739. public function getLastReply()
  740. {
  741. return $this->last_reply;
  742. }
  743. /**
  744. * Read the SMTP server's response.
  745. * Either before eof or socket timeout occurs on the operation.
  746. * With SMTP we can tell if we have more lines to read if the
  747. * 4th character is '-' symbol. If it is a space then we don't
  748. * need to read anything else.
  749. * @access protected
  750. * @return string
  751. */
  752. protected function get_lines()
  753. {
  754. // If the connection is bad, give up straight away
  755. if (!is_resource($this->smtp_conn)) {
  756. return '';
  757. }
  758. $data = '';
  759. $endtime = 0;
  760. stream_set_timeout($this->smtp_conn, $this->Timeout);
  761. if ($this->Timelimit > 0) {
  762. $endtime = time() + $this->Timelimit;
  763. }
  764. while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
  765. $str = @fgets($this->smtp_conn, 515);
  766. if ($this->do_debug >= 4) {
  767. $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
  768. $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
  769. }
  770. $data .= $str;
  771. if ($this->do_debug >= 4) {
  772. $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
  773. }
  774. // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
  775. if ((isset($str[3]) and $str[3] == ' ')) {
  776. break;
  777. }
  778. // Timed-out? Log and break
  779. $info = stream_get_meta_data($this->smtp_conn);
  780. if ($info['timed_out']) {
  781. if ($this->do_debug >= 4) {
  782. $this->edebug(
  783. 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)'
  784. );
  785. }
  786. break;
  787. }
  788. // Now check if reads took too long
  789. if ($endtime) {
  790. if (time() > $endtime) {
  791. if ($this->do_debug >= 4) {
  792. $this->edebug(
  793. 'SMTP -> get_lines(): timelimit reached ('.
  794. $this->Timelimit . ' sec)'
  795. );
  796. }
  797. break;
  798. }
  799. }
  800. }
  801. return $data;
  802. }
  803. /**
  804. * Enable or disable VERP address generation.
  805. * @param bool $enabled
  806. */
  807. public function setVerp($enabled = false)
  808. {
  809. $this->do_verp = $enabled;
  810. }
  811. /**
  812. * Get VERP address generation mode.
  813. * @return bool
  814. */
  815. public function getVerp()
  816. {
  817. return $this->do_verp;
  818. }
  819. /**
  820. * Set debug output method.
  821. * @param string $method The function/method to use for debugging output.
  822. */
  823. public function setDebugOutput($method = 'echo')
  824. {
  825. $this->Debugoutput = $method;
  826. }
  827. /**
  828. * Get debug output method.
  829. * @return string
  830. */
  831. public function getDebugOutput()
  832. {
  833. return $this->Debugoutput;
  834. }
  835. /**
  836. * Set debug output level.
  837. * @param int $level
  838. */
  839. public function setDebugLevel($level = 0)
  840. {
  841. $this->do_debug = $level;
  842. }
  843. /**
  844. * Get debug output level.
  845. * @return int
  846. */
  847. public function getDebugLevel()
  848. {
  849. return $this->do_debug;
  850. }
  851. /**
  852. * Set SMTP timeout.
  853. * @param int $timeout
  854. */
  855. public function setTimeout($timeout = 0)
  856. {
  857. $this->Timeout = $timeout;
  858. }
  859. /**
  860. * Get SMTP timeout.
  861. * @return int
  862. */
  863. public function getTimeout()
  864. {
  865. return $this->Timeout;
  866. }
  867. }