PageRenderTime 49ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://gitlab.com/grlopez90/servipro
PHP | 375 lines | 182 code | 31 blank | 162 comment | 24 complexity | edcf5946d1e2e51b35fdc40a6bf91974 MD5 | raw file
  1. <?php
  2. /**
  3. * Send mail using SMTP protocol
  4. *
  5. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  6. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  7. *
  8. * Licensed under The MIT License
  9. * For full copyright and license information, please see the LICENSE.txt
  10. * Redistributions of files must retain the above copyright notice.
  11. *
  12. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  13. * @link http://cakephp.org CakePHP(tm) Project
  14. * @package Cake.Network.Email
  15. * @since CakePHP(tm) v 2.0.0
  16. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  17. */
  18. App::uses('AbstractTransport', 'Network/Email');
  19. App::uses('CakeSocket', 'Network');
  20. /**
  21. * Send mail using SMTP protocol
  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. * Content of email to return
  34. *
  35. * @var string
  36. */
  37. protected $_content;
  38. /**
  39. * The response of the last sent SMTP command.
  40. *
  41. * @var array
  42. */
  43. protected $_lastResponse = array();
  44. /**
  45. * Returns the response of the last sent SMTP command.
  46. *
  47. * A response consists of one or more lines containing a response
  48. * code and an optional response message text:
  49. * ```
  50. * array(
  51. * array(
  52. * 'code' => '250',
  53. * 'message' => 'mail.example.com'
  54. * ),
  55. * array(
  56. * 'code' => '250',
  57. * 'message' => 'PIPELINING'
  58. * ),
  59. * array(
  60. * 'code' => '250',
  61. * 'message' => '8BITMIME'
  62. * ),
  63. * // etc...
  64. * )
  65. * ```
  66. *
  67. * @return array
  68. */
  69. public function getLastResponse() {
  70. return $this->_lastResponse;
  71. }
  72. /**
  73. * Send mail
  74. *
  75. * @param CakeEmail $email CakeEmail
  76. * @return array
  77. * @throws SocketException
  78. */
  79. public function send(CakeEmail $email) {
  80. $this->_connect();
  81. $this->_auth();
  82. $this->_sendRcpt($email);
  83. $this->_sendData($email);
  84. $this->_disconnect();
  85. return $this->_content;
  86. }
  87. /**
  88. * Set the configuration
  89. *
  90. * @param array $config Configuration options.
  91. * @return array Returns configs
  92. */
  93. public function config($config = null) {
  94. if ($config === null) {
  95. return $this->_config;
  96. }
  97. $default = array(
  98. 'host' => 'localhost',
  99. 'port' => 25,
  100. 'timeout' => 30,
  101. 'username' => null,
  102. 'password' => null,
  103. 'client' => null,
  104. 'tls' => false,
  105. 'ssl_allow_self_signed' => false
  106. );
  107. $this->_config = array_merge($default, $this->_config, $config);
  108. return $this->_config;
  109. }
  110. /**
  111. * Parses and stores the reponse lines in `'code' => 'message'` format.
  112. *
  113. * @param array $responseLines Response lines to parse.
  114. * @return void
  115. */
  116. protected function _bufferResponseLines(array $responseLines) {
  117. $response = array();
  118. foreach ($responseLines as $responseLine) {
  119. if (preg_match('/^(\d{3})(?:[ -]+(.*))?$/', $responseLine, $match)) {
  120. $response[] = array(
  121. 'code' => $match[1],
  122. 'message' => isset($match[2]) ? $match[2] : null
  123. );
  124. }
  125. }
  126. $this->_lastResponse = array_merge($this->_lastResponse, $response);
  127. }
  128. /**
  129. * Connect to SMTP Server
  130. *
  131. * @return void
  132. * @throws SocketException
  133. */
  134. protected function _connect() {
  135. $this->_generateSocket();
  136. if (!$this->_socket->connect()) {
  137. throw new SocketException(__d('cake_dev', 'Unable to connect to SMTP server.'));
  138. }
  139. $this->_smtpSend(null, '220');
  140. if (isset($this->_config['client'])) {
  141. $host = $this->_config['client'];
  142. } elseif ($httpHost = env('HTTP_HOST')) {
  143. list($host) = explode(':', $httpHost);
  144. } else {
  145. $host = 'localhost';
  146. }
  147. try {
  148. $this->_smtpSend("EHLO {$host}", '250');
  149. if ($this->_config['tls']) {
  150. $this->_smtpSend("STARTTLS", '220');
  151. $this->_socket->enableCrypto('tls');
  152. $this->_smtpSend("EHLO {$host}", '250');
  153. }
  154. } catch (SocketException $e) {
  155. if ($this->_config['tls']) {
  156. throw new SocketException(__d('cake_dev', 'SMTP server did not accept the connection or trying to connect to non TLS SMTP server using TLS.'));
  157. }
  158. try {
  159. $this->_smtpSend("HELO {$host}", '250');
  160. } catch (SocketException $e2) {
  161. throw new SocketException(__d('cake_dev', 'SMTP server did not accept the connection.'));
  162. }
  163. }
  164. }
  165. /**
  166. * Send authentication
  167. *
  168. * @return void
  169. * @throws SocketException
  170. */
  171. protected function _auth() {
  172. if (isset($this->_config['username']) && isset($this->_config['password'])) {
  173. $replyCode = $this->_smtpSend('AUTH LOGIN', '334|500|502|504');
  174. if ($replyCode == '334') {
  175. try {
  176. $this->_smtpSend(base64_encode($this->_config['username']), '334');
  177. } catch (SocketException $e) {
  178. throw new SocketException(__d('cake_dev', 'SMTP server did not accept the username.'));
  179. }
  180. try {
  181. $this->_smtpSend(base64_encode($this->_config['password']), '235');
  182. } catch (SocketException $e) {
  183. throw new SocketException(__d('cake_dev', 'SMTP server did not accept the password.'));
  184. }
  185. } elseif ($replyCode == '504') {
  186. throw new SocketException(__d('cake_dev', 'SMTP authentication method not allowed, check if SMTP server requires TLS.'));
  187. } else {
  188. throw new SocketException(__d('cake_dev', 'AUTH command not recognized or not implemented, SMTP server may not require authentication.'));
  189. }
  190. }
  191. }
  192. /**
  193. * Prepares the `MAIL FROM` SMTP command.
  194. *
  195. * @param string $email The email address to send with the command.
  196. * @return string
  197. */
  198. protected function _prepareFromCmd($email) {
  199. return 'MAIL FROM:<' . $email . '>';
  200. }
  201. /**
  202. * Prepares the `RCPT TO` SMTP command.
  203. *
  204. * @param string $email The email address to send with the command.
  205. * @return string
  206. */
  207. protected function _prepareRcptCmd($email) {
  208. return 'RCPT TO:<' . $email . '>';
  209. }
  210. /**
  211. * Prepares the `from` email address.
  212. *
  213. * @param CakeEmail $email CakeEmail
  214. * @return array
  215. */
  216. protected function _prepareFromAddress(CakeEmail $email) {
  217. $from = $email->returnPath();
  218. if (empty($from)) {
  219. $from = $email->from();
  220. }
  221. return $from;
  222. }
  223. /**
  224. * Prepares the recipient email addresses.
  225. *
  226. * @param CakeEmail $email CakeEmail
  227. * @return array
  228. */
  229. protected function _prepareRecipientAddresses(CakeEmail $email) {
  230. $to = $email->to();
  231. $cc = $email->cc();
  232. $bcc = $email->bcc();
  233. return array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
  234. }
  235. /**
  236. * Prepares the message headers.
  237. *
  238. * @param CakeEmail $email CakeEmail
  239. * @return array
  240. */
  241. protected function _prepareMessageHeaders(CakeEmail $email) {
  242. return $email->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject'));
  243. }
  244. /**
  245. * Prepares the message body.
  246. *
  247. * @param CakeEmail $email CakeEmail
  248. * @return string
  249. */
  250. protected function _prepareMessage(CakeEmail $email) {
  251. $lines = $email->message();
  252. $messages = array();
  253. foreach ($lines as $line) {
  254. if ((!empty($line)) && ($line[0] === '.')) {
  255. $messages[] = '.' . $line;
  256. } else {
  257. $messages[] = $line;
  258. }
  259. }
  260. return implode("\r\n", $messages);
  261. }
  262. /**
  263. * Send emails
  264. *
  265. * @param CakeEmail $email CakeEmail
  266. * @return void
  267. * @throws SocketException
  268. */
  269. protected function _sendRcpt(CakeEmail $email) {
  270. $from = $this->_prepareFromAddress($email);
  271. $this->_smtpSend($this->_prepareFromCmd(key($from)));
  272. $emails = $this->_prepareRecipientAddresses($email);
  273. foreach ($emails as $email) {
  274. $this->_smtpSend($this->_prepareRcptCmd($email));
  275. }
  276. }
  277. /**
  278. * Send Data
  279. *
  280. * @param CakeEmail $email CakeEmail
  281. * @return void
  282. * @throws SocketException
  283. */
  284. protected function _sendData(CakeEmail $email) {
  285. $this->_smtpSend('DATA', '354');
  286. $headers = $this->_headersToString($this->_prepareMessageHeaders($email));
  287. $message = $this->_prepareMessage($email);
  288. $this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n.");
  289. $this->_content = array('headers' => $headers, 'message' => $message);
  290. }
  291. /**
  292. * Disconnect
  293. *
  294. * @return void
  295. * @throws SocketException
  296. */
  297. protected function _disconnect() {
  298. $this->_smtpSend('QUIT', false);
  299. $this->_socket->disconnect();
  300. }
  301. /**
  302. * Helper method to generate socket
  303. *
  304. * @return void
  305. * @throws SocketException
  306. */
  307. protected function _generateSocket() {
  308. $this->_socket = new CakeSocket($this->_config);
  309. }
  310. /**
  311. * Protected method for sending data to SMTP connection
  312. *
  313. * @param string|null $data Data to be sent to SMTP server
  314. * @param string|bool $checkCode Code to check for in server response, false to skip
  315. * @return string|null The matched code, or null if nothing matched
  316. * @throws SocketException
  317. */
  318. protected function _smtpSend($data, $checkCode = '250') {
  319. $this->_lastResponse = array();
  320. if ($data !== null) {
  321. $this->_socket->write($data . "\r\n");
  322. }
  323. while ($checkCode !== false) {
  324. $response = '';
  325. $startTime = time();
  326. while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->_config['timeout'])) {
  327. $response .= $this->_socket->read();
  328. }
  329. if (substr($response, -2) !== "\r\n") {
  330. throw new SocketException(__d('cake_dev', 'SMTP timeout.'));
  331. }
  332. $responseLines = explode("\r\n", rtrim($response, "\r\n"));
  333. $response = end($responseLines);
  334. $this->_bufferResponseLines($responseLines);
  335. if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
  336. if ($code[2] === '-') {
  337. continue;
  338. }
  339. return $code[1];
  340. }
  341. throw new SocketException(__d('cake_dev', 'SMTP Error: %s', $response));
  342. }
  343. }
  344. }