File indexing completed on 2024-05-26 06:03:11

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_Mail
0018  * @subpackage Protocol
0019  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0020  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0021  * @version    $Id$
0022  */
0023 
0024 
0025 /**
0026  * @see Zend_Validate
0027  */
0028 // require_once 'Zend/Validate.php';
0029 
0030 
0031 /**
0032  * @see Zend_Validate_Hostname
0033  */
0034 // require_once 'Zend/Validate/Hostname.php';
0035 
0036 
0037 /**
0038  * Zend_Mail_Protocol_Abstract
0039  *
0040  * Provides low-level methods for concrete adapters to communicate with a remote mail server and track requests and responses.
0041  *
0042  * @category   Zend
0043  * @package    Zend_Mail
0044  * @subpackage Protocol
0045  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0046  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0047  * @version    $Id$
0048  * @todo Implement proxy settings
0049  */
0050 abstract class Zend_Mail_Protocol_Abstract
0051 {
0052     /**
0053      * Mail default EOL string
0054      */
0055     const EOL = "\r\n";
0056 
0057 
0058     /**
0059      * Default timeout in seconds for initiating session
0060      */
0061     const TIMEOUT_CONNECTION = 30;
0062 
0063     /**
0064      * Maximum of the transaction log
0065      * @var integer
0066      */
0067     protected $_maximumLog = 64;
0068 
0069 
0070     /**
0071      * Hostname or IP address of remote server
0072      * @var string
0073      */
0074     protected $_host;
0075 
0076 
0077     /**
0078      * Port number of connection
0079      * @var integer
0080      */
0081     protected $_port;
0082 
0083 
0084     /**
0085      * Instance of Zend_Validate to check hostnames
0086      * @var Zend_Validate
0087      */
0088     protected $_validHost;
0089 
0090 
0091     /**
0092      * Socket connection resource
0093      * @var resource
0094      */
0095     protected $_socket;
0096 
0097 
0098     /**
0099      * Last request sent to server
0100      * @var string
0101      */
0102     protected $_request;
0103 
0104 
0105     /**
0106      * Array of server responses to last request
0107      * @var array
0108      */
0109     protected $_response;
0110 
0111 
0112     /**
0113      * String template for parsing server responses using sscanf (default: 3 digit code and response string)
0114      * @var resource
0115      * @deprecated Since 1.10.3
0116      */
0117     protected $_template = '%d%s';
0118 
0119 
0120     /**
0121      * Log of mail requests and server responses for a session
0122      * @var array
0123      */
0124     private $_log = array();
0125 
0126 
0127     /**
0128      * Constructor.
0129      *
0130      * @param  string  $host OPTIONAL Hostname of remote connection (default: 127.0.0.1)
0131      * @param  integer $port OPTIONAL Port number (default: null)
0132      * @throws Zend_Mail_Protocol_Exception
0133      * @return void
0134      */
0135     public function __construct($host = '127.0.0.1', $port = null)
0136     {
0137         $this->_validHost = new Zend_Validate();
0138         $this->_validHost->addValidator(new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL));
0139 
0140         if (!$this->_validHost->isValid($host)) {
0141             /**
0142              * @see Zend_Mail_Protocol_Exception
0143              */
0144             // require_once 'Zend/Mail/Protocol/Exception.php';
0145             throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost->getMessages()));
0146         }
0147 
0148         $this->_host = $host;
0149         $this->_port = $port;
0150     }
0151 
0152 
0153     /**
0154      * Class destructor to cleanup open resources
0155      *
0156      * @return void
0157      */
0158     public function __destruct()
0159     {
0160         $this->_disconnect();
0161     }
0162 
0163     /**
0164      * Set the maximum log size
0165      *
0166      * @param integer $maximumLog Maximum log size
0167      * @return void
0168      */
0169     public function setMaximumLog($maximumLog)
0170     {
0171         $this->_maximumLog = (int) $maximumLog;
0172     }
0173 
0174 
0175     /**
0176      * Get the maximum log size
0177      *
0178      * @return int the maximum log size
0179      */
0180     public function getMaximumLog()
0181     {
0182         return $this->_maximumLog;
0183     }
0184 
0185 
0186     /**
0187      * Create a connection to the remote host
0188      *
0189      * Concrete adapters for this class will implement their own unique connect scripts, using the _connect() method to create the socket resource.
0190      */
0191     abstract public function connect();
0192 
0193 
0194     /**
0195      * Retrieve the last client request
0196      *
0197      * @return string
0198      */
0199     public function getRequest()
0200     {
0201         return $this->_request;
0202     }
0203 
0204 
0205     /**
0206      * Retrieve the last server response
0207      *
0208      * @return array
0209      */
0210     public function getResponse()
0211     {
0212         return $this->_response;
0213     }
0214 
0215 
0216     /**
0217      * Retrieve the transaction log
0218      *
0219      * @return string
0220      */
0221     public function getLog()
0222     {
0223         return implode('', $this->_log);
0224     }
0225 
0226 
0227     /**
0228      * Reset the transaction log
0229      *
0230      * @return void
0231      */
0232     public function resetLog()
0233     {
0234         $this->_log = array();
0235     }
0236 
0237     /**
0238      * Add the transaction log
0239      *
0240      * @param  string new transaction
0241      * @return void
0242      */
0243     protected function _addLog($value)
0244     {
0245         if ($this->_maximumLog >= 0 && count($this->_log) >= $this->_maximumLog) {
0246             array_shift($this->_log);
0247         }
0248 
0249         $this->_log[] = $value;
0250     }
0251 
0252     /**
0253      * Connect to the server using the supplied transport and target
0254      *
0255      * An example $remote string may be 'tcp://mail.example.com:25' or 'ssh://hostname.com:2222'
0256      *
0257      * @param  string $remote Remote
0258      * @throws Zend_Mail_Protocol_Exception
0259      * @return boolean
0260      */
0261     protected function _connect($remote)
0262     {
0263         $errorNum = 0;
0264         $errorStr = '';
0265 
0266         // open connection
0267         $this->_socket = @stream_socket_client($remote, $errorNum, $errorStr, self::TIMEOUT_CONNECTION);
0268 
0269         if ($this->_socket === false) {
0270             if ($errorNum == 0) {
0271                 $errorStr = 'Could not open socket';
0272             }
0273             /**
0274              * @see Zend_Mail_Protocol_Exception
0275              */
0276             // require_once 'Zend/Mail/Protocol/Exception.php';
0277             throw new Zend_Mail_Protocol_Exception($errorStr);
0278         }
0279 
0280         if (($result = $this->_setStreamTimeout(self::TIMEOUT_CONNECTION)) === false) {
0281             /**
0282              * @see Zend_Mail_Protocol_Exception
0283              */
0284             // require_once 'Zend/Mail/Protocol/Exception.php';
0285             throw new Zend_Mail_Protocol_Exception('Could not set stream timeout');
0286         }
0287 
0288         return $result;
0289     }
0290 
0291 
0292     /**
0293      * Disconnect from remote host and free resource
0294      *
0295      * @return void
0296      */
0297     protected function _disconnect()
0298     {
0299         if (is_resource($this->_socket)) {
0300             fclose($this->_socket);
0301         }
0302     }
0303 
0304 
0305     /**
0306      * Send the given request followed by a LINEEND to the server.
0307      *
0308      * @param  string $request
0309      * @throws Zend_Mail_Protocol_Exception
0310      * @return integer|boolean Number of bytes written to remote host
0311      */
0312     protected function _send($request)
0313     {
0314         if (!is_resource($this->_socket)) {
0315             /**
0316              * @see Zend_Mail_Protocol_Exception
0317              */
0318             // require_once 'Zend/Mail/Protocol/Exception.php';
0319             throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host);
0320         }
0321 
0322         $this->_request = $request;
0323 
0324         $result = fwrite($this->_socket, $request . self::EOL);
0325 
0326         // Save request to internal log
0327         $this->_addLog($request . self::EOL);
0328 
0329         if ($result === false) {
0330             /**
0331              * @see Zend_Mail_Protocol_Exception
0332              */
0333             // require_once 'Zend/Mail/Protocol/Exception.php';
0334             throw new Zend_Mail_Protocol_Exception('Could not send request to ' . $this->_host);
0335         }
0336 
0337         return $result;
0338     }
0339 
0340 
0341     /**
0342      * Get a line from the stream.
0343      *
0344      * @var    integer $timeout Per-request timeout value if applicable
0345      * @throws Zend_Mail_Protocol_Exception
0346      * @return string
0347      */
0348     protected function _receive($timeout = null)
0349     {
0350         if (!is_resource($this->_socket)) {
0351             /**
0352              * @see Zend_Mail_Protocol_Exception
0353              */
0354             // require_once 'Zend/Mail/Protocol/Exception.php';
0355             throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host);
0356         }
0357 
0358         // Adapters may wish to supply per-commend timeouts according to appropriate RFC
0359         if ($timeout !== null) {
0360             $this->_setStreamTimeout($timeout);
0361         }
0362 
0363         // Retrieve response
0364         $reponse = fgets($this->_socket, 1024);
0365 
0366         // Save request to internal log
0367         $this->_addLog($reponse);
0368 
0369         // Check meta data to ensure connection is still valid
0370         $info = stream_get_meta_data($this->_socket);
0371 
0372         if (!empty($info['timed_out'])) {
0373             /**
0374              * @see Zend_Mail_Protocol_Exception
0375              */
0376             // require_once 'Zend/Mail/Protocol/Exception.php';
0377             throw new Zend_Mail_Protocol_Exception($this->_host . ' has timed out');
0378         }
0379 
0380         if ($reponse === false) {
0381             /**
0382              * @see Zend_Mail_Protocol_Exception
0383              */
0384             // require_once 'Zend/Mail/Protocol/Exception.php';
0385             throw new Zend_Mail_Protocol_Exception('Could not read from ' . $this->_host);
0386         }
0387 
0388         return $reponse;
0389     }
0390 
0391 
0392     /**
0393      * Parse server response for successful codes
0394      *
0395      * Read the response from the stream and check for expected return code.
0396      * Throws a Zend_Mail_Protocol_Exception if an unexpected code is returned.
0397      *
0398      * @param  string|array $code One or more codes that indicate a successful response
0399      * @throws Zend_Mail_Protocol_Exception
0400      * @return string Last line of response string
0401      */
0402     protected function _expect($code, $timeout = null)
0403     {
0404         $this->_response = array();
0405         $cmd  = '';
0406         $more = '';
0407         $msg  = '';
0408         $errMsg = '';
0409 
0410         if (!is_array($code)) {
0411             $code = array($code);
0412         }
0413 
0414         do {
0415             $this->_response[] = $result = $this->_receive($timeout);
0416             list($cmd, $more, $msg) = preg_split('/([\s-]+)/', $result, 2, PREG_SPLIT_DELIM_CAPTURE);
0417 
0418             if ($errMsg !== '') {
0419                 $errMsg .= ' ' . $msg;
0420             } elseif ($cmd === null || !in_array($cmd, $code)) {
0421                 $errMsg =  $msg;
0422             }
0423 
0424         } while (strpos($more, '-') === 0); // The '-' message prefix indicates an information string instead of a response string.
0425 
0426         if ($errMsg !== '') {
0427             /**
0428              * @see Zend_Mail_Protocol_Exception
0429              */
0430             // require_once 'Zend/Mail/Protocol/Exception.php';
0431             throw new Zend_Mail_Protocol_Exception($errMsg, $cmd);
0432         }
0433 
0434         return $msg;
0435     }
0436 
0437     /**
0438      * Set stream timeout
0439      *
0440      * @param integer $timeout
0441      * @return boolean
0442      */
0443     protected function _setStreamTimeout($timeout)
0444     {
0445        return stream_set_timeout($this->_socket, $timeout);
0446     }
0447 }