vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php line 102

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