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

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