PageRenderTime 59ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/framework/vendor/swift/lib/classes/Swift/Transport/AbstractSmtpTransport.php

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