PageRenderTime 564ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/email/vendor/swift/classes/Swift/Transport/AbstractSmtpTransport.php

https://bitbucket.org/levjke/kohanablogds
PHP | 553 lines | 372 code | 58 blank | 123 comment | 34 complexity | c75516dfd4d7b4ba7a5601eb2a0fe92c MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /*
  3. The ESMTP Transport from Swift Mailer.
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. //@require 'Swift/Transport.php';
  16. //@require 'Swift/Transport/IoBuffer.php';
  17. //@require 'Swift/Transport/CommandSentException.php';
  18. //@require 'Swift/TransportException.php';
  19. //@require 'Swift/Mime/Message.php';
  20. //@require 'Swift/Events/EventDispatcher.php';
  21. //@require 'Swift/Events/EventListener.php';
  22. /**
  23. * Sends Messages over SMTP.
  24. *
  25. * @package Swift
  26. * @subpackage Transport
  27. * @author Chris Corbyn
  28. */
  29. abstract class Swift_Transport_AbstractSmtpTransport
  30. implements Swift_Transport
  31. {
  32. /** Input-Output buffer for sending/receiving SMTP commands and responses */
  33. protected $_buffer;
  34. /** Connection status */
  35. protected $_started = false;
  36. /** The domain name to use in HELO command */
  37. protected $_domain = '[127.0.0.1]';
  38. /** The event dispatching layer */
  39. protected $_eventDispatcher;
  40. /** Return an array of params for the Buffer */
  41. abstract protected function _getBufferParams();
  42. /**
  43. * Creates a new EsmtpTransport using the given I/O buffer.
  44. *
  45. * @param Swift_Transport_IoBuffer $buf
  46. * @param Swift_Events_EventDispatcher $dispatcher
  47. */
  48. public function __construct(Swift_Transport_IoBuffer $buf,
  49. Swift_Events_EventDispatcher $dispatcher)
  50. {
  51. $this->_eventDispatcher = $dispatcher;
  52. $this->_buffer = $buf;
  53. $this->_lookupHostname();
  54. }
  55. /**
  56. * Set the name of the local domain which Swift will identify itself as.
  57. * This should be a fully-qualified domain name and should be truly the domain
  58. * you're using. If your server doesn't have a domain name, use the IP in square
  59. * brackets (i.e. [127.0.0.1]).
  60. *
  61. * @param string $domain
  62. */
  63. public function setLocalDomain($domain)
  64. {
  65. $this->_domain = $domain;
  66. return $this;
  67. }
  68. /**
  69. * Get the name of the domain Swift will identify as.
  70. *
  71. * @return string
  72. */
  73. public function getLocalDomain()
  74. {
  75. return $this->_domain;
  76. }
  77. /**
  78. * Start the SMTP connection.
  79. */
  80. public function start()
  81. {
  82. if (!$this->_started)
  83. {
  84. if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this))
  85. {
  86. $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted');
  87. if ($evt->bubbleCancelled())
  88. {
  89. return;
  90. }
  91. }
  92. try
  93. {
  94. $this->_buffer->initialize($this->_getBufferParams());
  95. }
  96. catch (Swift_TransportException $e)
  97. {
  98. $this->_throwException($e);
  99. }
  100. $this->_readGreeting();
  101. $this->_doHeloCommand();
  102. if ($evt)
  103. {
  104. $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted');
  105. }
  106. $this->_started = true;
  107. }
  108. }
  109. /**
  110. * Test if an SMTP connection has been established.
  111. *
  112. * @return boolean
  113. */
  114. public function isStarted()
  115. {
  116. return $this->_started;
  117. }
  118. /**
  119. * Send the given Message.
  120. *
  121. * Recipient/sender data will be retreived from the Message API.
  122. * The return value is the number of recipients who were accepted for delivery.
  123. *
  124. * @param Swift_Mime_Message $message
  125. * @param string[] &$failedRecipients to collect failures by-reference
  126. * @return int
  127. */
  128. public function send(Swift_Mime_Message $message, &$failedRecipients = null)
  129. {
  130. $sent = 0;
  131. $failedRecipients = (array) $failedRecipients;
  132. if (!$reversePath = $this->_getReversePath($message))
  133. {
  134. throw new Swift_TransportException(
  135. 'Cannot send message without a sender address'
  136. );
  137. }
  138. if ($evt = $this->_eventDispatcher->createSendEvent($this, $message))
  139. {
  140. $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
  141. if ($evt->bubbleCancelled())
  142. {
  143. return 0;
  144. }
  145. }
  146. $to = (array) $message->getTo();
  147. $cc = (array) $message->getCc();
  148. $bcc = (array) $message->getBcc();
  149. $message->setBcc(array());
  150. try
  151. {
  152. $sent += $this->_sendTo($message, $reversePath, $to, $failedRecipients);
  153. $sent += $this->_sendCc($message, $reversePath, $cc, $failedRecipients);
  154. $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients);
  155. }
  156. catch (Exception $e)
  157. {
  158. $message->setBcc($bcc);
  159. throw $e;
  160. }
  161. $message->setBcc($bcc);
  162. if ($evt)
  163. {
  164. if ($sent == count($to) + count($cc) + count($bcc))
  165. {
  166. $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
  167. }
  168. elseif ($sent > 0)
  169. {
  170. $evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE);
  171. }
  172. else
  173. {
  174. $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
  175. }
  176. $evt->setFailedRecipients($failedRecipients);
  177. $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
  178. }
  179. $message->generateId(); //Make sure a new Message ID is used
  180. return $sent;
  181. }
  182. /**
  183. * Stop the SMTP connection.
  184. */
  185. public function stop()
  186. {
  187. if ($this->_started)
  188. {
  189. if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this))
  190. {
  191. $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped');
  192. if ($evt->bubbleCancelled())
  193. {
  194. return;
  195. }
  196. }
  197. try
  198. {
  199. $this->executeCommand("QUIT\r\n", array(221));
  200. }
  201. catch (Swift_TransportException $e) {}
  202. try
  203. {
  204. $this->_buffer->terminate();
  205. if ($evt)
  206. {
  207. $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped');
  208. }
  209. }
  210. catch (Swift_TransportException $e)
  211. {
  212. $this->_throwException($e);
  213. }
  214. }
  215. $this->_started = false;
  216. }
  217. /**
  218. * Register a plugin.
  219. *
  220. * @param Swift_Events_EventListener $plugin
  221. */
  222. public function registerPlugin(Swift_Events_EventListener $plugin)
  223. {
  224. $this->_eventDispatcher->bindEventListener($plugin);
  225. }
  226. /**
  227. * Reset the current mail transaction.
  228. */
  229. public function reset()
  230. {
  231. $this->executeCommand("RSET\r\n", array(250));
  232. }
  233. /**
  234. * Get the IoBuffer where read/writes are occurring.
  235. *
  236. * @return Swift_Transport_IoBuffer
  237. */
  238. public function getBuffer()
  239. {
  240. return $this->_buffer;
  241. }
  242. /**
  243. * Run a command against the buffer, expecting the given response codes.
  244. *
  245. * If no response codes are given, the response will not be validated.
  246. * If codes are given, an exception will be thrown on an invalid response.
  247. *
  248. * @param string $command
  249. * @param int[] $codes
  250. * @param string[] &$failures
  251. * @return string
  252. */
  253. public function executeCommand($command, $codes = array(), &$failures = null)
  254. {
  255. $failures = (array) $failures;
  256. $seq = $this->_buffer->write($command);
  257. $response = $this->_getFullResponse($seq);
  258. if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes))
  259. {
  260. $this->_eventDispatcher->dispatchEvent($evt, 'commandSent');
  261. }
  262. $this->_assertResponseCode($response, $codes);
  263. return $response;
  264. }
  265. // -- Protected methods
  266. /** Read the opening SMTP greeting */
  267. protected function _readGreeting()
  268. {
  269. $this->_assertResponseCode($this->_getFullResponse(0), array(220));
  270. }
  271. /** Send the HELO welcome */
  272. protected function _doHeloCommand()
  273. {
  274. $this->executeCommand(
  275. sprintf("HELO %s\r\n", $this->_domain), array(250)
  276. );
  277. }
  278. /** Send the MAIL FROM command */
  279. protected function _doMailFromCommand($address)
  280. {
  281. $this->executeCommand(
  282. sprintf("MAIL FROM: <%s>\r\n", $address), array(250)
  283. );
  284. }
  285. /** Send the RCPT TO command */
  286. protected function _doRcptToCommand($address)
  287. {
  288. $this->executeCommand(
  289. sprintf("RCPT TO: <%s>\r\n", $address), array(250, 251, 252)
  290. );
  291. }
  292. /** Send the DATA command */
  293. protected function _doDataCommand()
  294. {
  295. $this->executeCommand("DATA\r\n", array(354));
  296. }
  297. /** Stream the contents of the message over the buffer */
  298. protected function _streamMessage(Swift_Mime_Message $message)
  299. {
  300. $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n.."));
  301. try
  302. {
  303. $message->toByteStream($this->_buffer);
  304. $this->_buffer->flushBuffers();
  305. }
  306. catch (Swift_TransportException $e)
  307. {
  308. $this->_throwException($e);
  309. }
  310. $this->_buffer->setWriteTranslations(array());
  311. $this->executeCommand("\r\n.\r\n", array(250));
  312. }
  313. /** Determine the best-use reverse path for this message */
  314. protected function _getReversePath(Swift_Mime_Message $message)
  315. {
  316. $return = $message->getReturnPath();
  317. $sender = $message->getSender();
  318. $from = $message->getFrom();
  319. $path = null;
  320. if (!empty($return))
  321. {
  322. $path = $return;
  323. }
  324. elseif (!empty($sender))
  325. {
  326. // Don't use array_keys
  327. reset($sender); // Reset Pointer to first pos
  328. $path = key($sender); // Get key
  329. }
  330. elseif (!empty($from))
  331. {
  332. reset($from); // Reset Pointer to first pos
  333. $path = key($from); // Get key
  334. }
  335. return $path;
  336. }
  337. /** Throw a TransportException, first sending it to any listeners */
  338. protected function _throwException(Swift_TransportException $e)
  339. {
  340. if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e))
  341. {
  342. $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
  343. if (!$evt->bubbleCancelled())
  344. {
  345. throw $e;
  346. }
  347. }
  348. else
  349. {
  350. throw $e;
  351. }
  352. }
  353. /** Throws an Exception if a response code is incorrect */
  354. protected function _assertResponseCode($response, $wanted)
  355. {
  356. list($code, $separator, $text) = sscanf($response, '%3d%[ -]%s');
  357. $valid = (empty($wanted) || in_array($code, $wanted));
  358. if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response,
  359. $valid))
  360. {
  361. $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived');
  362. }
  363. if (!$valid)
  364. {
  365. $this->_throwException(
  366. new Swift_TransportException(
  367. 'Expected response code ' . implode('/', $wanted) . ' but got code ' .
  368. '"' . $code . '", with message "' . $response . '"'
  369. )
  370. );
  371. }
  372. }
  373. /** Get an entire multi-line response using its sequence number */
  374. protected function _getFullResponse($seq)
  375. {
  376. $response = '';
  377. try
  378. {
  379. do
  380. {
  381. $line = $this->_buffer->readLine($seq);
  382. $response .= $line;
  383. }
  384. while (null !== $line && false !== $line && ' ' != $line{3});
  385. }
  386. catch (Swift_TransportException $e)
  387. {
  388. $this->_throwException($e);
  389. }
  390. return $response;
  391. }
  392. // -- Private methods
  393. /** Send an email to the given recipients from the given reverse path */
  394. private function _doMailTransaction($message, $reversePath,
  395. array $recipients, array &$failedRecipients)
  396. {
  397. $sent = 0;
  398. $this->_doMailFromCommand($reversePath);
  399. foreach ($recipients as $forwardPath)
  400. {
  401. try
  402. {
  403. $this->_doRcptToCommand($forwardPath);
  404. $sent++;
  405. }
  406. catch (Swift_TransportException $e)
  407. {
  408. $failedRecipients[] = $forwardPath;
  409. }
  410. }
  411. if ($sent != 0)
  412. {
  413. $this->_doDataCommand();
  414. $this->_streamMessage($message);
  415. }
  416. else
  417. {
  418. $this->reset();
  419. }
  420. return $sent;
  421. }
  422. /** Send a message to the given To: recipients */
  423. private function _sendTo(Swift_Mime_Message $message, $reversePath,
  424. array $to, array &$failedRecipients)
  425. {
  426. if (empty($to))
  427. {
  428. return 0;
  429. }
  430. return $this->_doMailTransaction($message, $reversePath, array_keys($to),
  431. $failedRecipients);
  432. }
  433. /** Send a message to the given Cc: recipients */
  434. private function _sendCc(Swift_Mime_Message $message, $reversePath,
  435. array $cc, array &$failedRecipients)
  436. {
  437. if (empty($cc))
  438. {
  439. return 0;
  440. }
  441. return $this->_doMailTransaction($message, $reversePath, array_keys($cc),
  442. $failedRecipients);
  443. }
  444. /** Send a message to all Bcc: recipients */
  445. private function _sendBcc(Swift_Mime_Message $message, $reversePath,
  446. array $bcc, array &$failedRecipients)
  447. {
  448. $sent = 0;
  449. foreach ($bcc as $forwardPath => $name)
  450. {
  451. $message->setBcc(array($forwardPath => $name));
  452. $sent += $this->_doMailTransaction(
  453. $message, $reversePath, array($forwardPath), $failedRecipients
  454. );
  455. }
  456. return $sent;
  457. }
  458. /** Try to determine the hostname of the server this is run on */
  459. private function _lookupHostname()
  460. {
  461. if (!empty($_SERVER['SERVER_NAME'])
  462. && $this->_isFqdn($_SERVER['SERVER_NAME']))
  463. {
  464. $this->_domain = $_SERVER['SERVER_NAME'];
  465. }
  466. elseif (!empty($_SERVER['SERVER_ADDR']))
  467. {
  468. $this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']);
  469. }
  470. }
  471. /** Determine is the $hostname is a fully-qualified name */
  472. private function _isFqdn($hostname)
  473. {
  474. //We could do a really thorough check, but there's really no point
  475. if (false !== $dotPos = strpos($hostname, '.'))
  476. {
  477. return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1);
  478. }
  479. else
  480. {
  481. return false;
  482. }
  483. }
  484. /**
  485. * Destructor.
  486. */
  487. public function __destruct()
  488. {
  489. $this->stop();
  490. }
  491. }