/zf/library/Zend/Mail/Protocol/Abstract.php
PHP | 447 lines | 154 code | 79 blank | 214 comment | 19 complexity | 90982c14e180fb1d9d48ffa08bc7cea3 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
1<?php 2 3/** 4 * Zend Framework 5 * 6 * LICENSE 7 * 8 * This source file is subject to the new BSD license that is bundled 9 * with this package in the file LICENSE.txt. 10 * It is also available through the world-wide-web at this URL: 11 * http://framework.zend.com/license/new-bsd 12 * If you did not receive a copy of the license and are unable to 13 * obtain it through the world-wide-web, please send an email 14 * to license@zend.com so we can send you a copy immediately. 15 * 16 * @category Zend 17 * @package Zend_Mail 18 * @subpackage Protocol 19 * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com) 20 * @license http://framework.zend.com/license/new-bsd New BSD License 21 * @version $Id: Abstract.php 23775 2011-03-01 17:25:24Z ralph $ 22 */ 23 24 25/** 26 * @see Zend_Validate 27 */ 28require_once 'Zend/Validate.php'; 29 30 31/** 32 * @see Zend_Validate_Hostname 33 */ 34require_once 'Zend/Validate/Hostname.php'; 35 36 37/** 38 * Zend_Mail_Protocol_Abstract 39 * 40 * Provides low-level methods for concrete adapters to communicate with a remote mail server and track requests and responses. 41 * 42 * @category Zend 43 * @package Zend_Mail 44 * @subpackage Protocol 45 * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com) 46 * @license http://framework.zend.com/license/new-bsd New BSD License 47 * @version $Id: Abstract.php 23775 2011-03-01 17:25:24Z ralph $ 48 * @todo Implement proxy settings 49 */ 50abstract class Zend_Mail_Protocol_Abstract 51{ 52 /** 53 * Mail default EOL string 54 */ 55 const EOL = "\r\n"; 56 57 58 /** 59 * Default timeout in seconds for initiating session 60 */ 61 const TIMEOUT_CONNECTION = 30; 62 63 /** 64 * Maximum of the transaction log 65 * @var integer 66 */ 67 protected $_maximumLog = 64; 68 69 70 /** 71 * Hostname or IP address of remote server 72 * @var string 73 */ 74 protected $_host; 75 76 77 /** 78 * Port number of connection 79 * @var integer 80 */ 81 protected $_port; 82 83 84 /** 85 * Instance of Zend_Validate to check hostnames 86 * @var Zend_Validate 87 */ 88 protected $_validHost; 89 90 91 /** 92 * Socket connection resource 93 * @var resource 94 */ 95 protected $_socket; 96 97 98 /** 99 * Last request sent to server 100 * @var string 101 */ 102 protected $_request; 103 104 105 /** 106 * Array of server responses to last request 107 * @var array 108 */ 109 protected $_response; 110 111 112 /** 113 * String template for parsing server responses using sscanf (default: 3 digit code and response string) 114 * @var resource 115 * @deprecated Since 1.10.3 116 */ 117 protected $_template = '%d%s'; 118 119 120 /** 121 * Log of mail requests and server responses for a session 122 * @var array 123 */ 124 private $_log = array(); 125 126 127 /** 128 * Constructor. 129 * 130 * @param string $host OPTIONAL Hostname of remote connection (default: 127.0.0.1) 131 * @param integer $port OPTIONAL Port number (default: null) 132 * @throws Zend_Mail_Protocol_Exception 133 * @return void 134 */ 135 public function __construct($host = '127.0.0.1', $port = null) 136 { 137 $this->_validHost = new Zend_Validate(); 138 $this->_validHost->addValidator(new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL)); 139 140 if (!$this->_validHost->isValid($host)) { 141 /** 142 * @see Zend_Mail_Protocol_Exception 143 */ 144 require_once 'Zend/Mail/Protocol/Exception.php'; 145 throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost->getMessages())); 146 } 147 148 $this->_host = $host; 149 $this->_port = $port; 150 } 151 152 153 /** 154 * Class destructor to cleanup open resources 155 * 156 * @return void 157 */ 158 public function __destruct() 159 { 160 $this->_disconnect(); 161 } 162 163 /** 164 * Set the maximum log size 165 * 166 * @param integer $maximumLog Maximum log size 167 * @return void 168 */ 169 public function setMaximumLog($maximumLog) 170 { 171 $this->_maximumLog = (int) $maximumLog; 172 } 173 174 175 /** 176 * Get the maximum log size 177 * 178 * @return int the maximum log size 179 */ 180 public function getMaximumLog() 181 { 182 return $this->_maximumLog; 183 } 184 185 186 /** 187 * Create a connection to the remote host 188 * 189 * Concrete adapters for this class will implement their own unique connect scripts, using the _connect() method to create the socket resource. 190 */ 191 abstract public function connect(); 192 193 194 /** 195 * Retrieve the last client request 196 * 197 * @return string 198 */ 199 public function getRequest() 200 { 201 return $this->_request; 202 } 203 204 205 /** 206 * Retrieve the last server response 207 * 208 * @return array 209 */ 210 public function getResponse() 211 { 212 return $this->_response; 213 } 214 215 216 /** 217 * Retrieve the transaction log 218 * 219 * @return string 220 */ 221 public function getLog() 222 { 223 return implode('', $this->_log); 224 } 225 226 227 /** 228 * Reset the transaction log 229 * 230 * @return void 231 */ 232 public function resetLog() 233 { 234 $this->_log = array(); 235 } 236 237 /** 238 * Add the transaction log 239 * 240 * @param string new transaction 241 * @return void 242 */ 243 protected function _addLog($value) 244 { 245 if ($this->_maximumLog >= 0 && count($this->_log) >= $this->_maximumLog) { 246 array_shift($this->_log); 247 } 248 249 $this->_log[] = $value; 250 } 251 252 /** 253 * Connect to the server using the supplied transport and target 254 * 255 * An example $remote string may be 'tcp://mail.example.com:25' or 'ssh://hostname.com:2222' 256 * 257 * @param string $remote Remote 258 * @throws Zend_Mail_Protocol_Exception 259 * @return boolean 260 */ 261 protected function _connect($remote) 262 { 263 $errorNum = 0; 264 $errorStr = ''; 265 266 // open connection 267 $this->_socket = @stream_socket_client($remote, $errorNum, $errorStr, self::TIMEOUT_CONNECTION); 268 269 if ($this->_socket === false) { 270 if ($errorNum == 0) { 271 $errorStr = 'Could not open socket'; 272 } 273 /** 274 * @see Zend_Mail_Protocol_Exception 275 */ 276 require_once 'Zend/Mail/Protocol/Exception.php'; 277 throw new Zend_Mail_Protocol_Exception($errorStr); 278 } 279 280 if (($result = $this->_setStreamTimeout(self::TIMEOUT_CONNECTION)) === false) { 281 /** 282 * @see Zend_Mail_Protocol_Exception 283 */ 284 require_once 'Zend/Mail/Protocol/Exception.php'; 285 throw new Zend_Mail_Protocol_Exception('Could not set stream timeout'); 286 } 287 288 return $result; 289 } 290 291 292 /** 293 * Disconnect from remote host and free resource 294 * 295 * @return void 296 */ 297 protected function _disconnect() 298 { 299 if (is_resource($this->_socket)) { 300 fclose($this->_socket); 301 } 302 } 303 304 305 /** 306 * Send the given request followed by a LINEEND to the server. 307 * 308 * @param string $request 309 * @throws Zend_Mail_Protocol_Exception 310 * @return integer|boolean Number of bytes written to remote host 311 */ 312 protected function _send($request) 313 { 314 if (!is_resource($this->_socket)) { 315 /** 316 * @see Zend_Mail_Protocol_Exception 317 */ 318 require_once 'Zend/Mail/Protocol/Exception.php'; 319 throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host); 320 } 321 322 $this->_request = $request; 323 324 $result = fwrite($this->_socket, $request . self::EOL); 325 326 // Save request to internal log 327 $this->_addLog($request . self::EOL); 328 329 if ($result === false) { 330 /** 331 * @see Zend_Mail_Protocol_Exception 332 */ 333 require_once 'Zend/Mail/Protocol/Exception.php'; 334 throw new Zend_Mail_Protocol_Exception('Could not send request to ' . $this->_host); 335 } 336 337 return $result; 338 } 339 340 341 /** 342 * Get a line from the stream. 343 * 344 * @var integer $timeout Per-request timeout value if applicable 345 * @throws Zend_Mail_Protocol_Exception 346 * @return string 347 */ 348 protected function _receive($timeout = null) 349 { 350 if (!is_resource($this->_socket)) { 351 /** 352 * @see Zend_Mail_Protocol_Exception 353 */ 354 require_once 'Zend/Mail/Protocol/Exception.php'; 355 throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host); 356 } 357 358 // Adapters may wish to supply per-commend timeouts according to appropriate RFC 359 if ($timeout !== null) { 360 $this->_setStreamTimeout($timeout); 361 } 362 363 // Retrieve response 364 $reponse = fgets($this->_socket, 1024); 365 366 // Save request to internal log 367 $this->_addLog($reponse); 368 369 // Check meta data to ensure connection is still valid 370 $info = stream_get_meta_data($this->_socket); 371 372 if (!empty($info['timed_out'])) { 373 /** 374 * @see Zend_Mail_Protocol_Exception 375 */ 376 require_once 'Zend/Mail/Protocol/Exception.php'; 377 throw new Zend_Mail_Protocol_Exception($this->_host . ' has timed out'); 378 } 379 380 if ($reponse === false) { 381 /** 382 * @see Zend_Mail_Protocol_Exception 383 */ 384 require_once 'Zend/Mail/Protocol/Exception.php'; 385 throw new Zend_Mail_Protocol_Exception('Could not read from ' . $this->_host); 386 } 387 388 return $reponse; 389 } 390 391 392 /** 393 * Parse server response for successful codes 394 * 395 * Read the response from the stream and check for expected return code. 396 * Throws a Zend_Mail_Protocol_Exception if an unexpected code is returned. 397 * 398 * @param string|array $code One or more codes that indicate a successful response 399 * @throws Zend_Mail_Protocol_Exception 400 * @return string Last line of response string 401 */ 402 protected function _expect($code, $timeout = null) 403 { 404 $this->_response = array(); 405 $cmd = ''; 406 $more = ''; 407 $msg = ''; 408 $errMsg = ''; 409 410 if (!is_array($code)) { 411 $code = array($code); 412 } 413 414 do { 415 $this->_response[] = $result = $this->_receive($timeout); 416 list($cmd, $more, $msg) = preg_split('/([\s-]+)/', $result, 2, PREG_SPLIT_DELIM_CAPTURE); 417 418 if ($errMsg !== '') { 419 $errMsg .= ' ' . $msg; 420 } elseif ($cmd === null || !in_array($cmd, $code)) { 421 $errMsg = $msg; 422 } 423 424 } while (strpos($more, '-') === 0); // The '-' message prefix indicates an information string instead of a response string. 425 426 if ($errMsg !== '') { 427 /** 428 * @see Zend_Mail_Protocol_Exception 429 */ 430 require_once 'Zend/Mail/Protocol/Exception.php'; 431 throw new Zend_Mail_Protocol_Exception($errMsg, $cmd); 432 } 433 434 return $msg; 435 } 436 437 /** 438 * Set stream timeout 439 * 440 * @param integer $timeout 441 * @return boolean 442 */ 443 protected function _setStreamTimeout($timeout) 444 { 445 return stream_set_timeout($this->_socket, $timeout); 446 } 447}