web/lib/Zend/Mail/Protocol/Abstract.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     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-2010 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 22602 2010-07-16 22:37:31Z freak $
       
    22  */
       
    23 
       
    24 
       
    25 /**
       
    26  * @see Zend_Validate
       
    27  */
       
    28 require_once 'Zend/Validate.php';
       
    29 
       
    30 
       
    31 /**
       
    32  * @see Zend_Validate_Hostname
       
    33  */
       
    34 require_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-2010 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 22602 2010-07-16 22:37:31Z freak $
       
    48  * @todo Implement proxy settings
       
    49  */
       
    50 abstract 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 = stream_set_timeout($this->_socket, 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            stream_set_timeout($this->_socket, $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);
       
   432         }
       
   433 
       
   434         return $msg;
       
   435     }
       
   436 }