PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/SwiftMailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php

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