SmtpTransportTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. <?php
  2. /**
  3. * CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
  12. * @since 2.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Network\Email;
  16. use Cake\Network\Email\Email;
  17. use Cake\Network\Email\SmtpTransport;
  18. use Cake\Network\Socket;
  19. use Cake\TestSuite\TestCase;
  20. /**
  21. * Help to test SmtpTransport
  22. *
  23. */
  24. class SmtpTestTransport extends SmtpTransport {
  25. /**
  26. * Helper to change the socket
  27. *
  28. * @param Socket $socket
  29. * @return void
  30. */
  31. public function setSocket(Socket $socket) {
  32. $this->_socket = $socket;
  33. }
  34. /**
  35. * Helper to change the Email
  36. *
  37. * @param object $cakeEmail
  38. * @return void
  39. */
  40. public function setEmail($cakeEmail) {
  41. $this->_cakeEmail = $cakeEmail;
  42. }
  43. /**
  44. * Disabled the socket change
  45. *
  46. * @return void
  47. */
  48. protected function _generateSocket() {
  49. }
  50. /**
  51. * Magic function to call protected methods
  52. *
  53. * @param string $method
  54. * @param string $args
  55. * @return mixed
  56. */
  57. public function __call($method, $args) {
  58. $method = '_' . $method;
  59. return call_user_func_array(array($this, $method), $args);
  60. }
  61. }
  62. /**
  63. * Test case
  64. *
  65. */
  66. class SmtpTransportTest extends TestCase {
  67. /**
  68. * Setup
  69. *
  70. * @return void
  71. */
  72. public function setUp() {
  73. parent::setUp();
  74. $this->socket = $this->getMock(
  75. 'Cake\Network\Socket',
  76. array('read', 'write', 'connect', 'enableCrypto')
  77. );
  78. $this->SmtpTransport = new SmtpTestTransport();
  79. $this->SmtpTransport->setSocket($this->socket);
  80. $this->SmtpTransport->config(array('client' => 'localhost'));
  81. }
  82. /**
  83. * testConnectEhlo method
  84. *
  85. * @return void
  86. */
  87. public function testConnectEhlo() {
  88. $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true));
  89. $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false));
  90. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
  91. $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
  92. $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false));
  93. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250 Accepted\r\n"));
  94. $this->SmtpTransport->connect();
  95. }
  96. /**
  97. * testConnectEhloTls method
  98. *
  99. * @return void
  100. */
  101. public function testConnectEhloTls() {
  102. $this->SmtpTransport->config(array('tls' => true));
  103. $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true));
  104. $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false));
  105. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
  106. $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
  107. $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false));
  108. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250 Accepted\r\n"));
  109. $this->socket->expects($this->at(5))->method('write')->with("STARTTLS\r\n");
  110. $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false));
  111. $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("220 Server ready\r\n"));
  112. $this->socket->expects($this->at(8))->method('enableCrypto')->with('tls')->will($this->returnValue(true));
  113. $this->socket->expects($this->at(9))->method('write')->with("EHLO localhost\r\n");
  114. $this->socket->expects($this->at(10))->method('read')->will($this->returnValue(false));
  115. $this->socket->expects($this->at(11))->method('read')->will($this->returnValue("250 Accepted\r\n"));
  116. $this->SmtpTransport->connect();
  117. }
  118. /**
  119. * testConnectEhloTlsOnNonTlsServer method
  120. *
  121. * @expectedException \Cake\Network\Error\SocketException
  122. * @return void
  123. */
  124. public function testConnectEhloTlsOnNonTlsServer() {
  125. $this->SmtpTransport->config(array('tls' => true));
  126. $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true));
  127. $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false));
  128. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
  129. $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
  130. $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false));
  131. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250 Accepted\r\n"));
  132. $this->socket->expects($this->at(5))->method('write')->with("STARTTLS\r\n");
  133. $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false));
  134. $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("500 5.3.3 Unrecognized command\r\n"));
  135. $this->SmtpTransport->connect();
  136. }
  137. /**
  138. * testConnectEhloNoTlsOnRequiredTlsServer method
  139. *
  140. * @expectedException \Cake\Network\Error\SocketException
  141. * @return void
  142. */
  143. public function testConnectEhloNoTlsOnRequiredTlsServer() {
  144. $this->SmtpTransport->config(array('tls' => false, 'username' => 'user', 'password' => 'pass'));
  145. $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true));
  146. $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false));
  147. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
  148. $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
  149. $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false));
  150. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250 Accepted\r\n"));
  151. $this->socket->expects($this->at(5))->method('write')->with("AUTH LOGIN\r\n");
  152. $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false));
  153. $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized authentication type\r\n"));
  154. $this->SmtpTransport->connect();
  155. $this->SmtpTransport->auth();
  156. }
  157. /**
  158. * testConnectHelo method
  159. *
  160. * @return void
  161. */
  162. public function testConnectHelo() {
  163. $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true));
  164. $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false));
  165. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
  166. $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
  167. $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false));
  168. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("200 Not Accepted\r\n"));
  169. $this->socket->expects($this->at(5))->method('write')->with("HELO localhost\r\n");
  170. $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false));
  171. $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("250 Accepted\r\n"));
  172. $this->SmtpTransport->connect();
  173. }
  174. /**
  175. * testConnectFail method
  176. *
  177. * @expectedException \Cake\Network\Error\SocketException
  178. * @return void
  179. */
  180. public function testConnectFail() {
  181. $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true));
  182. $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false));
  183. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
  184. $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
  185. $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false));
  186. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("200 Not Accepted\r\n"));
  187. $this->socket->expects($this->at(5))->method('write')->with("HELO localhost\r\n");
  188. $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false));
  189. $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("200 Not Accepted\r\n"));
  190. $this->SmtpTransport->connect();
  191. }
  192. /**
  193. * testAuth method
  194. *
  195. * @return void
  196. */
  197. public function testAuth() {
  198. $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n");
  199. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false));
  200. $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("334 Login\r\n"));
  201. $this->socket->expects($this->at(3))->method('write')->with("bWFyaw==\r\n");
  202. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false));
  203. $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("334 Pass\r\n"));
  204. $this->socket->expects($this->at(6))->method('write')->with("c3Rvcnk=\r\n");
  205. $this->socket->expects($this->at(7))->method('read')->will($this->returnValue(false));
  206. $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("235 OK\r\n"));
  207. $this->SmtpTransport->config(array('username' => 'mark', 'password' => 'story'));
  208. $this->SmtpTransport->auth();
  209. }
  210. /**
  211. * testAuthNoAuth method
  212. *
  213. * @return void
  214. */
  215. public function testAuthNoAuth() {
  216. $this->socket->expects($this->any())->method('write')->with($this->logicalNot($this->stringContains('AUTH LOGIN')));
  217. $this->SmtpTransport->config(array('username' => null, 'password' => null));
  218. $this->SmtpTransport->auth();
  219. }
  220. /**
  221. * testRcpt method
  222. *
  223. * @return void
  224. */
  225. public function testRcpt() {
  226. $email = new Email();
  227. $email->from('noreply@cakephp.org', 'CakePHP Test');
  228. $email->to('cake@cakephp.org', 'CakePHP');
  229. $email->bcc('phpnut@cakephp.org');
  230. $email->cc(array('mark@cakephp.org' => 'Mark Story', 'juan@cakephp.org' => 'Juan Basso'));
  231. $this->socket->expects($this->at(0))->method('write')->with("MAIL FROM:<noreply@cakephp.org>\r\n");
  232. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false));
  233. $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("250 OK\r\n"));
  234. $this->socket->expects($this->at(3))->method('write')->with("RCPT TO:<cake@cakephp.org>\r\n");
  235. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false));
  236. $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n"));
  237. $this->socket->expects($this->at(6))->method('write')->with("RCPT TO:<mark@cakephp.org>\r\n");
  238. $this->socket->expects($this->at(7))->method('read')->will($this->returnValue(false));
  239. $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("250 OK\r\n"));
  240. $this->socket->expects($this->at(9))->method('write')->with("RCPT TO:<juan@cakephp.org>\r\n");
  241. $this->socket->expects($this->at(10))->method('read')->will($this->returnValue(false));
  242. $this->socket->expects($this->at(11))->method('read')->will($this->returnValue("250 OK\r\n"));
  243. $this->socket->expects($this->at(12))->method('write')->with("RCPT TO:<phpnut@cakephp.org>\r\n");
  244. $this->socket->expects($this->at(13))->method('read')->will($this->returnValue(false));
  245. $this->socket->expects($this->at(14))->method('read')->will($this->returnValue("250 OK\r\n"));
  246. $this->SmtpTransport->setEmail($email);
  247. $this->SmtpTransport->sendRcpt();
  248. }
  249. /**
  250. * testRcptWithReturnPath method
  251. *
  252. * @return void
  253. */
  254. public function testRcptWithReturnPath() {
  255. $email = new Email();
  256. $email->from('noreply@cakephp.org', 'CakePHP Test');
  257. $email->to('cake@cakephp.org', 'CakePHP');
  258. $email->returnPath('pleasereply@cakephp.org', 'CakePHP Return');
  259. $this->socket->expects($this->at(0))->method('write')->with("MAIL FROM:<pleasereply@cakephp.org>\r\n");
  260. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false));
  261. $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("250 OK\r\n"));
  262. $this->socket->expects($this->at(3))->method('write')->with("RCPT TO:<cake@cakephp.org>\r\n");
  263. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false));
  264. $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n"));
  265. $this->SmtpTransport->setEmail($email);
  266. $this->SmtpTransport->sendRcpt();
  267. }
  268. /**
  269. * testSendData method
  270. *
  271. * @return void
  272. */
  273. public function testSendData() {
  274. $email = $this->getMock('Cake\Network\Email\Email', array('message'));
  275. $email->from('noreply@cakephp.org', 'CakePHP Test');
  276. $email->returnPath('pleasereply@cakephp.org', 'CakePHP Return');
  277. $email->to('cake@cakephp.org', 'CakePHP');
  278. $email->cc(array('mark@cakephp.org' => 'Mark Story', 'juan@cakephp.org' => 'Juan Basso'));
  279. $email->bcc('phpnut@cakephp.org');
  280. $email->messageID('<4d9946cf-0a44-4907-88fe-1d0ccbdd56cb@localhost>');
  281. $email->subject('Testing SMTP');
  282. $date = date(DATE_RFC2822);
  283. $email->setHeaders(array('X-Mailer' => Email::EMAIL_CLIENT, 'Date' => $date));
  284. $email->expects($this->once())
  285. ->method('message')
  286. ->will($this->returnValue(array('First Line', 'Second Line', '.Third Line', '')));
  287. $data = "From: CakePHP Test <noreply@cakephp.org>\r\n";
  288. $data .= "To: CakePHP <cake@cakephp.org>\r\n";
  289. $data .= "Cc: Mark Story <mark@cakephp.org>, Juan Basso <juan@cakephp.org>\r\n";
  290. $data .= "X-Mailer: CakePHP Email\r\n";
  291. $data .= "Date: " . $date . "\r\n";
  292. $data .= "Message-ID: <4d9946cf-0a44-4907-88fe-1d0ccbdd56cb@localhost>\r\n";
  293. $data .= "Subject: Testing SMTP\r\n";
  294. $data .= "MIME-Version: 1.0\r\n";
  295. $data .= "Content-Type: text/plain; charset=UTF-8\r\n";
  296. $data .= "Content-Transfer-Encoding: 8bit\r\n";
  297. $data .= "\r\n";
  298. $data .= "First Line\r\n";
  299. $data .= "Second Line\r\n";
  300. $data .= "..Third Line\r\n"; // RFC5321 4.5.2.Transparency
  301. $data .= "\r\n";
  302. $data .= "\r\n\r\n.\r\n";
  303. $this->socket->expects($this->at(0))->method('write')->with("DATA\r\n");
  304. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false));
  305. $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("354 OK\r\n"));
  306. $this->socket->expects($this->at(3))->method('write')->with($data);
  307. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false));
  308. $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n"));
  309. $this->SmtpTransport->setEmail($email);
  310. $this->SmtpTransport->sendData();
  311. }
  312. /**
  313. * testQuit method
  314. *
  315. * @return void
  316. */
  317. public function testQuit() {
  318. $this->socket->expects($this->at(0))->method('write')->with("QUIT\r\n");
  319. $this->SmtpTransport->disconnect();
  320. }
  321. /**
  322. * testEmptyConfigArray method
  323. *
  324. * @return void
  325. */
  326. public function testEmptyConfigArray() {
  327. $this->SmtpTransport->config(array(
  328. 'client' => 'myhost.com',
  329. 'port' => 666
  330. ));
  331. $expected = $this->SmtpTransport->config();
  332. $this->assertEquals(666, $expected['port']);
  333. $this->SmtpTransport->config(array());
  334. $result = $this->SmtpTransport->config();
  335. $this->assertEquals($expected, $result);
  336. }
  337. /**
  338. * testGetLastResponse method
  339. *
  340. * @return void
  341. */
  342. public function testGetLastResponse() {
  343. $this->assertEmpty($this->SmtpTransport->getLastResponse());
  344. $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true));
  345. $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false));
  346. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n"));
  347. $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n");
  348. $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false));
  349. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250-PIPELINING\r\n"));
  350. $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250-SIZE 102400000\r\n"));
  351. $this->socket->expects($this->at(6))->method('read')->will($this->returnValue("250-VRFY\r\n"));
  352. $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("250-ETRN\r\n"));
  353. $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("250-STARTTLS\r\n"));
  354. $this->socket->expects($this->at(9))->method('read')->will($this->returnValue("250-AUTH PLAIN LOGIN\r\n"));
  355. $this->socket->expects($this->at(10))->method('read')->will($this->returnValue("250-AUTH=PLAIN LOGIN\r\n"));
  356. $this->socket->expects($this->at(11))->method('read')->will($this->returnValue("250-ENHANCEDSTATUSCODES\r\n"));
  357. $this->socket->expects($this->at(12))->method('read')->will($this->returnValue("250-8BITMIME\r\n"));
  358. $this->socket->expects($this->at(13))->method('read')->will($this->returnValue("250 DSN\r\n"));
  359. $this->SmtpTransport->connect();
  360. $expected = array(
  361. array('code' => '250', 'message' => 'PIPELINING'),
  362. array('code' => '250', 'message' => 'SIZE 102400000'),
  363. array('code' => '250', 'message' => 'VRFY'),
  364. array('code' => '250', 'message' => 'ETRN'),
  365. array('code' => '250', 'message' => 'STARTTLS'),
  366. array('code' => '250', 'message' => 'AUTH PLAIN LOGIN'),
  367. array('code' => '250', 'message' => 'AUTH=PLAIN LOGIN'),
  368. array('code' => '250', 'message' => 'ENHANCEDSTATUSCODES'),
  369. array('code' => '250', 'message' => '8BITMIME'),
  370. array('code' => '250', 'message' => 'DSN')
  371. );
  372. $result = $this->SmtpTransport->getLastResponse();
  373. $this->assertEquals($expected, $result);
  374. $email = new Email();
  375. $email->from('noreply@cakephp.org', 'CakePHP Test');
  376. $email->to('cake@cakephp.org', 'CakePHP');
  377. $this->socket->expects($this->at(0))->method('write')->with("MAIL FROM:<noreply@cakephp.org>\r\n");
  378. $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false));
  379. $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("250 OK\r\n"));
  380. $this->socket->expects($this->at(3))->method('write')->with("RCPT TO:<cake@cakephp.org>\r\n");
  381. $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false));
  382. $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n"));
  383. $this->SmtpTransport->setEmail($email);
  384. $this->SmtpTransport->sendRcpt();
  385. $expected = array(
  386. array('code' => '250', 'message' => 'OK'),
  387. );
  388. $result = $this->SmtpTransport->getLastResponse();
  389. $this->assertEquals($expected, $result);
  390. }
  391. /**
  392. * testBufferResponseLines method
  393. *
  394. * @return void
  395. */
  396. public function testBufferResponseLines() {
  397. $reponseLines = array(
  398. '123',
  399. "456\tFOO",
  400. 'FOOBAR',
  401. '250-PIPELINING',
  402. '250-ENHANCEDSTATUSCODES',
  403. '250-8BITMIME',
  404. '250 DSN',
  405. );
  406. $this->SmtpTransport->bufferResponseLines($reponseLines);
  407. $expected = array(
  408. array('code' => '123', 'message' => null),
  409. array('code' => '250', 'message' => 'PIPELINING'),
  410. array('code' => '250', 'message' => 'ENHANCEDSTATUSCODES'),
  411. array('code' => '250', 'message' => '8BITMIME'),
  412. array('code' => '250', 'message' => 'DSN')
  413. );
  414. $result = $this->SmtpTransport->getLastResponse();
  415. $this->assertEquals($expected, $result);
  416. }
  417. }