web/lib/Zend/Mail/Protocol/Pop3.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Mail
       
    17  * @subpackage Protocol
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: Pop3.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    21  */
       
    22 
       
    23 
       
    24 /**
       
    25  * @category   Zend
       
    26  * @package    Zend_Mail
       
    27  * @subpackage Protocol
       
    28  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    29  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    30  */
       
    31 class Zend_Mail_Protocol_Pop3
       
    32 {
       
    33     /**
       
    34      * Default timeout in seconds for initiating session
       
    35      */
       
    36     const TIMEOUT_CONNECTION = 30;
       
    37 
       
    38     /**
       
    39      * saves if server supports top
       
    40      * @var null|bool
       
    41      */
       
    42     public $hasTop = null;
       
    43 
       
    44     /**
       
    45      * socket to pop3
       
    46      * @var null|resource
       
    47      */
       
    48     protected $_socket;
       
    49 
       
    50     /**
       
    51      * greeting timestamp for apop
       
    52      * @var null|string
       
    53      */
       
    54     protected $_timestamp;
       
    55 
       
    56 
       
    57     /**
       
    58      * Public constructor
       
    59      *
       
    60      * @param  string      $host  hostname or IP address of POP3 server, if given connect() is called
       
    61      * @param  int|null    $port  port of POP3 server, null for default (110 or 995 for ssl)
       
    62      * @param  bool|string $ssl   use ssl? 'SSL', 'TLS' or false
       
    63      * @throws Zend_Mail_Protocol_Exception
       
    64      */
       
    65     public function __construct($host = '', $port = null, $ssl = false)
       
    66     {
       
    67         if ($host) {
       
    68             $this->connect($host, $port, $ssl);
       
    69         }
       
    70     }
       
    71 
       
    72 
       
    73     /**
       
    74      * Public destructor
       
    75      */
       
    76     public function __destruct()
       
    77     {
       
    78         $this->logout();
       
    79     }
       
    80 
       
    81 
       
    82     /**
       
    83      * Open connection to POP3 server
       
    84      *
       
    85      * @param  string      $host  hostname or IP address of POP3 server
       
    86      * @param  int|null    $port  of POP3 server, default is 110 (995 for ssl)
       
    87      * @param  string|bool $ssl   use 'SSL', 'TLS' or false
       
    88      * @return string welcome message
       
    89      * @throws Zend_Mail_Protocol_Exception
       
    90      */
       
    91     public function connect($host, $port = null, $ssl = false)
       
    92     {
       
    93         if ($ssl == 'SSL') {
       
    94             $host = 'ssl://' . $host;
       
    95         }
       
    96 
       
    97         if ($port === null) {
       
    98             $port = $ssl == 'SSL' ? 995 : 110;
       
    99         }
       
   100 
       
   101         $errno  =  0;
       
   102         $errstr = '';
       
   103         $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
       
   104         if (!$this->_socket) {
       
   105             /**
       
   106              * @see Zend_Mail_Protocol_Exception
       
   107              */
       
   108             require_once 'Zend/Mail/Protocol/Exception.php';
       
   109             throw new Zend_Mail_Protocol_Exception('cannot connect to host; error = ' . $errstr .
       
   110                                                    ' (errno = ' . $errno . ' )');
       
   111         }
       
   112 
       
   113         $welcome = $this->readResponse();
       
   114 
       
   115         strtok($welcome, '<');
       
   116         $this->_timestamp = strtok('>');
       
   117         if (!strpos($this->_timestamp, '@')) {
       
   118             $this->_timestamp = null;
       
   119         } else {
       
   120             $this->_timestamp = '<' . $this->_timestamp . '>';
       
   121         }
       
   122 
       
   123         if ($ssl === 'TLS') {
       
   124             $this->request('STLS');
       
   125             $result = stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
       
   126             if (!$result) {
       
   127                 /**
       
   128                  * @see Zend_Mail_Protocol_Exception
       
   129                  */
       
   130                 require_once 'Zend/Mail/Protocol/Exception.php';
       
   131                 throw new Zend_Mail_Protocol_Exception('cannot enable TLS');
       
   132             }
       
   133         }
       
   134 
       
   135         return $welcome;
       
   136     }
       
   137 
       
   138 
       
   139     /**
       
   140      * Send a request
       
   141      *
       
   142      * @param string $request your request without newline
       
   143      * @return null
       
   144      * @throws Zend_Mail_Protocol_Exception
       
   145      */
       
   146     public function sendRequest($request)
       
   147     {
       
   148         $result = @fputs($this->_socket, $request . "\r\n");
       
   149         if (!$result) {
       
   150             /**
       
   151              * @see Zend_Mail_Protocol_Exception
       
   152              */
       
   153             require_once 'Zend/Mail/Protocol/Exception.php';
       
   154             throw new Zend_Mail_Protocol_Exception('send failed - connection closed?');
       
   155         }
       
   156     }
       
   157 
       
   158 
       
   159     /**
       
   160      * read a response
       
   161      *
       
   162      * @param  boolean $multiline response has multiple lines and should be read until "<nl>.<nl>"
       
   163      * @return string response
       
   164      * @throws Zend_Mail_Protocol_Exception
       
   165      */
       
   166     public function readResponse($multiline = false)
       
   167     {
       
   168         $result = @fgets($this->_socket);
       
   169         if (!is_string($result)) {
       
   170             /**
       
   171              * @see Zend_Mail_Protocol_Exception
       
   172              */
       
   173             require_once 'Zend/Mail/Protocol/Exception.php';
       
   174             throw new Zend_Mail_Protocol_Exception('read failed - connection closed?');
       
   175         }
       
   176 
       
   177         $result = trim($result);
       
   178         if (strpos($result, ' ')) {
       
   179             list($status, $message) = explode(' ', $result, 2);
       
   180         } else {
       
   181             $status = $result;
       
   182             $message = '';
       
   183         }
       
   184 
       
   185         if ($status != '+OK') {
       
   186             /**
       
   187              * @see Zend_Mail_Protocol_Exception
       
   188              */
       
   189             require_once 'Zend/Mail/Protocol/Exception.php';
       
   190             throw new Zend_Mail_Protocol_Exception('last request failed');
       
   191         }
       
   192 
       
   193         if ($multiline) {
       
   194             $message = '';
       
   195             $line = fgets($this->_socket);
       
   196             while ($line && rtrim($line, "\r\n") != '.') {
       
   197                 if ($line[0] == '.') {
       
   198                     $line = substr($line, 1);
       
   199                 }
       
   200                 $message .= $line;
       
   201                 $line = fgets($this->_socket);
       
   202             };
       
   203         }
       
   204 
       
   205         return $message;
       
   206     }
       
   207 
       
   208 
       
   209     /**
       
   210      * Send request and get resposne
       
   211      *
       
   212      * @see sendRequest(), readResponse()
       
   213      *
       
   214      * @param  string $request    request
       
   215      * @param  bool   $multiline  multiline response?
       
   216      * @return string             result from readResponse()
       
   217      * @throws Zend_Mail_Protocol_Exception
       
   218      */
       
   219     public function request($request, $multiline = false)
       
   220     {
       
   221         $this->sendRequest($request);
       
   222         return $this->readResponse($multiline);
       
   223     }
       
   224 
       
   225 
       
   226     /**
       
   227      * End communication with POP3 server (also closes socket)
       
   228      *
       
   229      * @return null
       
   230      */
       
   231     public function logout()
       
   232     {
       
   233         if (!$this->_socket) {
       
   234             return;
       
   235         }
       
   236 
       
   237         try {
       
   238             $this->request('QUIT');
       
   239         } catch (Zend_Mail_Protocol_Exception $e) {
       
   240             // ignore error - we're closing the socket anyway
       
   241         }
       
   242 
       
   243         fclose($this->_socket);
       
   244         $this->_socket = null;
       
   245     }
       
   246 
       
   247 
       
   248     /**
       
   249      * Get capabilities from POP3 server
       
   250      *
       
   251      * @return array list of capabilities
       
   252      * @throws Zend_Mail_Protocol_Exception
       
   253      */
       
   254     public function capa()
       
   255     {
       
   256         $result = $this->request('CAPA', true);
       
   257         return explode("\n", $result);
       
   258     }
       
   259 
       
   260 
       
   261     /**
       
   262      * Login to POP3 server. Can use APOP
       
   263      *
       
   264      * @param  string $user      username
       
   265      * @param  string $password  password
       
   266      * @param  bool   $try_apop  should APOP be tried?
       
   267      * @return void
       
   268      * @throws Zend_Mail_Protocol_Exception
       
   269      */
       
   270     public function login($user, $password, $tryApop = true)
       
   271     {
       
   272         if ($tryApop && $this->_timestamp) {
       
   273             try {
       
   274                 $this->request("APOP $user " . md5($this->_timestamp . $password));
       
   275                 return;
       
   276             } catch (Zend_Mail_Protocol_Exception $e) {
       
   277                 // ignore
       
   278             }
       
   279         }
       
   280 
       
   281         $result = $this->request("USER $user");
       
   282         $result = $this->request("PASS $password");
       
   283     }
       
   284 
       
   285 
       
   286     /**
       
   287      * Make STAT call for message count and size sum
       
   288      *
       
   289      * @param  int $messages  out parameter with count of messages
       
   290      * @param  int $octets    out parameter with size in octects of messages
       
   291      * @return void
       
   292      * @throws Zend_Mail_Protocol_Exception
       
   293      */
       
   294     public function status(&$messages, &$octets)
       
   295     {
       
   296         $messages = 0;
       
   297         $octets = 0;
       
   298         $result = $this->request('STAT');
       
   299 
       
   300         list($messages, $octets) = explode(' ', $result);
       
   301     }
       
   302 
       
   303 
       
   304     /**
       
   305      * Make LIST call for size of message(s)
       
   306      *
       
   307      * @param  int|null $msgno number of message, null for all
       
   308      * @return int|array size of given message or list with array(num => size)
       
   309      * @throws Zend_Mail_Protocol_Exception
       
   310      */
       
   311     public function getList($msgno = null)
       
   312     {
       
   313         if ($msgno !== null) {
       
   314             $result = $this->request("LIST $msgno");
       
   315 
       
   316             list(, $result) = explode(' ', $result);
       
   317             return (int)$result;
       
   318         }
       
   319 
       
   320         $result = $this->request('LIST', true);
       
   321         $messages = array();
       
   322         $line = strtok($result, "\n");
       
   323         while ($line) {
       
   324             list($no, $size) = explode(' ', trim($line));
       
   325             $messages[(int)$no] = (int)$size;
       
   326             $line = strtok("\n");
       
   327         }
       
   328 
       
   329         return $messages;
       
   330     }
       
   331 
       
   332 
       
   333     /**
       
   334      * Make UIDL call for getting a uniqueid
       
   335      *
       
   336      * @param  int|null $msgno number of message, null for all
       
   337      * @return string|array uniqueid of message or list with array(num => uniqueid)
       
   338      * @throws Zend_Mail_Protocol_Exception
       
   339      */
       
   340     public function uniqueid($msgno = null)
       
   341     {
       
   342         if ($msgno !== null) {
       
   343             $result = $this->request("UIDL $msgno");
       
   344 
       
   345             list(, $result) = explode(' ', $result);
       
   346             return $result;
       
   347         }
       
   348 
       
   349         $result = $this->request('UIDL', true);
       
   350 
       
   351         $result = explode("\n", $result);
       
   352         $messages = array();
       
   353         foreach ($result as $line) {
       
   354             if (!$line) {
       
   355                 continue;
       
   356             }
       
   357             list($no, $id) = explode(' ', trim($line), 2);
       
   358             $messages[(int)$no] = $id;
       
   359         }
       
   360 
       
   361         return $messages;
       
   362 
       
   363     }
       
   364 
       
   365 
       
   366     /**
       
   367      * Make TOP call for getting headers and maybe some body lines
       
   368      * This method also sets hasTop - before it it's not known if top is supported
       
   369      *
       
   370      * The fallback makes normale RETR call, which retrieves the whole message. Additional
       
   371      * lines are not removed.
       
   372      *
       
   373      * @param  int  $msgno    number of message
       
   374      * @param  int  $lines    number of wanted body lines (empty line is inserted after header lines)
       
   375      * @param  bool $fallback fallback with full retrieve if top is not supported
       
   376      * @return string message headers with wanted body lines
       
   377      * @throws Zend_Mail_Protocol_Exception
       
   378      */
       
   379     public function top($msgno, $lines = 0, $fallback = false)
       
   380     {
       
   381         if ($this->hasTop === false) {
       
   382             if ($fallback) {
       
   383                 return $this->retrieve($msgno);
       
   384             } else {
       
   385                 /**
       
   386                  * @see Zend_Mail_Protocol_Exception
       
   387                  */
       
   388                 require_once 'Zend/Mail/Protocol/Exception.php';
       
   389                 throw new Zend_Mail_Protocol_Exception('top not supported and no fallback wanted');
       
   390             }
       
   391         }
       
   392         $this->hasTop = true;
       
   393 
       
   394         $lines = (!$lines || $lines < 1) ? 0 : (int)$lines;
       
   395 
       
   396         try {
       
   397             $result = $this->request("TOP $msgno $lines", true);
       
   398         } catch (Zend_Mail_Protocol_Exception $e) {
       
   399             $this->hasTop = false;
       
   400             if ($fallback) {
       
   401                 $result = $this->retrieve($msgno);
       
   402             } else {
       
   403                 throw $e;
       
   404             }
       
   405         }
       
   406 
       
   407         return $result;
       
   408     }
       
   409 
       
   410 
       
   411     /**
       
   412      * Make a RETR call for retrieving a full message with headers and body
       
   413      *
       
   414      * @deprecated since 1.1.0; this method has a typo - please use retrieve()
       
   415      * @param  int $msgno  message number
       
   416      * @return string message
       
   417      * @throws Zend_Mail_Protocol_Exception
       
   418      */
       
   419     public function retrive($msgno)
       
   420     {
       
   421         return $this->retrieve($msgno);
       
   422     }
       
   423 
       
   424 
       
   425     /**
       
   426      * Make a RETR call for retrieving a full message with headers and body
       
   427      *
       
   428      * @param  int $msgno  message number
       
   429      * @return string message
       
   430      * @throws Zend_Mail_Protocol_Exception
       
   431      */
       
   432     public function retrieve($msgno)
       
   433     {
       
   434         $result = $this->request("RETR $msgno", true);
       
   435         return $result;
       
   436     }
       
   437 
       
   438     /**
       
   439      * Make a NOOP call, maybe needed for keeping the server happy
       
   440      *
       
   441      * @return null
       
   442      * @throws Zend_Mail_Protocol_Exception
       
   443      */
       
   444     public function noop()
       
   445     {
       
   446         $this->request('NOOP');
       
   447     }
       
   448 
       
   449 
       
   450     /**
       
   451      * Make a DELE count to remove a message
       
   452      *
       
   453      * @return null
       
   454      * @throws Zend_Mail_Protocol_Exception
       
   455      */
       
   456     public function delete($msgno)
       
   457     {
       
   458         $this->request("DELE $msgno");
       
   459     }
       
   460 
       
   461 
       
   462     /**
       
   463      * Make RSET call, which rollbacks delete requests
       
   464      *
       
   465      * @return null
       
   466      * @throws Zend_Mail_Protocol_Exception
       
   467      */
       
   468     public function undelete()
       
   469     {
       
   470         $this->request('RSET');
       
   471     }
       
   472 }