vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php line 16

Open in your IDE?
  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 with ESMTP support.
  11.  *
  12.  * @author Chris Corbyn
  13.  */
  14. class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTransport implements Swift_Transport_SmtpAgent
  15. {
  16.     /**
  17.      * ESMTP extension handlers.
  18.      *
  19.      * @var Swift_Transport_EsmtpHandler[]
  20.      */
  21.     private $handlers = [];
  22.     /**
  23.      * ESMTP capabilities.
  24.      *
  25.      * @var string[]
  26.      */
  27.     private $capabilities = [];
  28.     /**
  29.      * Connection buffer parameters.
  30.      *
  31.      * @var array
  32.      */
  33.     private $params = [
  34.         'protocol' => 'tcp',
  35.         'host' => 'localhost',
  36.         'port' => 25,
  37.         'timeout' => 30,
  38.         'blocking' => 1,
  39.         'tls' => false,
  40.         'type' => Swift_Transport_IoBuffer::TYPE_SOCKET,
  41.         'stream_context_options' => [],
  42.         ];
  43.     /**
  44.      * Creates a new EsmtpTransport using the given I/O buffer.
  45.      *
  46.      * @param Swift_Transport_EsmtpHandler[] $extensionHandlers
  47.      * @param string                         $localDomain
  48.      */
  49.     public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlersSwift_Events_EventDispatcher $dispatcher$localDomain '127.0.0.1'Swift_AddressEncoder $addressEncoder null)
  50.     {
  51.         parent::__construct($buf$dispatcher$localDomain$addressEncoder);
  52.         $this->setExtensionHandlers($extensionHandlers);
  53.     }
  54.     /**
  55.      * Set the host to connect to.
  56.      *
  57.      * Literal IPv6 addresses should be wrapped in square brackets.
  58.      *
  59.      * @param string $host
  60.      *
  61.      * @return $this
  62.      */
  63.     public function setHost($host)
  64.     {
  65.         $this->params['host'] = $host;
  66.         return $this;
  67.     }
  68.     /**
  69.      * Get the host to connect to.
  70.      *
  71.      * @return string
  72.      */
  73.     public function getHost()
  74.     {
  75.         return $this->params['host'];
  76.     }
  77.     /**
  78.      * Set the port to connect to.
  79.      *
  80.      * @param int $port
  81.      *
  82.      * @return $this
  83.      */
  84.     public function setPort($port)
  85.     {
  86.         $this->params['port'] = (int) $port;
  87.         return $this;
  88.     }
  89.     /**
  90.      * Get the port to connect to.
  91.      *
  92.      * @return int
  93.      */
  94.     public function getPort()
  95.     {
  96.         return $this->params['port'];
  97.     }
  98.     /**
  99.      * Set the connection timeout.
  100.      *
  101.      * @param int $timeout seconds
  102.      *
  103.      * @return $this
  104.      */
  105.     public function setTimeout($timeout)
  106.     {
  107.         $this->params['timeout'] = (int) $timeout;
  108.         $this->buffer->setParam('timeout', (int) $timeout);
  109.         return $this;
  110.     }
  111.     /**
  112.      * Get the connection timeout.
  113.      *
  114.      * @return int
  115.      */
  116.     public function getTimeout()
  117.     {
  118.         return $this->params['timeout'];
  119.     }
  120.     /**
  121.      * Set the encryption type (tls or ssl).
  122.      *
  123.      * @param string $encryption
  124.      *
  125.      * @return $this
  126.      */
  127.     public function setEncryption($encryption)
  128.     {
  129.         $encryption strtolower($encryption ?? '');
  130.         if ('tls' == $encryption) {
  131.             $this->params['protocol'] = 'tcp';
  132.             $this->params['tls'] = true;
  133.         } else {
  134.             $this->params['protocol'] = $encryption;
  135.             $this->params['tls'] = false;
  136.         }
  137.         return $this;
  138.     }
  139.     /**
  140.      * Get the encryption type.
  141.      *
  142.      * @return string
  143.      */
  144.     public function getEncryption()
  145.     {
  146.         return $this->params['tls'] ? 'tls' $this->params['protocol'];
  147.     }
  148.     /**
  149.      * Sets the stream context options.
  150.      *
  151.      * @param array $options
  152.      *
  153.      * @return $this
  154.      */
  155.     public function setStreamOptions($options)
  156.     {
  157.         $this->params['stream_context_options'] = $options;
  158.         return $this;
  159.     }
  160.     /**
  161.      * Returns the stream context options.
  162.      *
  163.      * @return array
  164.      */
  165.     public function getStreamOptions()
  166.     {
  167.         return $this->params['stream_context_options'];
  168.     }
  169.     /**
  170.      * Sets the source IP.
  171.      *
  172.      * IPv6 addresses should be wrapped in square brackets.
  173.      *
  174.      * @param string $source
  175.      *
  176.      * @return $this
  177.      */
  178.     public function setSourceIp($source)
  179.     {
  180.         $this->params['sourceIp'] = $source;
  181.         return $this;
  182.     }
  183.     /**
  184.      * Returns the IP used to connect to the destination.
  185.      *
  186.      * @return string
  187.      */
  188.     public function getSourceIp()
  189.     {
  190.         return $this->params['sourceIp'] ?? null;
  191.     }
  192.     /**
  193.      * Sets whether SMTP pipelining is enabled.
  194.      *
  195.      * By default, support is auto-detected using the PIPELINING SMTP extension.
  196.      * Use this function to override that in the unlikely event of compatibility
  197.      * issues.
  198.      *
  199.      * @param bool $enabled
  200.      *
  201.      * @return $this
  202.      */
  203.     public function setPipelining($enabled)
  204.     {
  205.         $this->pipelining $enabled;
  206.         return $this;
  207.     }
  208.     /**
  209.      * Returns whether SMTP pipelining is enabled.
  210.      *
  211.      * @return bool|null a boolean if pipelining is explicitly enabled or disabled,
  212.      *                   or null if support is auto-detected
  213.      */
  214.     public function getPipelining()
  215.     {
  216.         return $this->pipelining;
  217.     }
  218.     /**
  219.      * Set ESMTP extension handlers.
  220.      *
  221.      * @param Swift_Transport_EsmtpHandler[] $handlers
  222.      *
  223.      * @return $this
  224.      */
  225.     public function setExtensionHandlers(array $handlers)
  226.     {
  227.         $assoc = [];
  228.         foreach ($handlers as $handler) {
  229.             $assoc[$handler->getHandledKeyword()] = $handler;
  230.         }
  231.         uasort($assoc, function ($a$b) {
  232.             return $a->getPriorityOver($b->getHandledKeyword());
  233.         });
  234.         $this->handlers $assoc;
  235.         $this->setHandlerParams();
  236.         return $this;
  237.     }
  238.     /**
  239.      * Get ESMTP extension handlers.
  240.      *
  241.      * @return Swift_Transport_EsmtpHandler[]
  242.      */
  243.     public function getExtensionHandlers()
  244.     {
  245.         return array_values($this->handlers);
  246.     }
  247.     /**
  248.      * Run a command against the buffer, expecting the given response codes.
  249.      *
  250.      * If no response codes are given, the response will not be validated.
  251.      * If codes are given, an exception will be thrown on an invalid response.
  252.      *
  253.      * @param string   $command
  254.      * @param int[]    $codes
  255.      * @param string[] $failures An array of failures by-reference
  256.      * @param bool     $pipeline Do not wait for response
  257.      * @param string   $address  the address, if command is RCPT TO
  258.      *
  259.      * @return string|null The server response, or null if pipelining is enabled
  260.      */
  261.     public function executeCommand($command$codes = [], &$failures null$pipeline false$address null)
  262.     {
  263.         $failures = (array) $failures;
  264.         $stopSignal false;
  265.         $response null;
  266.         foreach ($this->getActiveHandlers() as $handler) {
  267.             $response $handler->onCommand(
  268.                 $this$command$codes$failures$stopSignal
  269.                 );
  270.             if ($stopSignal) {
  271.                 return $response;
  272.             }
  273.         }
  274.         return parent::executeCommand($command$codes$failures$pipeline$address);
  275.     }
  276.     /** Mixin handling method for ESMTP handlers */
  277.     public function __call($method$args)
  278.     {
  279.         foreach ($this->handlers as $handler) {
  280.             if (\in_array(strtolower($method),
  281.                 array_map('strtolower', (array) $handler->exposeMixinMethods())
  282.                 )) {
  283.                 $return = \call_user_func_array([$handler$method], $args);
  284.                 // Allow fluid method calls
  285.                 if (null === $return && 'set' == substr($method03)) {
  286.                     return $this;
  287.                 } else {
  288.                     return $return;
  289.                 }
  290.             }
  291.         }
  292.         trigger_error('Call to undefined method '.$methodE_USER_ERROR);
  293.     }
  294.     /** Get the params to initialize the buffer */
  295.     protected function getBufferParams()
  296.     {
  297.         return $this->params;
  298.     }
  299.     /** Overridden to perform EHLO instead */
  300.     protected function doHeloCommand()
  301.     {
  302.         try {
  303.             $response $this->executeCommand(
  304.                 sprintf("EHLO %s\r\n"$this->domain), [250]
  305.                 );
  306.         } catch (Swift_TransportException $e) {
  307.             return parent::doHeloCommand();
  308.         }
  309.         if ($this->params['tls']) {
  310.             try {
  311.                 $this->executeCommand("STARTTLS\r\n", [220]);
  312.                 if (!$this->buffer->startTLS()) {
  313.                     throw new Swift_TransportException('Unable to connect with TLS encryption');
  314.                 }
  315.                 try {
  316.                     $response $this->executeCommand(
  317.                         sprintf("EHLO %s\r\n"$this->domain), [250]
  318.                         );
  319.                 } catch (Swift_TransportException $e) {
  320.                     return parent::doHeloCommand();
  321.                 }
  322.             } catch (Swift_TransportException $e) {
  323.                 $this->throwException($e);
  324.             }
  325.         }
  326.         $this->capabilities $this->getCapabilities($response);
  327.         if (!isset($this->pipelining)) {
  328.             $this->pipelining = isset($this->capabilities['PIPELINING']);
  329.         }
  330.         $this->setHandlerParams();
  331.         foreach ($this->getActiveHandlers() as $handler) {
  332.             $handler->afterEhlo($this);
  333.         }
  334.     }
  335.     /** Overridden to add Extension support */
  336.     protected function doMailFromCommand($address)
  337.     {
  338.         $address $this->addressEncoder->encodeString($address);
  339.         $handlers $this->getActiveHandlers();
  340.         $params = [];
  341.         foreach ($handlers as $handler) {
  342.             $params array_merge($params, (array) $handler->getMailParams());
  343.         }
  344.         $paramStr = !empty($params) ? ' '.implode(' '$params) : '';
  345.         $this->executeCommand(
  346.             sprintf("MAIL FROM:<%s>%s\r\n"$address$paramStr), [250], $failurestrue
  347.             );
  348.     }
  349.     /** Overridden to add Extension support */
  350.     protected function doRcptToCommand($address)
  351.     {
  352.         $address $this->addressEncoder->encodeString($address);
  353.         $handlers $this->getActiveHandlers();
  354.         $params = [];
  355.         foreach ($handlers as $handler) {
  356.             $params array_merge($params, (array) $handler->getRcptParams());
  357.         }
  358.         $paramStr = !empty($params) ? ' '.implode(' '$params) : '';
  359.         $this->executeCommand(
  360.             sprintf("RCPT TO:<%s>%s\r\n"$address$paramStr), [250251252], $failurestrue$address
  361.             );
  362.     }
  363.     /** Determine ESMTP capabilities by function group */
  364.     private function getCapabilities($ehloResponse)
  365.     {
  366.         $capabilities = [];
  367.         $ehloResponse trim($ehloResponse ?? '');
  368.         $lines explode("\r\n"$ehloResponse);
  369.         array_shift($lines);
  370.         foreach ($lines as $line) {
  371.             if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di'$line$matches)) {
  372.                 $keyword strtoupper($matches[1]);
  373.                 $paramStr strtoupper(ltrim($matches[2], ' ='));
  374.                 $params = !empty($paramStr) ? explode(' '$paramStr) : [];
  375.                 $capabilities[$keyword] = $params;
  376.             }
  377.         }
  378.         return $capabilities;
  379.     }
  380.     /** Set parameters which are used by each extension handler */
  381.     private function setHandlerParams()
  382.     {
  383.         foreach ($this->handlers as $keyword => $handler) {
  384.             if (\array_key_exists($keyword$this->capabilities)) {
  385.                 $handler->setKeywordParams($this->capabilities[$keyword]);
  386.             }
  387.         }
  388.     }
  389.     /** Get ESMTP handlers which are currently ok to use */
  390.     private function getActiveHandlers()
  391.     {
  392.         $handlers = [];
  393.         foreach ($this->handlers as $keyword => $handler) {
  394.             if (\array_key_exists($keyword$this->capabilities)) {
  395.                 $handlers[] = $handler;
  396.             }
  397.         }
  398.         return $handlers;
  399.     }
  400. }