PageRenderTime 226ms CodeModel.GetById 121ms app.highlight 13ms RepoModel.GetById 88ms app.codeStats 0ms

/Mail/Protocol/Pop3.php

https://bitbucket.org/bigstylee/zend-framework
PHP | 472 lines | 213 code | 65 blank | 194 comment | 35 complexity | d704d555039f476a2b8475189b63d489 MD5 | raw file
  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-2012 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 24593 2012-01-05 20:35:02Z matthew $
 21 */
 22
 23
 24/**
 25 * @category   Zend
 26 * @package    Zend_Mail
 27 * @subpackage Protocol
 28 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 29 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 30 */
 31class 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}