File indexing completed on 2025-03-02 05:29:34
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Mail 0017 * @subpackage Protocol 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 0024 /** 0025 * @category Zend 0026 * @package Zend_Mail 0027 * @subpackage Protocol 0028 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0029 * @license http://framework.zend.com/license/new-bsd New BSD License 0030 */ 0031 class Zend_Mail_Protocol_Pop3 0032 { 0033 /** 0034 * Default timeout in seconds for initiating session 0035 */ 0036 const TIMEOUT_CONNECTION = 30; 0037 0038 /** 0039 * saves if server supports top 0040 * @var null|bool 0041 */ 0042 public $hasTop = null; 0043 0044 /** 0045 * socket to pop3 0046 * @var null|resource 0047 */ 0048 protected $_socket; 0049 0050 /** 0051 * greeting timestamp for apop 0052 * @var null|string 0053 */ 0054 protected $_timestamp; 0055 0056 0057 /** 0058 * Public constructor 0059 * 0060 * @param string $host hostname or IP address of POP3 server, if given connect() is called 0061 * @param int|null $port port of POP3 server, null for default (110 or 995 for ssl) 0062 * @param bool|string $ssl use ssl? 'SSL', 'TLS' or false 0063 * @throws Zend_Mail_Protocol_Exception 0064 */ 0065 public function __construct($host = '', $port = null, $ssl = false) 0066 { 0067 if ($host) { 0068 $this->connect($host, $port, $ssl); 0069 } 0070 } 0071 0072 0073 /** 0074 * Public destructor 0075 */ 0076 public function __destruct() 0077 { 0078 $this->logout(); 0079 } 0080 0081 0082 /** 0083 * Open connection to POP3 server 0084 * 0085 * @param string $host hostname or IP address of POP3 server 0086 * @param int|null $port of POP3 server, default is 110 (995 for ssl) 0087 * @param string|bool $ssl use 'SSL', 'TLS' or false 0088 * @return string welcome message 0089 * @throws Zend_Mail_Protocol_Exception 0090 */ 0091 public function connect($host, $port = null, $ssl = false) 0092 { 0093 if ($ssl == 'SSL') { 0094 $host = 'ssl://' . $host; 0095 } 0096 0097 if ($port === null) { 0098 $port = $ssl == 'SSL' ? 995 : 110; 0099 } 0100 0101 $errno = 0; 0102 $errstr = ''; 0103 $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION); 0104 if (!$this->_socket) { 0105 /** 0106 * @see Zend_Mail_Protocol_Exception 0107 */ 0108 // require_once 'Zend/Mail/Protocol/Exception.php'; 0109 throw new Zend_Mail_Protocol_Exception('cannot connect to host; error = ' . $errstr . 0110 ' (errno = ' . $errno . ' )'); 0111 } 0112 0113 $welcome = $this->readResponse(); 0114 0115 strtok($welcome, '<'); 0116 $this->_timestamp = strtok('>'); 0117 if (!strpos($this->_timestamp, '@')) { 0118 $this->_timestamp = null; 0119 } else { 0120 $this->_timestamp = '<' . $this->_timestamp . '>'; 0121 } 0122 0123 if ($ssl === 'TLS') { 0124 $this->request('STLS'); 0125 $result = stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); 0126 if (!$result) { 0127 /** 0128 * @see Zend_Mail_Protocol_Exception 0129 */ 0130 // require_once 'Zend/Mail/Protocol/Exception.php'; 0131 throw new Zend_Mail_Protocol_Exception('cannot enable TLS'); 0132 } 0133 } 0134 0135 return $welcome; 0136 } 0137 0138 0139 /** 0140 * Send a request 0141 * 0142 * @param string $request your request without newline 0143 * @return null 0144 * @throws Zend_Mail_Protocol_Exception 0145 */ 0146 public function sendRequest($request) 0147 { 0148 $result = @fputs($this->_socket, $request . "\r\n"); 0149 if (!$result) { 0150 /** 0151 * @see Zend_Mail_Protocol_Exception 0152 */ 0153 // require_once 'Zend/Mail/Protocol/Exception.php'; 0154 throw new Zend_Mail_Protocol_Exception('send failed - connection closed?'); 0155 } 0156 } 0157 0158 0159 /** 0160 * read a response 0161 * 0162 * @param boolean $multiline response has multiple lines and should be read until "<nl>.<nl>" 0163 * @return string response 0164 * @throws Zend_Mail_Protocol_Exception 0165 */ 0166 public function readResponse($multiline = false) 0167 { 0168 $result = @fgets($this->_socket); 0169 if (!is_string($result)) { 0170 /** 0171 * @see Zend_Mail_Protocol_Exception 0172 */ 0173 // require_once 'Zend/Mail/Protocol/Exception.php'; 0174 throw new Zend_Mail_Protocol_Exception('read failed - connection closed?'); 0175 } 0176 0177 $result = trim($result); 0178 if (strpos($result, ' ')) { 0179 list($status, $message) = explode(' ', $result, 2); 0180 } else { 0181 $status = $result; 0182 $message = ''; 0183 } 0184 0185 if ($status != '+OK') { 0186 /** 0187 * @see Zend_Mail_Protocol_Exception 0188 */ 0189 // require_once 'Zend/Mail/Protocol/Exception.php'; 0190 throw new Zend_Mail_Protocol_Exception('last request failed'); 0191 } 0192 0193 if ($multiline) { 0194 $message = ''; 0195 $line = fgets($this->_socket); 0196 while ($line && rtrim($line, "\r\n") != '.') { 0197 if ($line[0] == '.') { 0198 $line = substr($line, 1); 0199 } 0200 $message .= $line; 0201 $line = fgets($this->_socket); 0202 }; 0203 } 0204 0205 return $message; 0206 } 0207 0208 0209 /** 0210 * Send request and get resposne 0211 * 0212 * @see sendRequest(), readResponse() 0213 * 0214 * @param string $request request 0215 * @param bool $multiline multiline response? 0216 * @return string result from readResponse() 0217 * @throws Zend_Mail_Protocol_Exception 0218 */ 0219 public function request($request, $multiline = false) 0220 { 0221 $this->sendRequest($request); 0222 return $this->readResponse($multiline); 0223 } 0224 0225 0226 /** 0227 * End communication with POP3 server (also closes socket) 0228 * 0229 * @return null 0230 */ 0231 public function logout() 0232 { 0233 if (!$this->_socket) { 0234 return; 0235 } 0236 0237 try { 0238 $this->request('QUIT'); 0239 } catch (Zend_Mail_Protocol_Exception $e) { 0240 // ignore error - we're closing the socket anyway 0241 } 0242 0243 fclose($this->_socket); 0244 $this->_socket = null; 0245 } 0246 0247 0248 /** 0249 * Get capabilities from POP3 server 0250 * 0251 * @return array list of capabilities 0252 * @throws Zend_Mail_Protocol_Exception 0253 */ 0254 public function capa() 0255 { 0256 $result = $this->request('CAPA', true); 0257 return explode("\n", $result); 0258 } 0259 0260 0261 /** 0262 * Login to POP3 server. Can use APOP 0263 * 0264 * @param string $user username 0265 * @param string $password password 0266 * @param bool $try_apop should APOP be tried? 0267 * @return void 0268 * @throws Zend_Mail_Protocol_Exception 0269 */ 0270 public function login($user, $password, $tryApop = true) 0271 { 0272 if ($tryApop && $this->_timestamp) { 0273 try { 0274 $this->request("APOP $user " . md5($this->_timestamp . $password)); 0275 return; 0276 } catch (Zend_Mail_Protocol_Exception $e) { 0277 // ignore 0278 } 0279 } 0280 0281 $result = $this->request("USER $user"); 0282 $result = $this->request("PASS $password"); 0283 } 0284 0285 0286 /** 0287 * Make STAT call for message count and size sum 0288 * 0289 * @param int $messages out parameter with count of messages 0290 * @param int $octets out parameter with size in octects of messages 0291 * @return void 0292 * @throws Zend_Mail_Protocol_Exception 0293 */ 0294 public function status(&$messages, &$octets) 0295 { 0296 $messages = 0; 0297 $octets = 0; 0298 $result = $this->request('STAT'); 0299 0300 list($messages, $octets) = explode(' ', $result); 0301 } 0302 0303 0304 /** 0305 * Make LIST call for size of message(s) 0306 * 0307 * @param int|null $msgno number of message, null for all 0308 * @return int|array size of given message or list with array(num => size) 0309 * @throws Zend_Mail_Protocol_Exception 0310 */ 0311 public function getList($msgno = null) 0312 { 0313 if ($msgno !== null) { 0314 $result = $this->request("LIST $msgno"); 0315 0316 list(, $result) = explode(' ', $result); 0317 return (int)$result; 0318 } 0319 0320 $result = $this->request('LIST', true); 0321 $messages = array(); 0322 $line = strtok($result, "\n"); 0323 while ($line) { 0324 list($no, $size) = explode(' ', trim($line)); 0325 $messages[(int)$no] = (int)$size; 0326 $line = strtok("\n"); 0327 } 0328 0329 return $messages; 0330 } 0331 0332 0333 /** 0334 * Make UIDL call for getting a uniqueid 0335 * 0336 * @param int|null $msgno number of message, null for all 0337 * @return string|array uniqueid of message or list with array(num => uniqueid) 0338 * @throws Zend_Mail_Protocol_Exception 0339 */ 0340 public function uniqueid($msgno = null) 0341 { 0342 if ($msgno !== null) { 0343 $result = $this->request("UIDL $msgno"); 0344 0345 list(, $result) = explode(' ', $result); 0346 return $result; 0347 } 0348 0349 $result = $this->request('UIDL', true); 0350 0351 $result = explode("\n", $result); 0352 $messages = array(); 0353 foreach ($result as $line) { 0354 if (!$line) { 0355 continue; 0356 } 0357 list($no, $id) = explode(' ', trim($line), 2); 0358 $messages[(int)$no] = $id; 0359 } 0360 0361 return $messages; 0362 0363 } 0364 0365 0366 /** 0367 * Make TOP call for getting headers and maybe some body lines 0368 * This method also sets hasTop - before it it's not known if top is supported 0369 * 0370 * The fallback makes normale RETR call, which retrieves the whole message. Additional 0371 * lines are not removed. 0372 * 0373 * @param int $msgno number of message 0374 * @param int $lines number of wanted body lines (empty line is inserted after header lines) 0375 * @param bool $fallback fallback with full retrieve if top is not supported 0376 * @return string message headers with wanted body lines 0377 * @throws Zend_Mail_Protocol_Exception 0378 */ 0379 public function top($msgno, $lines = 0, $fallback = false) 0380 { 0381 if ($this->hasTop === false) { 0382 if ($fallback) { 0383 return $this->retrieve($msgno); 0384 } else { 0385 /** 0386 * @see Zend_Mail_Protocol_Exception 0387 */ 0388 // require_once 'Zend/Mail/Protocol/Exception.php'; 0389 throw new Zend_Mail_Protocol_Exception('top not supported and no fallback wanted'); 0390 } 0391 } 0392 $this->hasTop = true; 0393 0394 $lines = (!$lines || $lines < 1) ? 0 : (int)$lines; 0395 0396 try { 0397 $result = $this->request("TOP $msgno $lines", true); 0398 } catch (Zend_Mail_Protocol_Exception $e) { 0399 $this->hasTop = false; 0400 if ($fallback) { 0401 $result = $this->retrieve($msgno); 0402 } else { 0403 throw $e; 0404 } 0405 } 0406 0407 return $result; 0408 } 0409 0410 0411 /** 0412 * Make a RETR call for retrieving a full message with headers and body 0413 * 0414 * @deprecated since 1.1.0; this method has a typo - please use retrieve() 0415 * @param int $msgno message number 0416 * @return string message 0417 * @throws Zend_Mail_Protocol_Exception 0418 */ 0419 public function retrive($msgno) 0420 { 0421 return $this->retrieve($msgno); 0422 } 0423 0424 0425 /** 0426 * Make a RETR call for retrieving a full message with headers and body 0427 * 0428 * @param int $msgno message number 0429 * @return string message 0430 * @throws Zend_Mail_Protocol_Exception 0431 */ 0432 public function retrieve($msgno) 0433 { 0434 $result = $this->request("RETR $msgno", true); 0435 return $result; 0436 } 0437 0438 /** 0439 * Make a NOOP call, maybe needed for keeping the server happy 0440 * 0441 * @return null 0442 * @throws Zend_Mail_Protocol_Exception 0443 */ 0444 public function noop() 0445 { 0446 $this->request('NOOP'); 0447 } 0448 0449 0450 /** 0451 * Make a DELE count to remove a message 0452 * 0453 * @return null 0454 * @throws Zend_Mail_Protocol_Exception 0455 */ 0456 public function delete($msgno) 0457 { 0458 $this->request("DELE $msgno"); 0459 } 0460 0461 0462 /** 0463 * Make RSET call, which rollbacks delete requests 0464 * 0465 * @return null 0466 * @throws Zend_Mail_Protocol_Exception 0467 */ 0468 public function undelete() 0469 { 0470 $this->request('RSET'); 0471 } 0472 }