PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/standard/tags/release-0.1.2/library/Zend/Mail/Transport/Smtp.php

https://github.com/bhaumik25/zend-framework
PHP | 376 lines | 154 code | 48 blank | 174 comment | 17 complexity | 8585d003c1c0fdc225a687d2d84edcf2 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to version 1.0 of the Zend Framework
  8. * license, that is bundled with this package in the file LICENSE, and
  9. * is available through the world-wide-web at the following URL:
  10. * http://www.zend.com/license/framework/1_0.txt. If you did not receive
  11. * a copy of the Zend Framework license and are unable to obtain it
  12. * through the world-wide-web, please send a note to license@zend.com
  13. * so we can mail you a copy immediately.
  14. *
  15. * @package Zend_Mail
  16. * @subpackage Transport
  17. * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
  19. */
  20. /**
  21. * Zend_Mail_Transport_Exception
  22. */
  23. require_once 'Zend/Mail/Transport/Exception.php';
  24. /**
  25. * Zend_Mail_Transport_Interface
  26. */
  27. require_once 'Zend/Mail/Transport/Interface.php';
  28. /**
  29. * SMTP connection object
  30. * minimum implementation according to RFC2821:
  31. * EHLO, MAIL FROM, RCPT TO, DATA, RSET, NOOP, QUIT
  32. *
  33. * @package Zend_Mail
  34. * @subpackage Transport
  35. * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
  36. * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
  37. */
  38. class Zend_Mail_Transport_Smtp implements Zend_Mail_Transport_Interface {
  39. const CONNECTION_TIMEOUT = 30;
  40. const COMMUNICATION_TIMEOUT = 2;
  41. const LINEEND = "\r\n";
  42. const DEBUG = false;
  43. protected $_host;
  44. protected $_port;
  45. protected $_myName;
  46. /**
  47. * Last Response from the SMTP server, 1 Array Element per line
  48. *
  49. * @var array of strings
  50. */
  51. public $lastResponse = array();
  52. /**
  53. * Stream to SMTP Server
  54. *
  55. * @var Stream
  56. */
  57. protected $_con = null;
  58. /**
  59. * Constructor.
  60. *
  61. * @param string $host
  62. * @param int $port
  63. * @param string $myName (for use with HELO)
  64. */
  65. public function __construct($host, $port=25, $myName='127.0.0.1')
  66. {
  67. $this->_host = $host;
  68. $this->_port = $port;
  69. $this->_myName = $myName;
  70. }
  71. /**
  72. * Connect to the server with the parameters given
  73. * in the constructor and send "HELO". The connection
  74. * is immediately closed if an error occurs.
  75. *
  76. * @throws Zend_Mail_Transport_Exception
  77. */
  78. public function connect()
  79. {
  80. $errno = null;
  81. $errstr = null;
  82. // open connection
  83. $fp = stream_socket_client('tcp://'.$this->_host.':'.$this->_port, $errno, $errstr, self::CONNECTION_TIMEOUT);
  84. if ($fp===false) {
  85. if ($errno==0) {
  86. $msg = 'Could not open socket';
  87. } else {
  88. $msg = $errstr;
  89. }
  90. throw new Zend_Mail_Transport_Exception($msg);
  91. }
  92. $this->_con = $fp;
  93. try {
  94. $res = stream_set_timeout($this->_con, self::COMMUNICATION_TIMEOUT );
  95. if ($res === false) {
  96. throw new Zend_Mail_Transport_Exception('Could not set Stream Timeout');
  97. }
  98. /**
  99. * Now the connection is open. Wait for the welcome message:
  100. * welcome message has error code 220
  101. */
  102. $this->_expect(220);
  103. $this->helo($this->_myName);
  104. } catch (Zend_Mail_Transport_Exception $e) {
  105. fclose($fp);
  106. throw $e;
  107. }
  108. }
  109. /**
  110. * Sends EHLO along with the given machine name and
  111. * validates server response. If EHLO fails, HELO is
  112. * sent for compatibility with older MTAs.
  113. *
  114. * @param string $myname
  115. * @throws Zend_Mail_Transport_Exception
  116. */
  117. public function helo($myname)
  118. {
  119. $this->_send('EHLO '.$myname);
  120. try {
  121. $this->_expect(250); // Hello OK is code 250
  122. } catch (Zend_Mail_Transport_Exception $e) {
  123. // propably wrong status code, RFC 2821 requires sending HELO in this case:
  124. $this->_send('HELO '.$myname);
  125. $this->_expect(250); // if we get an exception here, we give up...
  126. }
  127. }
  128. /**
  129. * sends a MAIL command for the senders address
  130. * and validates the response.
  131. *
  132. * @param string $from_email
  133. * @throws Zend_Mail_Transport_Exception
  134. */
  135. public function mail_from($from_email)
  136. {
  137. $this->_send('MAIL FROM: <'.$from_email.'>');
  138. $this->_expect(250);
  139. }
  140. /**
  141. * sends a RCPT command for a recipient address
  142. * and validates the response.
  143. *
  144. * @param string $to
  145. * @throws Zend_Mail_Transport_Exception
  146. */
  147. public function rcpt_to($to)
  148. {
  149. $this->_send('RCPT TO: <' .$to. '>');
  150. $this->_expect(250,251);
  151. }
  152. /**
  153. * sends the DATA command followed by the
  154. * email content (headers plus body) folowed
  155. * by a dot and validates the response of the
  156. * server.
  157. *
  158. * @param string $data
  159. * @throws Zend_Mail_Transport_Exception
  160. */
  161. public function data($data)
  162. {
  163. $this->_send('DATA');
  164. $this->_expect(354);
  165. foreach(explode(self::LINEEND ,$data) AS $line) {
  166. if($line=='.') $line='..'; // important. replace single dot on a line
  167. $this->_send($line);
  168. }
  169. $this->_send('.');
  170. $this->_expect(250);
  171. }
  172. /**
  173. * Sends the RSET command end validates answer
  174. * Not used by Zend_Mail, can be used to restore a clean
  175. * smtp communication state when a transaction has
  176. * been cancelled.
  177. *
  178. * @throws Zend_Mail_Transport_Exception
  179. */
  180. public function rset()
  181. {
  182. $this->_send('RSET');
  183. $this->_expect(250);
  184. }
  185. /**
  186. * Sends the NOOP command end validates answer
  187. * Not used by Zend_Mail, could be used to keep a connection
  188. * alive or check if it is still open.
  189. *
  190. * @throws Zend_Mail_Transport_Exception
  191. */
  192. public function noop()
  193. {
  194. $this->_send('NOOP');
  195. $this->_expect(250);
  196. }
  197. /**
  198. * Sends the VRFY command end validates answer
  199. * The calling method needs to evaluate $this->lastResponse
  200. * This function was implemented for completeness only.
  201. * It is not used by Zend_Mail.
  202. *
  203. * @param string $user User Name or eMail to verify
  204. * @throws Zend_Mail_Transport_Exception
  205. */
  206. public function vrfy($user)
  207. {
  208. $this->_send('VRFY ' . $user);
  209. $this->_expect(250,251,252);
  210. }
  211. /**
  212. * Sends the QUIT command and validates answer
  213. *
  214. * @throws Zend_Mail_Transport_Exception
  215. */
  216. public function quit()
  217. {
  218. $this->_send('QUIT');
  219. $this->_expect(221);
  220. }
  221. /**
  222. * close an existing connection.
  223. * sends QUIT and closes stream.
  224. *
  225. * @throws Zend_Mail_Transport_Exception
  226. */
  227. public function disconnect()
  228. {
  229. $this->quit();
  230. fclose($this->_con);
  231. $this->_con = NULL;
  232. }
  233. /**
  234. * Read the response from the stream and
  235. * check for expected return code. throws
  236. * a Zend_Mail_Transport_Exception if an unexpected code
  237. * is returned
  238. *
  239. * @param int $val1
  240. * @param int $val2
  241. * @param int $val3
  242. * @throws Zend_Mail_Transport_Exception
  243. */
  244. protected function _expect($val1, $val2=null, $val3=null)
  245. {
  246. /**
  247. * according to the new RFC2821, a multiline response can be sent
  248. * so we now check if it is the case here.
  249. * a multiline response is structured as follows:
  250. * 250-ok welcome 127.0.0.1
  251. * 250-PIPELINING
  252. * 250 HELP
  253. * normal answer would be:
  254. *
  255. * 250 ok.
  256. */
  257. $this->lastResponse = array();
  258. do {
  259. // blocking
  260. $res = $this->_receive();
  261. // we might need this later
  262. $this->lastResponse[] = $res;
  263. // returncode is always 3 digits at the beginning of the line
  264. $errorcode = substr($res,0,3);
  265. if ($errorcode === NULL || ( ($errorcode!=$val1) && ($errorcode!=$val2) && ($errorcode!=$val3)) ) {
  266. throw new Zend_Mail_Transport_Exception($res);
  267. }
  268. } while($res[3]=='-');
  269. }
  270. /**
  271. * Get a line from the stream. includes error checking and debugging
  272. *
  273. * @return string
  274. * @throws Zend_Mail_Transport_Exception
  275. */
  276. protected function _receive()
  277. {
  278. $res = fgets($this->_con, 1024);
  279. if ($res === false) {
  280. throw new Zend_Mail_Transport_Exception('Could not read from SMTP server');
  281. }
  282. if (self::DEBUG) {
  283. echo "R: $res<br>\n";
  284. }
  285. return $res;
  286. }
  287. /**
  288. * Send the given string followed by a LINEEND to the server
  289. *
  290. * @param string $str
  291. * @throws Zend_Mail_Transport_Exception
  292. */
  293. protected function _send($str)
  294. {
  295. $res = fwrite($this->_con, $str.self::LINEEND);
  296. if ($res === false) {
  297. throw new Zend_Mail_Transport_Exception('Could not write to SMTP server');
  298. }
  299. if (self::DEBUG) {
  300. echo "S: $str<br>\n";
  301. }
  302. }
  303. /**
  304. * send an email
  305. *
  306. * @param Zend_Mail $mail
  307. * @param string $body
  308. * @param string $headers
  309. */
  310. public function sendMail(Zend_Mail $mail, $body, $headers)
  311. {
  312. $wasConnected = ($this->_con!==null); // check if the connection is already there
  313. if(!$wasConnected) $this->connect();// if not, establish a connection
  314. else $this->rset(); // if already connected, reset connection
  315. try {
  316. $this->mail_from($mail->getFrom());
  317. foreach($mail->getRecipients() AS $recipient) {
  318. $this->rcpt_to($recipient);
  319. }
  320. $this->data($headers."\r\n".$body);
  321. }
  322. catch (Zend_Mail_Transport_Exception $e) {
  323. if(!$wasConnected) $this->disconnect(); // remove connection if we made one
  324. throw $e;
  325. }
  326. if(!$wasConnected) $this->disconnect(); // remove connection if we made one
  327. }
  328. }