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.
18 * @subpackage Protocol
19 * @copyright Copyright (c) 2005-2009 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 16219 2009-06-21 19:45:39Z thomas $
28 require_once 'Zend/Validate.php';
32 * @see Zend_Validate_Hostname
34 require_once 'Zend/Validate/Hostname.php';
38 * Zend_Mail_Protocol_Abstract
40 * Provides low-level methods for concrete adapters to communicate with a remote mail server and track requests and responses.
44 * @subpackage Protocol
45 * @copyright Copyright (c) 2005-2009 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 16219 2009-06-21 19:45:39Z thomas $
48 * @todo Implement proxy settings
50 abstract class Zend_Mail_Protocol_Abstract
53 * Mail default EOL string
59 * Default timeout in seconds for initiating session
61 const TIMEOUT_CONNECTION
= 30;
65 * Hostname or IP address of remote server
72 * Port number of connection
79 * Instance of Zend_Validate to check hostnames
82 protected $_validHost;
86 * Socket connection resource
93 * Last request sent to server
100 * Array of server responses to last request
103 protected $_response;
107 * String template for parsing server responses using sscanf (default: 3 digit code and response string)
110 protected $_template = '%d%s';
114 * Log of mail requests and server responses for a session
123 * @param string $host OPTIONAL Hostname of remote connection (default: 127.0.0.1)
124 * @param integer $port OPTIONAL Port number (default: null)
125 * @throws Zend_Mail_Protocol_Exception
128 public function __construct($host = '127.0.0.1', $port = null)
130 $this->_validHost
= new Zend_Validate();
131 $this->_validHost
->addValidator(new Zend_Validate_Hostname(Zend_Validate_Hostname
::ALLOW_ALL
));
133 if (!$this->_validHost
->isValid($host)) {
135 * @see Zend_Mail_Protocol_Exception
137 require_once 'Zend/Mail/Protocol/Exception.php';
138 throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost
->getMessages()));
141 $this->_host
= $host;
142 $this->_port
= $port;
147 * Class destructor to cleanup open resources
151 public function __destruct()
153 $this->_disconnect();
158 * Create a connection to the remote host
160 * Concrete adapters for this class will implement their own unique connect scripts, using the _connect() method to create the socket resource.
162 abstract public function connect();
166 * Retrieve the last client request
170 public function getRequest()
172 return $this->_request
;
177 * Retrieve the last server response
181 public function getResponse()
183 return $this->_response
;
188 * Retrieve the transaction log
192 public function getLog()
199 * Reset the transaction log
203 public function resetLog()
210 * Connect to the server using the supplied transport and target
212 * An example $remote string may be 'tcp://mail.example.com:25' or 'ssh://hostname.com:2222'
214 * @param string $remote Remote
215 * @throws Zend_Mail_Protocol_Exception
218 protected function _connect($remote)
224 $this->_socket
= @stream_socket_client
($remote, $errorNum, $errorStr, self
::TIMEOUT_CONNECTION
);
226 if ($this->_socket
=== false) {
227 if ($errorNum == 0) {
228 $errorStr = 'Could not open socket';
231 * @see Zend_Mail_Protocol_Exception
233 require_once 'Zend/Mail/Protocol/Exception.php';
234 throw new Zend_Mail_Protocol_Exception($errorStr);
237 if (($result = stream_set_timeout($this->_socket
, self
::TIMEOUT_CONNECTION
)) === false) {
239 * @see Zend_Mail_Protocol_Exception
241 require_once 'Zend/Mail/Protocol/Exception.php';
242 throw new Zend_Mail_Protocol_Exception('Could not set stream timeout');
250 * Disconnect from remote host and free resource
254 protected function _disconnect()
256 if (is_resource($this->_socket
)) {
257 fclose($this->_socket
);
263 * Send the given request followed by a LINEEND to the server.
265 * @param string $request
266 * @throws Zend_Mail_Protocol_Exception
267 * @return integer|boolean Number of bytes written to remote host
269 protected function _send($request)
271 if (!is_resource($this->_socket
)) {
273 * @see Zend_Mail_Protocol_Exception
275 require_once 'Zend/Mail/Protocol/Exception.php';
276 throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host
);
279 $this->_request
= $request;
281 $result = fwrite($this->_socket
, $request . self
::EOL
);
283 // Save request to internal log
284 $this->_log
.= $request . self
::EOL
;
286 if ($result === false) {
288 * @see Zend_Mail_Protocol_Exception
290 require_once 'Zend/Mail/Protocol/Exception.php';
291 throw new Zend_Mail_Protocol_Exception('Could not send request to ' . $this->_host
);
299 * Get a line from the stream.
301 * @var integer $timeout Per-request timeout value if applicable
302 * @throws Zend_Mail_Protocol_Exception
305 protected function _receive($timeout = null)
307 if (!is_resource($this->_socket
)) {
309 * @see Zend_Mail_Protocol_Exception
311 require_once 'Zend/Mail/Protocol/Exception.php';
312 throw new Zend_Mail_Protocol_Exception('No connection has been established to ' . $this->_host
);
315 // Adapters may wish to supply per-commend timeouts according to appropriate RFC
316 if ($timeout !== null) {
317 stream_set_timeout($this->_socket
, $timeout);
321 $reponse = fgets($this->_socket
, 1024);
323 // Save request to internal log
324 $this->_log
.= $reponse;
326 // Check meta data to ensure connection is still valid
327 $info = stream_get_meta_data($this->_socket
);
329 if (!empty($info['timed_out'])) {
331 * @see Zend_Mail_Protocol_Exception
333 require_once 'Zend/Mail/Protocol/Exception.php';
334 throw new Zend_Mail_Protocol_Exception($this->_host
. ' has timed out');
337 if ($reponse === false) {
339 * @see Zend_Mail_Protocol_Exception
341 require_once 'Zend/Mail/Protocol/Exception.php';
342 throw new Zend_Mail_Protocol_Exception('Could not read from ' . $this->_host
);
350 * Parse server response for successful codes
352 * Read the response from the stream and check for expected return code.
353 * Throws a Zend_Mail_Protocol_Exception if an unexpected code is returned.
355 * @param string|array $code One or more codes that indicate a successful response
356 * @throws Zend_Mail_Protocol_Exception
357 * @return string Last line of response string
359 protected function _expect($code, $timeout = null)
361 $this->_response
= array();
365 if (!is_array($code)) {
366 $code = array($code);
370 $this->_response
[] = $result = $this->_receive($timeout);
371 sscanf($result, $this->_template
, $cmd, $msg);
373 if ($cmd === null ||
!in_array($cmd, $code)) {
375 * @see Zend_Mail_Protocol_Exception
377 require_once 'Zend/Mail/Protocol/Exception.php';
378 throw new Zend_Mail_Protocol_Exception($result);
381 } while (strpos($msg, '-') === 0); // The '-' message prefix indicates an information string instead of a response string.