SmtpProtocolTest.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Mail
  17. * @subpackage UnitTests
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * Zend_Mail
  24. */
  25. require_once 'Zend/Mail.php';
  26. /**
  27. * Zend_Mail_Protocol_Smtp
  28. */
  29. require_once 'Zend/Mail/Protocol/Smtp.php';
  30. /**
  31. * PHPUnit test case
  32. */
  33. require_once 'PHPUnit/Framework/TestCase.php';
  34. /**
  35. * Test helper for configuration when run standalone
  36. */
  37. require_once dirname(__FILE__) . '/../../TestHelper.php';
  38. /**
  39. * @category Zend
  40. * @package Zend_Mail
  41. * @subpackage UnitTests
  42. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  43. * @license http://framework.zend.com/license/new-bsd New BSD License
  44. * @group Zend_Mail
  45. */
  46. class Zend_Mail_SmtpProtocolTest extends PHPUnit_Framework_TestCase
  47. {
  48. /**
  49. * @var Zend_Mail_Protocol_Smtp
  50. */
  51. protected $_protocol;
  52. public function setUp()
  53. {
  54. $this->_protocol = new ProtocolMock();
  55. }
  56. public function testEhlo()
  57. {
  58. $this->_connectAndEhlo(); // expects 250 response
  59. $this->assertEquals(array(
  60. '220 example.com ESMTP welcome',
  61. 'EHLO 127.0.0.1',
  62. '250 Hello 127.0.0.1, go ahead'
  63. ), $this->_protocol->dialog);
  64. }
  65. /**
  66. * @depends testEhlo
  67. * @expectedException Zend_Mail_Protocol_Exception
  68. */
  69. public function testHeloIsOnlyAllowedOncePerSession()
  70. {
  71. $this->_connectAndEhlo(); // do it once
  72. $this->_protocol->helo(); // do it again
  73. }
  74. /**
  75. * @depends testEhlo
  76. */
  77. public function testEhloFallsBackToHelo()
  78. {
  79. $this->_protocol->responseBuffer = array(
  80. '220 example.com ESMTP welcome',
  81. '500 Unrecognized', /* 500 or 502 error on unrecognized EHLO */
  82. '250 Hello 127.0.0.1, go ahead'
  83. );
  84. $this->_protocol->connect();
  85. $this->_protocol->helo();
  86. $this->assertEquals(array(
  87. '220 example.com ESMTP welcome',
  88. 'EHLO 127.0.0.1', // tries EHLO
  89. '500 Unrecognized', // .. which fails
  90. 'HELO 127.0.0.1', // continues to HELO
  91. '250 Hello 127.0.0.1, go ahead' // success
  92. ), $this->_protocol->dialog);
  93. }
  94. /**
  95. * @depends testEhlo
  96. */
  97. public function testMail()
  98. {
  99. $p = $this->_protocol;
  100. $expectedDialog = $this->_connectAndEhlo();
  101. $expectedDialog[] = 'MAIL FROM:<from@example.com>';
  102. $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
  103. $this->_protocol->mail('from@example.com');
  104. $this->assertEquals($expectedDialog, $this->_protocol->dialog);
  105. }
  106. /**
  107. * @depends testMail
  108. */
  109. public function testRcptExpects250()
  110. {
  111. $p = $this->_protocol;
  112. $expectedDialog = $this->_connectAndEhlo();
  113. $expectedDialog[] = 'MAIL FROM:<from@example.com>';
  114. $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
  115. $this->_protocol->mail('from@example.com');
  116. $expectedDialog[] = 'RCPT TO:<to@example.com>';
  117. $expectedDialog[] = $p->responseBuffer[] = '250 Recipient OK';
  118. $this->_protocol->rcpt('to@example.com');
  119. $this->assertEquals($expectedDialog, $this->_protocol->dialog);
  120. }
  121. /**
  122. * @depends testMail
  123. */
  124. public function testRcptExpects251()
  125. {
  126. $p = $this->_protocol;
  127. $expectedDialog = $this->_connectAndEhlo();
  128. $expectedDialog[] = 'MAIL FROM:<from@example.com>';
  129. $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
  130. $this->_protocol->mail('from@example.com');
  131. $expectedDialog[] = 'RCPT TO:<to@example.com>';
  132. $expectedDialog[] = $p->responseBuffer[] = '251 Recipient OK';
  133. $this->_protocol->rcpt('to@example.com');
  134. $this->assertEquals($expectedDialog, $this->_protocol->dialog);
  135. }
  136. /**
  137. * @depends testRcptExpects250
  138. */
  139. public function testData()
  140. {
  141. $p = $this->_protocol;
  142. $expectedDialog = $this->_connectAndEhlo();
  143. $expectedDialog[] = 'MAIL FROM:<from@example.com>';
  144. $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
  145. $this->_protocol->mail('from@example.com');
  146. $expectedDialog[] = 'RCPT TO:<to@example.com>';
  147. $expectedDialog[] = $p->responseBuffer[] = '250 Recipient OK';
  148. $this->_protocol->rcpt('to@example.com');
  149. $expectedDialog[] = 'DATA';
  150. $expectedDialog[] = $p->responseBuffer[] = '354 Go ahead';
  151. $expectedDialog[] = 'foo';
  152. $expectedDialog[] = '.'; // end of data marker
  153. $expectedDialog[] = $p->responseBuffer[] = '250 Accepted';
  154. $this->_protocol->data('foo');
  155. $this->assertEquals($expectedDialog, $this->_protocol->dialog);
  156. }
  157. /**
  158. * @depends testEhlo
  159. */
  160. public function testRset()
  161. {
  162. $expectedDialog = $this->_connectAndEhlo();
  163. $this->_protocol->responseBuffer = array('250 OK');
  164. $expectedDialog[] = 'RSET';
  165. $expectedDialog[] = '250 OK';
  166. $this->_protocol->rset();
  167. $this->assertEquals($expectedDialog, $this->_protocol->dialog);
  168. }
  169. /**
  170. * @depends testEhlo
  171. * @group ZF-1377
  172. */
  173. public function testRsetExpects220()
  174. {
  175. $expectedDialog = $this->_connectAndEhlo();
  176. // Microsoft ESMTP server responds to RSET with 220 rather than 250
  177. $this->_protocol->responseBuffer = array('220 OK');
  178. $expectedDialog[] = 'RSET';
  179. $expectedDialog[] = '220 OK';
  180. $this->_protocol->rset();
  181. $this->assertEquals($expectedDialog, $this->_protocol->dialog);
  182. }
  183. /**
  184. * @depends testEhlo
  185. */
  186. public function testQuit()
  187. {
  188. $p = $this->_protocol;
  189. $expectedDialog = $this->_connectAndEhlo();
  190. $expectedDialog[] = 'QUIT';
  191. $expectedDialog[] = $p->responseBuffer[] = '221 goodbye';
  192. $this->_protocol->quit();
  193. $this->assertEquals($expectedDialog, $this->_protocol->dialog);
  194. }
  195. /**
  196. * @depends testMail
  197. * @group ZF-8511
  198. */
  199. public function testMultilineResponsesAreNotTruncated()
  200. {
  201. $this->_connectAndEhlo();
  202. $this->_protocol->responseBuffer[] = '550-line one';
  203. $this->_protocol->responseBuffer[] = '550 line two';
  204. try {
  205. $this->_protocol->mail('from@example.com');
  206. $this->fail('Expected exception on 550 response');
  207. } catch (Zend_Mail_Protocol_Exception $e) {
  208. $this->assertEquals('line one line two', $e->getMessage());
  209. }
  210. }
  211. /**
  212. * @depends testMail
  213. * @group ZF-10249
  214. */
  215. public function testExceptionCodeIsSmtpStatusCode()
  216. {
  217. $p = $this->_protocol;
  218. $this->_connectAndEhlo();
  219. $p->responseBuffer[] = '550 failure';
  220. try {
  221. $this->_protocol->mail('from@example.com');
  222. $this->fail('Expected exception on 550 response');
  223. } catch (Zend_Mail_Protocol_Exception $e) {
  224. $this->assertEquals(550, $e->getCode());
  225. }
  226. }
  227. /**
  228. * @depends testMail
  229. * @expectedException Zend_Mail_Protocol_Exception
  230. */
  231. public function testRcptThrowsExceptionOnUnexpectedResponse()
  232. {
  233. $p = $this->_protocol;
  234. $expectedDialog = $this->_connectAndEhlo();
  235. $expectedDialog[] = 'MAIL FROM:<from@example.com>';
  236. $expectedDialog[] = $p->responseBuffer[] = '250 Sender accepted';
  237. $this->_protocol->mail('from@example.com');
  238. $expectedDialog[] = 'RCPT TO:<to@example.com>';
  239. $expectedDialog[] = $p->responseBuffer[] = '500 error';
  240. $this->_protocol->rcpt('to@example.com');
  241. }
  242. public function testMailBeforeHeloThrowsException()
  243. {
  244. try {
  245. $this->_protocol->mail('from@example.com');
  246. $this->fail('mail() before helo() should throw exception');
  247. } catch (Zend_Mail_Protocol_Exception $e) {
  248. $this->assertEquals('A valid session has not been started', $e->getMessage());
  249. }
  250. }
  251. /**
  252. * @depends testEhlo
  253. */
  254. public function testRcptBeforeMailThrowsException()
  255. {
  256. $this->_connectAndEhlo();
  257. try {
  258. $this->_protocol->rcpt('to@example.com');
  259. $this->fail('rcpt() before mail() should throw exception');
  260. } catch (Zend_Mail_Protocol_Exception $e) {
  261. $this->assertEquals('No sender reverse path has been supplied', $e->getMessage());
  262. }
  263. }
  264. /**
  265. * @depends testEhlo
  266. * @expectedException Zend_Mail_Protocol_Exception
  267. */
  268. public function testDataBeforeRcptThrowsException()
  269. {
  270. $this->_connectAndEhlo();
  271. $this->_protocol->data('foo');
  272. }
  273. /**
  274. * Performs the initial EHLO dialog
  275. */
  276. protected function _connectAndEhlo()
  277. {
  278. $this->_protocol->responseBuffer = array(
  279. '220 example.com ESMTP welcome',
  280. '250 Hello 127.0.0.1, go ahead'
  281. );
  282. $this->_protocol->connect();
  283. $this->_protocol->helo();
  284. return $this->_protocol->dialog;
  285. }
  286. }
  287. class ProtocolMock extends Zend_Mail_Protocol_Smtp
  288. {
  289. public $dialog = array();
  290. public $responseBuffer = array();
  291. /**
  292. * Override connect function to use local file for testing
  293. *
  294. * @param string $remote
  295. */
  296. protected function _connect($remote)
  297. {
  298. $this->_socket = tmpfile();
  299. }
  300. protected function _send($request)
  301. {
  302. $this->dialog[] = $request;
  303. }
  304. protected function _receive($timeout = null)
  305. {
  306. $line = array_shift($this->responseBuffer);
  307. $this->dialog[] = $line;
  308. return $line;
  309. }
  310. }