File indexing completed on 2025-03-09 05:26:01
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 }