/lib/Cake/Network/Email/SmtpTransport.php

https://github.com/dbarbar/spotbox · PHP · 229 lines · 114 code · 21 blank · 94 comment · 17 complexity · 4db03ccd2a00fd75cd2b63c1a2b0c5b4 MD5 · raw file

  1. <?php
  2. /**
  3. * Send mail using SMTP protocol
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package Cake.Network.Email
  16. * @since CakePHP(tm) v 2.0.0
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. App::uses('CakeSocket', 'Network');
  20. /**
  21. * SendEmail class
  22. *
  23. * @package Cake.Network.Email
  24. */
  25. class SmtpTransport extends AbstractTransport {
  26. /**
  27. * Socket to SMTP server
  28. *
  29. * @var CakeSocket
  30. */
  31. protected $_socket;
  32. /**
  33. * CakeEmail
  34. *
  35. * @var CakeEmail
  36. */
  37. protected $_cakeEmail;
  38. /**
  39. * Content of email to return
  40. *
  41. * @var string
  42. */
  43. protected $_content;
  44. /**
  45. * Send mail
  46. *
  47. * @param CakeEmail $email CakeEmail
  48. * @return array
  49. * @throws SocketException
  50. */
  51. public function send(CakeEmail $email) {
  52. $this->_cakeEmail = $email;
  53. $this->_connect();
  54. $this->_auth();
  55. $this->_sendRcpt();
  56. $this->_sendData();
  57. $this->_disconnect();
  58. return $this->_content;
  59. }
  60. /**
  61. * Set the configuration
  62. *
  63. * @param array $config
  64. * @return void
  65. */
  66. public function config($config = array()) {
  67. $default = array(
  68. 'host' => 'localhost',
  69. 'port' => 25,
  70. 'timeout' => 30,
  71. 'username' => null,
  72. 'password' => null,
  73. 'client' => null
  74. );
  75. $this->_config = $config + $default;
  76. }
  77. /**
  78. * Connect to SMTP Server
  79. *
  80. * @return void
  81. * @throws SocketException
  82. */
  83. protected function _connect() {
  84. $this->_generateSocket();
  85. if (!$this->_socket->connect()) {
  86. throw new SocketException(__d('cake_dev', 'Unable to connect to SMTP server.'));
  87. }
  88. $this->_smtpSend(null, '220');
  89. if (isset($this->_config['client'])) {
  90. $host = $this->_config['client'];
  91. } elseif ($httpHost = env('HTTP_HOST')) {
  92. list($host) = explode(':', $httpHost);
  93. } else {
  94. $host = 'localhost';
  95. }
  96. try {
  97. $this->_smtpSend("EHLO {$host}", '250');
  98. } catch (SocketException $e) {
  99. try {
  100. $this->_smtpSend("HELO {$host}", '250');
  101. } catch (SocketException $e2) {
  102. throw new SocketException(__d('cake_dev', 'SMTP server did not accept the connection.'));
  103. }
  104. }
  105. }
  106. /**
  107. * Send authentication
  108. *
  109. * @return void
  110. * @throws SocketException
  111. */
  112. protected function _auth() {
  113. if (isset($this->_config['username']) && isset($this->_config['password'])) {
  114. $authRequired = $this->_smtpSend('AUTH LOGIN', '334|503');
  115. if ($authRequired == '334') {
  116. if (!$this->_smtpSend(base64_encode($this->_config['username']), '334')) {
  117. throw new SocketException(__d('cake_dev', 'SMTP server did not accept the username.'));
  118. }
  119. if (!$this->_smtpSend(base64_encode($this->_config['password']), '235')) {
  120. throw new SocketException(__d('cake_dev', 'SMTP server did not accept the password.'));
  121. }
  122. } elseif ($authRequired != '503') {
  123. throw new SocketException(__d('cake_dev', 'SMTP does not require authentication.'));
  124. }
  125. }
  126. }
  127. /**
  128. * Send emails
  129. *
  130. * @return void
  131. * @throws SocketException
  132. */
  133. protected function _sendRcpt() {
  134. $from = $this->_cakeEmail->from();
  135. $this->_smtpSend('MAIL FROM:<' . key($from) . '>');
  136. $to = $this->_cakeEmail->to();
  137. $cc = $this->_cakeEmail->cc();
  138. $bcc = $this->_cakeEmail->bcc();
  139. $emails = array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
  140. foreach ($emails as $email) {
  141. $this->_smtpSend('RCPT TO:<' . $email . '>');
  142. }
  143. }
  144. /**
  145. * Send Data
  146. *
  147. * @return void
  148. * @throws SocketException
  149. */
  150. protected function _sendData() {
  151. $this->_smtpSend('DATA', '354');
  152. $headers = $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'subject'));
  153. $headers = $this->_headersToString($headers);
  154. $message = implode("\r\n", $this->_cakeEmail->message());
  155. $this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n.");
  156. $this->_content = array('headers' => $headers, 'message' => $message);
  157. }
  158. /**
  159. * Disconnect
  160. *
  161. * @return void
  162. * @throws SocketException
  163. */
  164. protected function _disconnect() {
  165. $this->_smtpSend('QUIT', false);
  166. $this->_socket->disconnect();
  167. }
  168. /**
  169. * Helper method to generate socket
  170. *
  171. * @return void
  172. * @throws SocketException
  173. */
  174. protected function _generateSocket() {
  175. $this->_socket = new CakeSocket($this->_config);
  176. }
  177. /**
  178. * Protected method for sending data to SMTP connection
  179. *
  180. * @param string $data data to be sent to SMTP server
  181. * @param mixed $checkCode code to check for in server response, false to skip
  182. * @return void
  183. * @throws SocketException
  184. */
  185. protected function _smtpSend($data, $checkCode = '250') {
  186. if (!is_null($data)) {
  187. $this->_socket->write($data . "\r\n");
  188. }
  189. while ($checkCode !== false) {
  190. $response = '';
  191. $startTime = time();
  192. while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->_config['timeout'])) {
  193. $response .= $this->_socket->read();
  194. }
  195. if (substr($response, -2) !== "\r\n") {
  196. throw new SocketException(__d('cake_dev', 'SMTP timeout.'));
  197. }
  198. $response = end(explode("\r\n", rtrim($response, "\r\n")));
  199. if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
  200. if ($code[2] === '-') {
  201. continue;
  202. }
  203. return $code[1];
  204. }
  205. throw new SocketException(__d('cake_dev', 'SMTP Error: %s', $response));
  206. }
  207. }
  208. }