vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php line 231

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.  * A generic IoBuffer implementation supporting remote sockets and local processes.
  11.  *
  12.  * @author     Chris Corbyn
  13.  */
  14. class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_Transport_IoBuffer
  15. {
  16.     /** A primary socket */
  17.     private $stream;
  18.     /** The input stream */
  19.     private $in;
  20.     /** The output stream */
  21.     private $out;
  22.     /** Buffer initialization parameters */
  23.     private $params = [];
  24.     /** The ReplacementFilterFactory */
  25.     private $replacementFactory;
  26.     /** Translations performed on data being streamed into the buffer */
  27.     private $translations = [];
  28.     /**
  29.      * Create a new StreamBuffer using $replacementFactory for transformations.
  30.      */
  31.     public function __construct(Swift_ReplacementFilterFactory $replacementFactory)
  32.     {
  33.         $this->replacementFactory $replacementFactory;
  34.     }
  35.     /**
  36.      * Perform any initialization needed, using the given $params.
  37.      *
  38.      * Parameters will vary depending upon the type of IoBuffer used.
  39.      */
  40.     public function initialize(array $params)
  41.     {
  42.         $this->params $params;
  43.         switch ($params['type']) {
  44.             case self::TYPE_PROCESS:
  45.                 $this->establishProcessConnection();
  46.                 break;
  47.             case self::TYPE_SOCKET:
  48.             default:
  49.                 $this->establishSocketConnection();
  50.                 break;
  51.         }
  52.     }
  53.     /**
  54.      * Set an individual param on the buffer (e.g. switching to SSL).
  55.      *
  56.      * @param string $param
  57.      * @param mixed  $value
  58.      */
  59.     public function setParam($param$value)
  60.     {
  61.         if (isset($this->stream)) {
  62.             switch ($param) {
  63.                 case 'timeout':
  64.                     if ($this->stream) {
  65.                         stream_set_timeout($this->stream$value);
  66.                     }
  67.                     break;
  68.                 case 'blocking':
  69.                     if ($this->stream) {
  70.                         stream_set_blocking($this->stream1);
  71.                     }
  72.             }
  73.         }
  74.         $this->params[$param] = $value;
  75.     }
  76.     public function startTLS()
  77.     {
  78.         // STREAM_CRYPTO_METHOD_TLS_CLIENT only allow tls1.0 connections (some php versions)
  79.         // To support modern tls we allow explicit tls1.0, tls1.1, tls1.2
  80.         // Ssl3 and older are not allowed because they are vulnerable
  81.         // @TODO make tls arguments configurable
  82.         return stream_socket_enable_crypto($this->streamtrueSTREAM_CRYPTO_METHOD_TLSv1_0_CLIENT STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT);
  83.     }
  84.     /**
  85.      * Perform any shutdown logic needed.
  86.      */
  87.     public function terminate()
  88.     {
  89.         if (isset($this->stream)) {
  90.             switch ($this->params['type']) {
  91.                 case self::TYPE_PROCESS:
  92.                     fclose($this->in);
  93.                     fclose($this->out);
  94.                     proc_close($this->stream);
  95.                     break;
  96.                 case self::TYPE_SOCKET:
  97.                 default:
  98.                     fclose($this->stream);
  99.                     break;
  100.             }
  101.         }
  102.         $this->stream null;
  103.         $this->out null;
  104.         $this->in null;
  105.     }
  106.     /**
  107.      * Set an array of string replacements which should be made on data written
  108.      * to the buffer.
  109.      *
  110.      * This could replace LF with CRLF for example.
  111.      *
  112.      * @param string[] $replacements
  113.      */
  114.     public function setWriteTranslations(array $replacements)
  115.     {
  116.         foreach ($this->translations as $search => $replace) {
  117.             if (!isset($replacements[$search])) {
  118.                 $this->removeFilter($search);
  119.                 unset($this->translations[$search]);
  120.             }
  121.         }
  122.         foreach ($replacements as $search => $replace) {
  123.             if (!isset($this->translations[$search])) {
  124.                 $this->addFilter(
  125.                     $this->replacementFactory->createFilter($search$replace), $search
  126.                     );
  127.                 $this->translations[$search] = true;
  128.             }
  129.         }
  130.     }
  131.     /**
  132.      * Get a line of output (including any CRLF).
  133.      *
  134.      * The $sequence number comes from any writes and may or may not be used
  135.      * depending upon the implementation.
  136.      *
  137.      * @param int $sequence of last write to scan from
  138.      *
  139.      * @return string
  140.      *
  141.      * @throws Swift_IoException
  142.      */
  143.     public function readLine($sequence)
  144.     {
  145.         if (isset($this->out) && !feof($this->out)) {
  146.             $line fgets($this->out);
  147.             if (== strlen($line)) {
  148.                 $metas stream_get_meta_data($this->out);
  149.                 if ($metas['timed_out']) {
  150.                     throw new Swift_IoException(
  151.                         'Connection to '.
  152.                             $this->getReadConnectionDescription().
  153.                         ' Timed Out'
  154.                     );
  155.                 }
  156.             }
  157.             return $line;
  158.         }
  159.     }
  160.     /**
  161.      * Reads $length bytes from the stream into a string and moves the pointer
  162.      * through the stream by $length.
  163.      *
  164.      * If less bytes exist than are requested the remaining bytes are given instead.
  165.      * If no bytes are remaining at all, boolean false is returned.
  166.      *
  167.      * @param int $length
  168.      *
  169.      * @return string|bool
  170.      *
  171.      * @throws Swift_IoException
  172.      */
  173.     public function read($length)
  174.     {
  175.         if (isset($this->out) && !feof($this->out)) {
  176.             $ret fread($this->out$length);
  177.             if (== strlen($ret)) {
  178.                 $metas stream_get_meta_data($this->out);
  179.                 if ($metas['timed_out']) {
  180.                     throw new Swift_IoException(
  181.                         'Connection to '.
  182.                             $this->getReadConnectionDescription().
  183.                         ' Timed Out'
  184.                     );
  185.                 }
  186.             }
  187.             return $ret;
  188.         }
  189.     }
  190.     /** Not implemented */
  191.     public function setReadPointer($byteOffset)
  192.     {
  193.     }
  194.     /** Flush the stream contents */
  195.     protected function flush()
  196.     {
  197.         if (isset($this->in)) {
  198.             fflush($this->in);
  199.         }
  200.     }
  201.     /** Write this bytes to the stream */
  202.     protected function doCommit($bytes)
  203.     {
  204.         if (isset($this->in)) {
  205.             $bytesToWrite strlen($bytes);
  206.             $totalBytesWritten 0;
  207.             while ($totalBytesWritten $bytesToWrite) {
  208.                 $bytesWritten fwrite($this->insubstr($bytes$totalBytesWritten));
  209.                 if (false === $bytesWritten || === $bytesWritten) {
  210.                     break;
  211.                 }
  212.                 $totalBytesWritten += $bytesWritten;
  213.             }
  214.             if ($totalBytesWritten 0) {
  215.                 return ++$this->sequence;
  216.             }
  217.         }
  218.     }
  219.     /**
  220.      * Establishes a connection to a remote server.
  221.      */
  222.     private function establishSocketConnection()
  223.     {
  224.         $host $this->params['host'];
  225.         if (!empty($this->params['protocol'])) {
  226.             $host $this->params['protocol'].'://'.$host;
  227.         }
  228.         $timeout 15;
  229.         if (!empty($this->params['timeout'])) {
  230.             $timeout $this->params['timeout'];
  231.         }
  232.         $options = [];
  233.         if (!empty($this->params['sourceIp'])) {
  234.             $options['socket']['bindto'] = $this->params['sourceIp'].':0';
  235.         }
  236.         if (isset($this->params['stream_context_options'])) {
  237.             $options array_merge($options$this->params['stream_context_options']);
  238.         }
  239.         $streamContext stream_context_create($options);
  240.         set_error_handler(function ($type$msg) {
  241.             throw new Swift_TransportException('Connection could not be established with host '.$this->params['host'].' :'.$msg);
  242.         });
  243.         try {
  244.             $this->stream stream_socket_client($host.':'.$this->params['port'], $errno$errstr$timeoutSTREAM_CLIENT_CONNECT$streamContext);
  245.         } finally {
  246.             restore_error_handler();
  247.         }
  248.         if (!empty($this->params['blocking'])) {
  249.             stream_set_blocking($this->stream1);
  250.         } else {
  251.             stream_set_blocking($this->stream0);
  252.         }
  253.         stream_set_timeout($this->stream$timeout);
  254.         $this->in = &$this->stream;
  255.         $this->out = &$this->stream;
  256.     }
  257.     /**
  258.      * Opens a process for input/output.
  259.      */
  260.     private function establishProcessConnection()
  261.     {
  262.         $command $this->params['command'];
  263.         $descriptorSpec = [
  264.             => ['pipe''r'],
  265.             => ['pipe''w'],
  266.             => ['pipe''w'],
  267.             ];
  268.         $pipes = [];
  269.         $this->stream proc_open($command$descriptorSpec$pipes);
  270.         stream_set_blocking($pipes[2], 0);
  271.         if ($err stream_get_contents($pipes[2])) {
  272.             throw new Swift_TransportException(
  273.                 'Process could not be started ['.$err.']'
  274.                 );
  275.         }
  276.         $this->in = &$pipes[0];
  277.         $this->out = &$pipes[1];
  278.     }
  279.     private function getReadConnectionDescription()
  280.     {
  281.         switch ($this->params['type']) {
  282.             case self::TYPE_PROCESS:
  283.                 return 'Process '.$this->params['command'];
  284.                 break;
  285.             case self::TYPE_SOCKET:
  286.             default:
  287.                 $host $this->params['host'];
  288.                 if (!empty($this->params['protocol'])) {
  289.                     $host $this->params['protocol'].'://'.$host;
  290.                 }
  291.                 $host .= ':'.$this->params['port'];
  292.                 return $host;
  293.                 break;
  294.         }
  295.     }
  296. }