PageRenderTime 40ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/Ip/Lib/PHPMailer/class.smtp.php

https://gitlab.com/x33n/ImpressPages
PHP | 1088 lines | 579 code | 138 blank | 371 comment | 127 complexity | 71edf85f25f4409f6b39283b5a179ccf MD5 | raw file
  1. <?php
  2. /*~ class.smtp.php
  3. .---------------------------------------------------------------------------.
  4. | Software: PHPMailer - PHP email class |
  5. | Version: 5.2.6 |
  6. | Site: https://github.com/PHPMailer/PHPMailer/ |
  7. | ------------------------------------------------------------------------- |
  8. | Admins: Marcus Bointon |
  9. | Admins: Jim Jagielski |
  10. | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
  11. | : Marcus Bointon (coolbru) phpmailer@synchromedia.co.uk |
  12. | : Jim Jagielski (jimjag) jimjag@gmail.com |
  13. | Founder: Brent R. Matzelle (original founder) |
  14. | Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. |
  15. | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. |
  16. | Copyright (c) 2001-2003, Brent R. Matzelle |
  17. | ------------------------------------------------------------------------- |
  18. | License: Distributed under the Lesser General Public License (LGPL) |
  19. | http://www.gnu.org/copyleft/lesser.html |
  20. | This program is distributed in the hope that it will be useful - WITHOUT |
  21. | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
  22. | FITNESS FOR A PARTICULAR PURPOSE. |
  23. '---------------------------------------------------------------------------'
  24. */
  25. /**
  26. * PHPMailer - PHP SMTP email transport class
  27. * NOTE: Designed for use with PHP version 5 and up
  28. * @package PHPMailer
  29. * @author Andy Prevost
  30. * @author Marcus Bointon
  31. * @copyright 2004 - 2008 Andy Prevost
  32. * @author Jim Jagielski
  33. * @copyright 2010 - 2012 Jim Jagielski
  34. * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
  35. */
  36. /**
  37. * PHP RFC821 SMTP client
  38. *
  39. * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error.
  40. * SMTP also provides some utility methods for sending mail to an SMTP server.
  41. * @author Chris Ryan
  42. * @package PHPMailer
  43. */
  44. class SMTP {
  45. /**
  46. * SMTP server port
  47. * @var int
  48. */
  49. public $SMTP_PORT = 25;
  50. /**
  51. * SMTP reply line ending (don't change)
  52. * @var string
  53. */
  54. public $CRLF = "\r\n";
  55. /**
  56. * Debug output level; 0 for no output
  57. * @var int
  58. */
  59. public $do_debug = 0;
  60. /**
  61. * Sets the function/method to use for debugging output.
  62. * Right now we only honor 'echo' or 'error_log'
  63. * @var string
  64. */
  65. public $Debugoutput = 'echo';
  66. /**
  67. * Sets VERP use on/off (default is off)
  68. * @var bool
  69. */
  70. public $do_verp = false;
  71. /**
  72. * Sets the SMTP timeout value for reads, in seconds
  73. * @var int
  74. */
  75. public $Timeout = 15;
  76. /**
  77. * Sets the SMTP timelimit value for reads, in seconds
  78. * @var int
  79. */
  80. public $Timelimit = 30;
  81. /**
  82. * Sets the SMTP PHPMailer Version number
  83. * @var string
  84. */
  85. public $Version = '5.2.6';
  86. /////////////////////////////////////////////////
  87. // PROPERTIES, PRIVATE AND PROTECTED
  88. /////////////////////////////////////////////////
  89. /**
  90. * @var resource The socket to the server
  91. */
  92. private $smtp_conn;
  93. /**
  94. * @var string Error message, if any, for the last call
  95. */
  96. private $error;
  97. /**
  98. * @var string The reply the server sent to us for HELO
  99. */
  100. private $helo_rply;
  101. /**
  102. * Outputs debugging info via user-defined method
  103. * @param string $str
  104. */
  105. private function edebug($str) {
  106. if ($this->Debugoutput == 'error_log') {
  107. error_log($str);
  108. } else {
  109. echo $str;
  110. }
  111. }
  112. /**
  113. * Initialize the class so that the data is in a known state.
  114. * @access public
  115. * @return SMTP
  116. */
  117. public function __construct() {
  118. $this->smtp_conn = 0;
  119. $this->error = null;
  120. $this->helo_rply = null;
  121. $this->do_debug = 0;
  122. }
  123. /////////////////////////////////////////////////
  124. // CONNECTION FUNCTIONS
  125. /////////////////////////////////////////////////
  126. /**
  127. * Connect to the server specified on the port specified.
  128. * If the port is not specified use the default SMTP_PORT.
  129. * If tval is specified then a connection will try and be
  130. * established with the server for that number of seconds.
  131. * If tval is not specified the default is 30 seconds to
  132. * try on the connection.
  133. *
  134. * SMTP CODE SUCCESS: 220
  135. * SMTP CODE FAILURE: 421
  136. * @access public
  137. * @param string $host
  138. * @param int $port
  139. * @param int $tval
  140. * @return bool
  141. */
  142. public function Connect($host, $port = 0, $tval = 30) {
  143. // set the error val to null so there is no confusion
  144. $this->error = null;
  145. // make sure we are __not__ connected
  146. if($this->connected()) {
  147. // already connected, generate error
  148. $this->error = array('error' => 'Already connected to a server');
  149. return false;
  150. }
  151. if(empty($port)) {
  152. $port = $this->SMTP_PORT;
  153. }
  154. // connect to the smtp server
  155. $this->smtp_conn = @fsockopen($host, // the host of the server
  156. $port, // the port to use
  157. $errno, // error number if any
  158. $errstr, // error message if any
  159. $tval); // give up after ? secs
  160. // verify we connected properly
  161. if(empty($this->smtp_conn)) {
  162. $this->error = array('error' => 'Failed to connect to server',
  163. 'errno' => $errno,
  164. 'errstr' => $errstr);
  165. if($this->do_debug >= 1) {
  166. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ": $errstr ($errno)" . $this->CRLF . '<br />');
  167. }
  168. return false;
  169. }
  170. // SMTP server can take longer to respond, give longer timeout for first read
  171. // Windows does not have support for this timeout function
  172. if(substr(PHP_OS, 0, 3) != 'WIN') {
  173. $max = ini_get('max_execution_time');
  174. if ($max != 0 && $tval > $max) { // don't bother if unlimited
  175. @set_time_limit($tval);
  176. }
  177. stream_set_timeout($this->smtp_conn, $tval, 0);
  178. }
  179. // get any announcement
  180. $announce = $this->get_lines();
  181. if($this->do_debug >= 2) {
  182. $this->edebug('SMTP -> FROM SERVER:' . $announce . $this->CRLF . '<br />');
  183. }
  184. return true;
  185. }
  186. /**
  187. * Initiate a TLS communication with the server.
  188. *
  189. * SMTP CODE 220 Ready to start TLS
  190. * SMTP CODE 501 Syntax error (no parameters allowed)
  191. * SMTP CODE 454 TLS not available due to temporary reason
  192. * @access public
  193. * @return bool success
  194. */
  195. public function StartTLS() {
  196. $this->error = null; # to avoid confusion
  197. if(!$this->connected()) {
  198. $this->error = array('error' => 'Called StartTLS() without being connected');
  199. return false;
  200. }
  201. $this->client_send('STARTTLS' . $this->CRLF);
  202. $rply = $this->get_lines();
  203. $code = substr($rply, 0, 3);
  204. if($this->do_debug >= 2) {
  205. $this->edebug('SMTP -> FROM SERVER:' . $rply . $this->CRLF . '<br />');
  206. }
  207. if($code != 220) {
  208. $this->error =
  209. array('error' => 'STARTTLS not accepted from server',
  210. 'smtp_code' => $code,
  211. 'smtp_msg' => substr($rply, 4));
  212. if($this->do_debug >= 1) {
  213. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  214. }
  215. return false;
  216. }
  217. // Begin encrypted connection
  218. if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
  219. return false;
  220. }
  221. return true;
  222. }
  223. /**
  224. * Performs SMTP authentication. Must be run after running the
  225. * Hello() method. Returns true if successfully authenticated.
  226. * @access public
  227. * @param string $username
  228. * @param string $password
  229. * @param string $authtype
  230. * @param string $realm
  231. * @param string $workstation
  232. * @return bool
  233. */
  234. public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') {
  235. if (empty($authtype)) {
  236. $authtype = 'LOGIN';
  237. }
  238. switch ($authtype) {
  239. case 'PLAIN':
  240. // Start authentication
  241. $this->client_send('AUTH PLAIN' . $this->CRLF);
  242. $rply = $this->get_lines();
  243. $code = substr($rply, 0, 3);
  244. if($code != 334) {
  245. $this->error =
  246. array('error' => 'AUTH not accepted from server',
  247. 'smtp_code' => $code,
  248. 'smtp_msg' => substr($rply, 4));
  249. if($this->do_debug >= 1) {
  250. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  251. }
  252. return false;
  253. }
  254. // Send encoded username and password
  255. $this->client_send(base64_encode("\0".$username."\0".$password) . $this->CRLF);
  256. $rply = $this->get_lines();
  257. $code = substr($rply, 0, 3);
  258. if($code != 235) {
  259. $this->error =
  260. array('error' => 'Authentication not accepted from server',
  261. 'smtp_code' => $code,
  262. 'smtp_msg' => substr($rply, 4));
  263. if($this->do_debug >= 1) {
  264. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  265. }
  266. return false;
  267. }
  268. break;
  269. case 'LOGIN':
  270. // Start authentication
  271. $this->client_send('AUTH LOGIN' . $this->CRLF);
  272. $rply = $this->get_lines();
  273. $code = substr($rply, 0, 3);
  274. if($code != 334) {
  275. $this->error =
  276. array('error' => 'AUTH not accepted from server',
  277. 'smtp_code' => $code,
  278. 'smtp_msg' => substr($rply, 4));
  279. if($this->do_debug >= 1) {
  280. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  281. }
  282. return false;
  283. }
  284. // Send encoded username
  285. $this->client_send(base64_encode($username) . $this->CRLF);
  286. $rply = $this->get_lines();
  287. $code = substr($rply, 0, 3);
  288. if($code != 334) {
  289. $this->error =
  290. array('error' => 'Username not accepted from server',
  291. 'smtp_code' => $code,
  292. 'smtp_msg' => substr($rply, 4));
  293. if($this->do_debug >= 1) {
  294. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  295. }
  296. return false;
  297. }
  298. // Send encoded password
  299. $this->client_send(base64_encode($password) . $this->CRLF);
  300. $rply = $this->get_lines();
  301. $code = substr($rply, 0, 3);
  302. if($code != 235) {
  303. $this->error =
  304. array('error' => 'Password not accepted from server',
  305. 'smtp_code' => $code,
  306. 'smtp_msg' => substr($rply, 4));
  307. if($this->do_debug >= 1) {
  308. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  309. }
  310. return false;
  311. }
  312. break;
  313. case 'NTLM':
  314. /*
  315. * ntlm_sasl_client.php
  316. ** Bundled with Permission
  317. **
  318. ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
  319. ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
  320. */
  321. require_once 'extras/ntlm_sasl_client.php';
  322. $temp = new stdClass();
  323. $ntlm_client = new ntlm_sasl_client_class;
  324. if(! $ntlm_client->Initialize($temp)){//let's test if every function its available
  325. $this->error = array('error' => $temp->error);
  326. if($this->do_debug >= 1) {
  327. $this->edebug('You need to enable some modules in your php.ini file: ' . $this->error['error'] . $this->CRLF);
  328. }
  329. return false;
  330. }
  331. $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1
  332. $this->client_send('AUTH NTLM ' . base64_encode($msg1) . $this->CRLF);
  333. $rply = $this->get_lines();
  334. $code = substr($rply, 0, 3);
  335. if($code != 334) {
  336. $this->error =
  337. array('error' => 'AUTH not accepted from server',
  338. 'smtp_code' => $code,
  339. 'smtp_msg' => substr($rply, 4));
  340. if($this->do_debug >= 1) {
  341. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF);
  342. }
  343. return false;
  344. }
  345. $challenge = substr($rply, 3);//though 0 based, there is a white space after the 3 digit number....//msg2
  346. $challenge = base64_decode($challenge);
  347. $ntlm_res = $ntlm_client->NTLMResponse(substr($challenge, 24, 8), $password);
  348. $msg3 = $ntlm_client->TypeMsg3($ntlm_res, $username, $realm, $workstation);//msg3
  349. // Send encoded username
  350. $this->client_send(base64_encode($msg3) . $this->CRLF);
  351. $rply = $this->get_lines();
  352. $code = substr($rply, 0, 3);
  353. if($code != 235) {
  354. $this->error =
  355. array('error' => 'Could not authenticate',
  356. 'smtp_code' => $code,
  357. 'smtp_msg' => substr($rply, 4));
  358. if($this->do_debug >= 1) {
  359. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF);
  360. }
  361. return false;
  362. }
  363. break;
  364. case 'CRAM-MD5':
  365. // Start authentication
  366. $this->client_send('AUTH CRAM-MD5' . $this->CRLF);
  367. $rply = $this->get_lines();
  368. $code = substr($rply, 0, 3);
  369. if($code != 334) {
  370. $this->error =
  371. array('error' => 'AUTH not accepted from server',
  372. 'smtp_code' => $code,
  373. 'smtp_msg' => substr($rply, 4));
  374. if($this->do_debug >= 1) {
  375. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  376. }
  377. return false;
  378. }
  379. // Get the challenge
  380. $challenge = base64_decode(substr($rply, 4));
  381. // Build the response
  382. $response = $username . ' ' . $this->hmac($challenge, $password);
  383. // Send encoded credentials
  384. $this->client_send(base64_encode($response) . $this->CRLF);
  385. $rply = $this->get_lines();
  386. $code = substr($rply, 0, 3);
  387. if($code != 334) {
  388. $this->error =
  389. array('error' => 'Credentials not accepted from server',
  390. 'smtp_code' => $code,
  391. 'smtp_msg' => substr($rply, 4));
  392. if($this->do_debug >= 1) {
  393. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  394. }
  395. return false;
  396. }
  397. break;
  398. }
  399. return true;
  400. }
  401. /**
  402. * Works like hash_hmac('md5', $data, $key) in case that function is not available
  403. * @access private
  404. * @param string $data
  405. * @param string $key
  406. * @return string
  407. */
  408. private function hmac($data, $key) {
  409. if (function_exists('hash_hmac')) {
  410. return hash_hmac('md5', $data, $key);
  411. }
  412. // The following borrowed from http://php.net/manual/en/function.mhash.php#27225
  413. // RFC 2104 HMAC implementation for php.
  414. // Creates an md5 HMAC.
  415. // Eliminates the need to install mhash to compute a HMAC
  416. // Hacked by Lance Rushing
  417. $b = 64; // byte length for md5
  418. if (strlen($key) > $b) {
  419. $key = pack('H*', md5($key));
  420. }
  421. $key = str_pad($key, $b, chr(0x00));
  422. $ipad = str_pad('', $b, chr(0x36));
  423. $opad = str_pad('', $b, chr(0x5c));
  424. $k_ipad = $key ^ $ipad ;
  425. $k_opad = $key ^ $opad;
  426. return md5($k_opad . pack('H*', md5($k_ipad . $data)));
  427. }
  428. /**
  429. * Returns true if connected to a server otherwise false
  430. * @access public
  431. * @return bool
  432. */
  433. public function Connected() {
  434. if(!empty($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. if($this->do_debug >= 1) {
  439. $this->edebug('SMTP -> NOTICE:' . $this->CRLF . 'EOF caught while checking if connected');
  440. }
  441. $this->Close();
  442. return false;
  443. }
  444. return true; // everything looks good
  445. }
  446. return false;
  447. }
  448. /**
  449. * Closes the socket and cleans up the state of the class.
  450. * It is not considered good to use this function without
  451. * first trying to use QUIT.
  452. * @access public
  453. * @return void
  454. */
  455. public function Close() {
  456. $this->error = null; // so there is no confusion
  457. $this->helo_rply = null;
  458. if(!empty($this->smtp_conn)) {
  459. // close the connection and cleanup
  460. fclose($this->smtp_conn);
  461. $this->smtp_conn = 0;
  462. }
  463. }
  464. /////////////////////////////////////////////////
  465. // SMTP COMMANDS
  466. /////////////////////////////////////////////////
  467. /**
  468. * Issues a data command and sends the msg_data to the server
  469. * finializing the mail transaction. $msg_data is the message
  470. * that is to be send with the headers. Each header needs to be
  471. * on a single line followed by a <CRLF> with the message headers
  472. * and the message body being seperated by and additional <CRLF>.
  473. *
  474. * Implements rfc 821: DATA <CRLF>
  475. *
  476. * SMTP CODE INTERMEDIATE: 354
  477. * [data]
  478. * <CRLF>.<CRLF>
  479. * SMTP CODE SUCCESS: 250
  480. * SMTP CODE FAILURE: 552, 554, 451, 452
  481. * SMTP CODE FAILURE: 451, 554
  482. * SMTP CODE ERROR : 500, 501, 503, 421
  483. * @access public
  484. * @param string $msg_data
  485. * @return bool
  486. */
  487. public function Data($msg_data) {
  488. $this->error = null; // so no confusion is caused
  489. if(!$this->connected()) {
  490. $this->error = array(
  491. 'error' => 'Called Data() without being connected');
  492. return false;
  493. }
  494. $this->client_send('DATA' . $this->CRLF);
  495. $rply = $this->get_lines();
  496. $code = substr($rply, 0, 3);
  497. if($this->do_debug >= 2) {
  498. $this->edebug('SMTP -> FROM SERVER:' . $rply . $this->CRLF . '<br />');
  499. }
  500. if($code != 354) {
  501. $this->error =
  502. array('error' => 'DATA command not accepted from server',
  503. 'smtp_code' => $code,
  504. 'smtp_msg' => substr($rply, 4));
  505. if($this->do_debug >= 1) {
  506. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  507. }
  508. return false;
  509. }
  510. /* the server is ready to accept data!
  511. * according to rfc 821 we should not send more than 1000
  512. * including the CRLF
  513. * characters on a single line so we will break the data up
  514. * into lines by \r and/or \n then if needed we will break
  515. * each of those into smaller lines to fit within the limit.
  516. * in addition we will be looking for lines that start with
  517. * a period '.' and append and additional period '.' to that
  518. * line. NOTE: this does not count towards limit.
  519. */
  520. // normalize the line breaks so we know the explode works
  521. $msg_data = str_replace("\r\n", "\n", $msg_data);
  522. $msg_data = str_replace("\r", "\n", $msg_data);
  523. $lines = explode("\n", $msg_data);
  524. /* we need to find a good way to determine is headers are
  525. * in the msg_data or if it is a straight msg body
  526. * currently I am assuming rfc 822 definitions of msg headers
  527. * and if the first field of the first line (':' sperated)
  528. * does not contain a space then it _should_ be a header
  529. * and we can process all lines before a blank "" line as
  530. * headers.
  531. */
  532. $field = substr($lines[0], 0, strpos($lines[0], ':'));
  533. $in_headers = false;
  534. if(!empty($field) && !strstr($field, ' ')) {
  535. $in_headers = true;
  536. }
  537. $max_line_length = 998; // used below; set here for ease in change
  538. while(list(, $line) = @each($lines)) {
  539. $lines_out = null;
  540. if($line == '' && $in_headers) {
  541. $in_headers = false;
  542. }
  543. // ok we need to break this line up into several smaller lines
  544. while(strlen($line) > $max_line_length) {
  545. $pos = strrpos(substr($line, 0, $max_line_length), ' ');
  546. // Patch to fix DOS attack
  547. if(!$pos) {
  548. $pos = $max_line_length - 1;
  549. $lines_out[] = substr($line, 0, $pos);
  550. $line = substr($line, $pos);
  551. } else {
  552. $lines_out[] = substr($line, 0, $pos);
  553. $line = substr($line, $pos + 1);
  554. }
  555. /* if processing headers add a LWSP-char to the front of new line
  556. * rfc 822 on long msg headers
  557. */
  558. if($in_headers) {
  559. $line = "\t" . $line;
  560. }
  561. }
  562. $lines_out[] = $line;
  563. // send the lines to the server
  564. while(list(, $line_out) = @each($lines_out)) {
  565. if(strlen($line_out) > 0)
  566. {
  567. if(substr($line_out, 0, 1) == '.') {
  568. $line_out = '.' . $line_out;
  569. }
  570. }
  571. $this->client_send($line_out . $this->CRLF);
  572. }
  573. }
  574. // message data has been sent
  575. $this->client_send($this->CRLF . '.' . $this->CRLF);
  576. $rply = $this->get_lines();
  577. $code = substr($rply, 0, 3);
  578. if($this->do_debug >= 2) {
  579. $this->edebug('SMTP -> FROM SERVER:' . $rply . $this->CRLF . '<br />');
  580. }
  581. if($code != 250) {
  582. $this->error =
  583. array('error' => 'DATA not accepted from server',
  584. 'smtp_code' => $code,
  585. 'smtp_msg' => substr($rply, 4));
  586. if($this->do_debug >= 1) {
  587. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  588. }
  589. return false;
  590. }
  591. return true;
  592. }
  593. /**
  594. * Sends the HELO command to the smtp server.
  595. * This makes sure that we and the server are in
  596. * the same known state.
  597. *
  598. * Implements from rfc 821: HELO <SP> <domain> <CRLF>
  599. *
  600. * SMTP CODE SUCCESS: 250
  601. * SMTP CODE ERROR : 500, 501, 504, 421
  602. * @access public
  603. * @param string $host
  604. * @return bool
  605. */
  606. public function Hello($host = '') {
  607. $this->error = null; // so no confusion is caused
  608. if(!$this->connected()) {
  609. $this->error = array(
  610. 'error' => 'Called Hello() without being connected');
  611. return false;
  612. }
  613. // if hostname for HELO was not specified send default
  614. if(empty($host)) {
  615. // determine appropriate default to send to server
  616. $host = 'localhost';
  617. }
  618. // Send extended hello first (RFC 2821)
  619. if(!$this->SendHello('EHLO', $host)) {
  620. if(!$this->SendHello('HELO', $host)) {
  621. return false;
  622. }
  623. }
  624. return true;
  625. }
  626. /**
  627. * Sends a HELO/EHLO command.
  628. * @access private
  629. * @param string $hello
  630. * @param string $host
  631. * @return bool
  632. */
  633. private function SendHello($hello, $host) {
  634. $this->client_send($hello . ' ' . $host . $this->CRLF);
  635. $rply = $this->get_lines();
  636. $code = substr($rply, 0, 3);
  637. if($this->do_debug >= 2) {
  638. $this->edebug('SMTP -> FROM SERVER: ' . $rply . $this->CRLF . '<br />');
  639. }
  640. if($code != 250) {
  641. $this->error =
  642. array('error' => $hello . ' not accepted from server',
  643. 'smtp_code' => $code,
  644. 'smtp_msg' => substr($rply, 4));
  645. if($this->do_debug >= 1) {
  646. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  647. }
  648. return false;
  649. }
  650. $this->helo_rply = $rply;
  651. return true;
  652. }
  653. /**
  654. * Starts a mail transaction from the email address specified in
  655. * $from. Returns true if successful or false otherwise. If True
  656. * the mail transaction is started and then one or more Recipient
  657. * commands may be called followed by a Data command.
  658. *
  659. * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
  660. *
  661. * SMTP CODE SUCCESS: 250
  662. * SMTP CODE SUCCESS: 552, 451, 452
  663. * SMTP CODE SUCCESS: 500, 501, 421
  664. * @access public
  665. * @param string $from
  666. * @return bool
  667. */
  668. public function Mail($from) {
  669. $this->error = null; // so no confusion is caused
  670. if(!$this->connected()) {
  671. $this->error = array(
  672. 'error' => 'Called Mail() without being connected');
  673. return false;
  674. }
  675. $useVerp = ($this->do_verp ? ' XVERP' : '');
  676. $this->client_send('MAIL FROM:<' . $from . '>' . $useVerp . $this->CRLF);
  677. $rply = $this->get_lines();
  678. $code = substr($rply, 0, 3);
  679. if($this->do_debug >= 2) {
  680. $this->edebug('SMTP -> FROM SERVER:' . $rply . $this->CRLF . '<br />');
  681. }
  682. if($code != 250) {
  683. $this->error =
  684. array('error' => 'MAIL not accepted from server',
  685. 'smtp_code' => $code,
  686. 'smtp_msg' => substr($rply, 4));
  687. if($this->do_debug >= 1) {
  688. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  689. }
  690. return false;
  691. }
  692. return true;
  693. }
  694. /**
  695. * Sends the quit command to the server and then closes the socket
  696. * if there is no error or the $close_on_error argument is true.
  697. *
  698. * Implements from rfc 821: QUIT <CRLF>
  699. *
  700. * SMTP CODE SUCCESS: 221
  701. * SMTP CODE ERROR : 500
  702. * @access public
  703. * @param bool $close_on_error
  704. * @return bool
  705. */
  706. public function Quit($close_on_error = true) {
  707. $this->error = null; // so there is no confusion
  708. if(!$this->connected()) {
  709. $this->error = array(
  710. 'error' => 'Called Quit() without being connected');
  711. return false;
  712. }
  713. // send the quit command to the server
  714. $this->client_send('quit' . $this->CRLF);
  715. // get any good-bye messages
  716. $byemsg = $this->get_lines();
  717. if($this->do_debug >= 2) {
  718. $this->edebug('SMTP -> FROM SERVER:' . $byemsg . $this->CRLF . '<br />');
  719. }
  720. $rval = true;
  721. $e = null;
  722. $code = substr($byemsg, 0, 3);
  723. if($code != 221) {
  724. // use e as a tmp var cause Close will overwrite $this->error
  725. $e = array('error' => 'SMTP server rejected quit command',
  726. 'smtp_code' => $code,
  727. 'smtp_rply' => substr($byemsg, 4));
  728. $rval = false;
  729. if($this->do_debug >= 1) {
  730. $this->edebug('SMTP -> ERROR: ' . $e['error'] . ': ' . $byemsg . $this->CRLF . '<br />');
  731. }
  732. }
  733. if(empty($e) || $close_on_error) {
  734. $this->Close();
  735. }
  736. return $rval;
  737. }
  738. /**
  739. * Sends the command RCPT to the SMTP server with the TO: argument of $to.
  740. * Returns true if the recipient was accepted false if it was rejected.
  741. *
  742. * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
  743. *
  744. * SMTP CODE SUCCESS: 250, 251
  745. * SMTP CODE FAILURE: 550, 551, 552, 553, 450, 451, 452
  746. * SMTP CODE ERROR : 500, 501, 503, 421
  747. * @access public
  748. * @param string $to
  749. * @return bool
  750. */
  751. public function Recipient($to) {
  752. $this->error = null; // so no confusion is caused
  753. if(!$this->connected()) {
  754. $this->error = array(
  755. 'error' => 'Called Recipient() without being connected');
  756. return false;
  757. }
  758. $this->client_send('RCPT TO:<' . $to . '>' . $this->CRLF);
  759. $rply = $this->get_lines();
  760. $code = substr($rply, 0, 3);
  761. if($this->do_debug >= 2) {
  762. $this->edebug('SMTP -> FROM SERVER:' . $rply . $this->CRLF . '<br />');
  763. }
  764. if($code != 250 && $code != 251) {
  765. $this->error =
  766. array('error' => 'RCPT not accepted from server',
  767. 'smtp_code' => $code,
  768. 'smtp_msg' => substr($rply, 4));
  769. if($this->do_debug >= 1) {
  770. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  771. }
  772. return false;
  773. }
  774. return true;
  775. }
  776. /**
  777. * Sends the RSET command to abort and transaction that is
  778. * currently in progress. Returns true if successful false
  779. * otherwise.
  780. *
  781. * Implements rfc 821: RSET <CRLF>
  782. *
  783. * SMTP CODE SUCCESS: 250
  784. * SMTP CODE ERROR : 500, 501, 504, 421
  785. * @access public
  786. * @return bool
  787. */
  788. public function Reset() {
  789. $this->error = null; // so no confusion is caused
  790. if(!$this->connected()) {
  791. $this->error = array('error' => 'Called Reset() without being connected');
  792. return false;
  793. }
  794. $this->client_send('RSET' . $this->CRLF);
  795. $rply = $this->get_lines();
  796. $code = substr($rply, 0, 3);
  797. if($this->do_debug >= 2) {
  798. $this->edebug('SMTP -> FROM SERVER:' . $rply . $this->CRLF . '<br />');
  799. }
  800. if($code != 250) {
  801. $this->error =
  802. array('error' => 'RSET failed',
  803. 'smtp_code' => $code,
  804. 'smtp_msg' => substr($rply, 4));
  805. if($this->do_debug >= 1) {
  806. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  807. }
  808. return false;
  809. }
  810. return true;
  811. }
  812. /**
  813. * Starts a mail transaction from the email address specified in
  814. * $from. Returns true if successful or false otherwise. If True
  815. * the mail transaction is started and then one or more Recipient
  816. * commands may be called followed by a Data command. This command
  817. * will send the message to the users terminal if they are logged
  818. * in and send them an email.
  819. *
  820. * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
  821. *
  822. * SMTP CODE SUCCESS: 250
  823. * SMTP CODE SUCCESS: 552, 451, 452
  824. * SMTP CODE SUCCESS: 500, 501, 502, 421
  825. * @access public
  826. * @param string $from
  827. * @return bool
  828. */
  829. public function SendAndMail($from) {
  830. $this->error = null; // so no confusion is caused
  831. if(!$this->connected()) {
  832. $this->error = array(
  833. 'error' => 'Called SendAndMail() without being connected');
  834. return false;
  835. }
  836. $this->client_send('SAML FROM:' . $from . $this->CRLF);
  837. $rply = $this->get_lines();
  838. $code = substr($rply, 0, 3);
  839. if($this->do_debug >= 2) {
  840. $this->edebug('SMTP -> FROM SERVER:' . $rply . $this->CRLF . '<br />');
  841. }
  842. if($code != 250) {
  843. $this->error =
  844. array('error' => 'SAML not accepted from server',
  845. 'smtp_code' => $code,
  846. 'smtp_msg' => substr($rply, 4));
  847. if($this->do_debug >= 1) {
  848. $this->edebug('SMTP -> ERROR: ' . $this->error['error'] . ': ' . $rply . $this->CRLF . '<br />');
  849. }
  850. return false;
  851. }
  852. return true;
  853. }
  854. /**
  855. * This is an optional command for SMTP that this class does not
  856. * support. This method is here to make the RFC821 Definition
  857. * complete for this class and __may__ be implimented in the future
  858. *
  859. * Implements from rfc 821: TURN <CRLF>
  860. *
  861. * SMTP CODE SUCCESS: 250
  862. * SMTP CODE FAILURE: 502
  863. * SMTP CODE ERROR : 500, 503
  864. * @access public
  865. * @return bool
  866. */
  867. public function Turn() {
  868. $this->error = array('error' => 'This method, TURN, of the SMTP '.
  869. 'is not implemented');
  870. if($this->do_debug >= 1) {
  871. $this->edebug('SMTP -> NOTICE: ' . $this->error['error'] . $this->CRLF . '<br />');
  872. }
  873. return false;
  874. }
  875. /**
  876. * Sends data to the server
  877. * @param string $data
  878. * @access public
  879. * @return Integer number of bytes sent to the server or FALSE on error
  880. */
  881. public function client_send($data) {
  882. if ($this->do_debug >= 1) {
  883. $this->edebug("CLIENT -> SMTP: $data" . $this->CRLF . '<br />');
  884. }
  885. return fwrite($this->smtp_conn, $data);
  886. }
  887. /**
  888. * Get the current error
  889. * @access public
  890. * @return array
  891. */
  892. public function getError() {
  893. return $this->error;
  894. }
  895. /////////////////////////////////////////////////
  896. // INTERNAL FUNCTIONS
  897. /////////////////////////////////////////////////
  898. /**
  899. * Read in as many lines as possible
  900. * either before eof or socket timeout occurs on the operation.
  901. * With SMTP we can tell if we have more lines to read if the
  902. * 4th character is '-' symbol. If it is a space then we don't
  903. * need to read anything else.
  904. * @access private
  905. * @return string
  906. */
  907. private function get_lines() {
  908. $data = '';
  909. $endtime = 0;
  910. /* If for some reason the fp is bad, don't inf loop */
  911. if (!is_resource($this->smtp_conn)) {
  912. return $data;
  913. }
  914. stream_set_timeout($this->smtp_conn, $this->Timeout);
  915. if ($this->Timelimit > 0) {
  916. $endtime = time() + $this->Timelimit;
  917. }
  918. while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
  919. $str = @fgets($this->smtp_conn, 515);
  920. if($this->do_debug >= 4) {
  921. $this->edebug("SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '<br />');
  922. $this->edebug("SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '<br />');
  923. }
  924. $data .= $str;
  925. if($this->do_debug >= 4) {
  926. $this->edebug("SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '<br />');
  927. }
  928. // if 4th character is a space, we are done reading, break the loop
  929. if(substr($str, 3, 1) == ' ') { break; }
  930. // Timed-out? Log and break
  931. $info = stream_get_meta_data($this->smtp_conn);
  932. if ($info['timed_out']) {
  933. if($this->do_debug >= 4) {
  934. $this->edebug('SMTP -> get_lines(): timed-out (' . $this->Timeout . ' seconds) <br />');
  935. }
  936. break;
  937. }
  938. // Now check if reads took too long
  939. if ($endtime) {
  940. if (time() > $endtime) {
  941. if($this->do_debug >= 4) {
  942. $this->edebug('SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' seconds) <br />');
  943. }
  944. break;
  945. }
  946. }
  947. }
  948. return $data;
  949. }
  950. }