Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/** This file is part of SwiftMailer.* (c) 2004-2009 Chris Corbyn** For the full copyright and license information, please view the LICENSE* file that was distributed with this source code.*///@require 'Swift/Transport.php';//@require 'Swift/Transport/IoBuffer.php';//@require 'Swift/Transport/CommandSentException.php';//@require 'Swift/TransportException.php';//@require 'Swift/Mime/Message.php';//@require 'Swift/Events/EventDispatcher.php';//@require 'Swift/Events/EventListener.php';/*** Sends Messages over SMTP.** @package Swift* @subpackage Transport* @author Chris Corbyn*/abstract class Swift_Transport_AbstractSmtpTransportimplements Swift_Transport{/** Input-Output buffer for sending/receiving SMTP commands and responses */protected $_buffer;/** Connection status */protected $_started = false;/** The domain name to use in HELO command */protected $_domain = '[127.0.0.1]';/** The event dispatching layer */protected $_eventDispatcher;/** Return an array of params for the Buffer */abstract protected function _getBufferParams();/*** Creates a new EsmtpTransport using the given I/O buffer.** @param Swift_Transport_IoBuffer $buf* @param Swift_Events_EventDispatcher $dispatcher*/public function __construct(Swift_Transport_IoBuffer $buf,Swift_Events_EventDispatcher $dispatcher){$this->_eventDispatcher = $dispatcher;$this->_buffer = $buf;$this->_lookupHostname();}/*** Set the name of the local domain which Swift will identify itself as.* This should be a fully-qualified domain name and should be truly the domain* you're using. If your server doesn't have a domain name, use the IP in square* brackets (i.e. [127.0.0.1]).** @param string $domain*/public function setLocalDomain($domain){$this->_domain = $domain;return $this;}/*** Get the name of the domain Swift will identify as.** @return string*/public function getLocalDomain(){return $this->_domain;}/*** Start the SMTP connection.*/public function start(){if (!$this->_started){if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)){$this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted');if ($evt->bubbleCancelled()){return;}}try{$this->_buffer->initialize($this->_getBufferParams());}catch (Swift_TransportException $e){$this->_throwException($e);}$this->_readGreeting();$this->_doHeloCommand();if ($evt){$this->_eventDispatcher->dispatchEvent($evt, 'transportStarted');}$this->_started = true;}}/*** Test if an SMTP connection has been established.** @return boolean*/public function isStarted(){return $this->_started;}/*** Send the given Message.** Recipient/sender data will be retreived from the Message API.* The return value is the number of recipients who were accepted for delivery.** @param Swift_Mime_Message $message* @param string[] &$failedRecipients to collect failures by-reference* @return int*/public function send(Swift_Mime_Message $message, &$failedRecipients = null){$sent = 0;$failedRecipients = (array) $failedRecipients;if (!$reversePath = $this->_getReversePath($message)){throw new Swift_TransportException('Cannot send message without a sender address');}if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)){$this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');if ($evt->bubbleCancelled()){return 0;}}$to = (array) $message->getTo();$cc = (array) $message->getCc();$bcc = (array) $message->getBcc();$message->setBcc(array());try{$sent += $this->_sendTo($message, $reversePath, $to, $failedRecipients);$sent += $this->_sendCc($message, $reversePath, $cc, $failedRecipients);$sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients);}catch (Exception $e){$message->setBcc($bcc);throw $e;}$message->setBcc($bcc);if ($evt){if ($sent == count($to) + count($cc) + count($bcc)){$evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);}elseif ($sent > 0){$evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE);}else{$evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);}$evt->setFailedRecipients($failedRecipients);$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');}$message->generateId(); //Make sure a new Message ID is usedreturn $sent;}/*** Stop the SMTP connection.*/public function stop(){if ($this->_started){if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)){$this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped');if ($evt->bubbleCancelled()){return;}}try{$this->executeCommand("QUIT\r\n", array(221));}catch (Swift_TransportException $e) {}try{$this->_buffer->terminate();if ($evt){$this->_eventDispatcher->dispatchEvent($evt, 'transportStopped');}}catch (Swift_TransportException $e){$this->_throwException($e);}}$this->_started = false;}/*** Register a plugin.** @param Swift_Events_EventListener $plugin*/public function registerPlugin(Swift_Events_EventListener $plugin){$this->_eventDispatcher->bindEventListener($plugin);}/*** Reset the current mail transaction.*/public function reset(){$this->executeCommand("RSET\r\n", array(250));}/*** Get the IoBuffer where read/writes are occurring.** @return Swift_Transport_IoBuffer*/public function getBuffer(){return $this->_buffer;}/*** Run a command against the buffer, expecting the given response codes.** If no response codes are given, the response will not be validated.* If codes are given, an exception will be thrown on an invalid response.** @param string $command* @param int[] $codes* @param string[] &$failures* @return string*/public function executeCommand($command, $codes = array(), &$failures = null){$failures = (array) $failures;$seq = $this->_buffer->write($command);$response = $this->_getFullResponse($seq);if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)){$this->_eventDispatcher->dispatchEvent($evt, 'commandSent');}$this->_assertResponseCode($response, $codes);return $response;}// -- Protected methods/** Read the opening SMTP greeting */protected function _readGreeting(){$this->_assertResponseCode($this->_getFullResponse(0), array(220));}/** Send the HELO welcome */protected function _doHeloCommand(){$this->executeCommand(sprintf("HELO %s\r\n", $this->_domain), array(250));}/** Send the MAIL FROM command */protected function _doMailFromCommand($address){$this->executeCommand(sprintf("MAIL FROM: <%s>\r\n", $address), array(250));}/** Send the RCPT TO command */protected function _doRcptToCommand($address){$this->executeCommand(sprintf("RCPT TO: <%s>\r\n", $address), array(250, 251, 252));}/** Send the DATA command */protected function _doDataCommand(){$this->executeCommand("DATA\r\n", array(354));}/** Stream the contents of the message over the buffer */protected function _streamMessage(Swift_Mime_Message $message){$this->_buffer->setWriteTranslations(array("\r\n." => "\r\n.."));try{$message->toByteStream($this->_buffer);$this->_buffer->flushBuffers();}catch (Swift_TransportException $e){$this->_throwException($e);}$this->_buffer->setWriteTranslations(array());$this->executeCommand("\r\n.\r\n", array(250));}/** Determine the best-use reverse path for this message */protected function _getReversePath(Swift_Mime_Message $message){$return = $message->getReturnPath();$sender = $message->getSender();$from = $message->getFrom();$path = null;if (!empty($return)){$path = $return;}elseif (!empty($sender)){// Don't use array_keysreset($sender); // Reset Pointer to first pos$path = key($sender); // Get key}elseif (!empty($from)){reset($from); // Reset Pointer to first pos$path = key($from); // Get key}return $path;}/** Throw a TransportException, first sending it to any listeners */protected function _throwException(Swift_TransportException $e){if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)){$this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');if (!$evt->bubbleCancelled()){throw $e;}}else{throw $e;}}/** Throws an Exception if a response code is incorrect */protected function _assertResponseCode($response, $wanted){list($code, $separator, $text) = sscanf($response, '%3d%[ -]%s');$valid = (empty($wanted) || in_array($code, $wanted));if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response,$valid)){$this->_eventDispatcher->dispatchEvent($evt, 'responseReceived');}if (!$valid){$this->_throwException(new Swift_TransportException('Expected response code ' . implode('/', $wanted) . ' but got code ' .'"' . $code . '", with message "' . $response . '"'));}}/** Get an entire multi-line response using its sequence number */protected function _getFullResponse($seq){$response = '';try{do{$line = $this->_buffer->readLine($seq);$response .= $line;}while (null !== $line && false !== $line && ' ' != $line{3});}catch (Swift_TransportException $e){$this->_throwException($e);}return $response;}// -- Private methods/** Send an email to the given recipients from the given reverse path */private function _doMailTransaction($message, $reversePath,array $recipients, array &$failedRecipients){$sent = 0;$this->_doMailFromCommand($reversePath);foreach ($recipients as $forwardPath){try{$this->_doRcptToCommand($forwardPath);$sent++;}catch (Swift_TransportException $e){$failedRecipients[] = $forwardPath;}}if ($sent != 0){$this->_doDataCommand();$this->_streamMessage($message);}else{$this->reset();}return $sent;}/** Send a message to the given To: recipients */private function _sendTo(Swift_Mime_Message $message, $reversePath,array $to, array &$failedRecipients){if (empty($to)){return 0;}return $this->_doMailTransaction($message, $reversePath, array_keys($to),$failedRecipients);}/** Send a message to the given Cc: recipients */private function _sendCc(Swift_Mime_Message $message, $reversePath,array $cc, array &$failedRecipients){if (empty($cc)){return 0;}return $this->_doMailTransaction($message, $reversePath, array_keys($cc),$failedRecipients);}/** Send a message to all Bcc: recipients */private function _sendBcc(Swift_Mime_Message $message, $reversePath,array $bcc, array &$failedRecipients){$sent = 0;foreach ($bcc as $forwardPath => $name){$message->setBcc(array($forwardPath => $name));$sent += $this->_doMailTransaction($message, $reversePath, array($forwardPath), $failedRecipients);}return $sent;}/** Try to determine the hostname of the server this is run on */private function _lookupHostname(){if (!empty($_SERVER['SERVER_NAME'])&& $this->_isFqdn($_SERVER['SERVER_NAME'])){$this->_domain = $_SERVER['SERVER_NAME'];}elseif (!empty($_SERVER['SERVER_ADDR'])){$this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']);}}/** Determine is the $hostname is a fully-qualified name */private function _isFqdn($hostname){//We could do a really thorough check, but there's really no pointif (false !== $dotPos = strpos($hostname, '.')){return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1);}else{return false;}}/*** Destructor.*/public function __destruct(){$this->stop();}}