ここの情報は古いです。ご理解頂いた上でお取り扱いください。

source: OpenPNE/branches/maruyama/prj_ext_auth/lib/include/Net/POP3.php @ 1137

Last change on this file since 1137 was 1137, checked in by maruyama, 14 years ago

認証タイプの未定義対応

File size: 32.9 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Copyright (c) 2002, Richard Heyes                                     |
4// | All rights reserved.                                                  |
5// |                                                                       |
6// | Redistribution and use in source and binary forms, with or without    |
7// | modification, are permitted provided that the following conditions    |
8// | are met:                                                              |
9// |                                                                       |
10// | o Redistributions of source code must retain the above copyright      |
11// |   notice, this list of conditions and the following disclaimer.       |
12// | o Redistributions in binary form must reproduce the above copyright   |
13// |   notice, this list of conditions and the following disclaimer in the |
14// |   documentation and/or other materials provided with the distribution.|
15// | o The names of the authors may not be used to endorse or promote      |
16// |   products derived from this software without specific prior written  |
17// |   permission.                                                         |
18// |                                                                       |
19// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
20// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
21// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
23// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
25// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
28// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
30// |                                                                       |
31// +-----------------------------------------------------------------------+
32// | Author: Richard Heyes <richard@phpguru.org>                           |
33// | Co-Author: Damian Fernandez Sosa <damlists@cnba.uba.ar>               |
34// +-----------------------------------------------------------------------+
35//
36// $Id: POP3.php,v 1.2 2004/12/05 16:34:39 damian Exp $
37
38require_once('Net/Socket.php');
39
40
41
42/**
43*  +----------------------------- IMPORTANT ------------------------------+
44*  | Usage of this class compared to native php extensions such as IMAP   |
45*  | is slow and may be feature deficient. If available you are STRONGLY  |
46*  | recommended to use the php extensions.                               |
47*  +----------------------------------------------------------------------+
48*
49* POP3 Access Class
50*
51* For usage see the example script
52*/
53
54define('NET_POP3_STATE_DISCONNECTED',  1, true);
55define('NET_POP3_STATE_AUTHORISATION', 2, true);
56define('NET_POP3_STATE_TRANSACTION',   4, true);
57
58class Net_POP3 {
59
60    /*
61    * Some basic information about the mail drop
62    * garnered from the STAT command
63    *
64    * @var array
65    */
66    var $_maildrop;
67
68    /*
69    * Used for APOP to store the timestamp
70    *
71    * @var string
72    */
73    var $_timestamp;
74
75    /*
76    * Timeout that is passed to the socket object
77    *
78    * @var integer
79    */
80    var $_timeout;
81
82    /*
83    * Socket object
84    *
85    * @var object
86    */
87    var $_socket;
88
89    /*
90    * Current state of the connection. Used with the
91    * constants defined above.
92    *
93    * @var integer
94    */
95    var $_state;
96
97    /*
98    * Hostname to connect to
99    *
100    * @var string
101    */
102    var $_host;
103
104    /*
105    * Port to connect to
106    *
107    * @var integer
108    */
109    var $_port;
110
111    /**
112    * To allow class debuging
113    * @var boolean
114    */
115    var $_debug = false;
116
117
118    /**
119    * The auth methods this class support
120    * @var array
121    */
122    //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
123    //Disabling DIGEST-MD5 for now
124    var $supportedAuthMethods=array( 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
125    //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');
126    //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');
127
128
129    /**
130    * The auth methods this class support
131    * @var array
132    */
133    var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5');
134
135
136    /**
137    * The capability response
138    * @var array
139    */
140    var $_capability;
141
142   /*
143    * Constructor. Sets up the object variables, and instantiates
144    * the socket object.
145    *
146    */
147
148
149    function Net_POP3()
150    {
151        $this->_timestamp =  ''; // Used for APOP
152        $this->_maildrop  =  array();
153        $this->_timeout   =  3;
154        $this->_state     =  NET_POP3_STATE_DISCONNECTED;
155        $this->_socket    =& new Net_Socket();
156        /*
157        * Include the Auth_SASL package.  If the package is not available,
158        * we disable the authentication methods that depend upon it.
159        */
160        if ((@include_once 'Auth/SASL.php') == false) {
161            if($this->_debug){
162                echo "AUTH_SASL NOT PRESENT!\n";
163            }
164            foreach($this->supportedSASLAuthMethods as $SASLMethod){
165                $pos = array_search( $SASLMethod, $this->supportedAuthMethods );
166                if($this->_debug){
167                    echo "DISABLING METHOD $SASLMethod\n";
168                }
169                unset($this->supportedAuthMethods[$pos]);
170            }
171        }
172
173
174
175    }
176
177
178    /**
179    * Handles the errors the class can find
180    * on the server
181    *
182    * @access private
183    * @return PEAR_Error
184    */
185
186    function _raiseError($msg, $code =-1)
187    {
188    include_once 'PEAR.php';
189    return PEAR::raiseError($msg, $code);
190    }
191
192
193   
194    /*
195    * Connects to the given host on the given port.
196    * Also looks for the timestamp in the greeting
197    * needed for APOP authentication
198    *
199    * @param  string $host Hostname/IP address to connect to
200    * @param  string $port Port to use to connect to on host
201    * @return bool  Success/Failure
202    */
203    function connect($host = 'localhost', $port = 110)
204    {
205        $this->_host = $host;
206        $this->_port = $port;
207
208        $result = $this->_socket->connect($host, $port, false, $this->_timeout);
209        if ($result === true) {
210            $data = $this->_recvLn();
211
212            if( $this->_checkResponse($data) ){
213            // if the response begins with '+OK' ...
214//            if (@substr(strtoupper($data), 0, 3) == '+OK') {
215                // Check for string matching apop timestamp
216                if (preg_match('/<.+@.+>/U', $data, $matches)) {
217                    $this->_timestamp = $matches[0];
218                }
219                $this->_maildrop = array();
220                $this->_state    = NET_POP3_STATE_AUTHORISATION;
221
222                return true;
223            }
224        }
225
226        $this->_socket->disconnect();
227        return false;
228    }
229
230    /*
231    * Disconnect function. Sends the QUIT command
232    * and closes the socket.
233    *
234    * @return bool Success/Failure
235    */
236    function disconnect()
237    {
238        return $this->_cmdQuit();
239    }
240
241    /*
242    * Performs the login procedure. If there is a timestamp
243    * stored, APOP will be tried first, then basic USER/PASS.
244    *
245    * @param  string $user Username to use
246    * @param  string $pass Password to use
247    * @param  mixed $apop Whether to try APOP first, if used as string you can select the auth methd to use ( $pop3->login('validlogin', 'validpass', "CRAM-MD5");
248    *          Valid methods are: 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER'
249    * @return mixed  true on Success/ PEAR_ERROR on error
250    */
251    function login($user, $pass, $apop = true)
252    {
253        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
254
255            if(PEAR::isError($ret= $this->_cmdAuthenticate($user , $pass , $apop ) ) ){
256                return $ret;
257            }
258            if( ! PEAR::isError($ret)){
259                $this->_state = NET_POP3_STATE_TRANSACTION;
260                return true;
261            }
262
263        }
264        return $this->_raiseError('Generic login error' , 1);
265    }
266
267
268
269    /**
270    * Parses the response from the capability command. Stores
271    * the result in $this->_capability
272    *
273    * @access private
274    */
275    function _parseCapability()
276    {
277
278        if(!PEAR::isError($data = $this->_sendCmd('CAPA'))){
279            $data = $this->_getMultiline();
280        }else {
281            // CAPA command not supported, reset data var
282            //  to avoid Notice errors of preg_split on an object
283            $data = '';
284        }
285        $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);
286
287        for ($i = 0; $i < count($data); $i++) {
288
289            $capa='';
290            if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $data[$i], $matches)) {
291
292                $capa=strtolower($matches[1]);
293                switch ($capa) {
294                    case 'implementation':
295                        $this->_capability['implementation'] = $matches[3];
296                        break;
297                    case 'sasl':
298                        $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);
299                        break;
300                    default :
301                        $this->_capability[$capa] = $matches[2];
302                        break;
303                }
304            }
305        }
306    }
307
308
309
310
311    /**
312     * Returns the name of the best authentication method that the server
313     * has advertised.
314     *
315     * @param string if !=null,authenticate with this method ($userMethod).
316     *
317     * @return mixed    Returns a string containing the name of the best
318     *                  supported authentication method or a PEAR_Error object
319     *                  if a failure condition is encountered.
320     * @access private
321     * @since  1.0
322     */
323    function _getBestAuthMethod($userMethod = null)
324    {
325
326/*
327       return 'USER';
328       return 'APOP';
329       return 'DIGEST-MD5';
330       return 'CRAM-MD5';
331*/
332
333
334        $this->_parseCapability();
335
336        //unset($this->_capability['sasl']);
337
338       if( isset($this->_capability['sasl']) ){
339           $serverMethods=$this->_capability['sasl'];
340       }else{
341            $serverMethods=array('USER');
342            // Check for timestamp before attempting APOP
343            if ($this->_timestamp != null)
344            {
345                $serverMethods[] = 'APOP';
346            }
347       }
348
349        if($userMethod !== null && $userMethod !== true ){
350            $methods = array();
351            $methods[] = $userMethod;
352            return $userMethod;
353        }else{
354            $methods = $this->supportedAuthMethods;
355        }
356
357        if( ($methods != null) && ($serverMethods != null)){
358
359            foreach ( $methods as $method ) {
360
361                if ( in_array( $method , $serverMethods ) ) {
362                    return $method;
363                }
364            }
365            $serverMethods=implode(',' , $serverMethods );
366            $myMethods=implode(',' ,$this->supportedAuthMethods);
367            return $this->_raiseError("$method NOT supported authentication method!. This server " .
368                "supports these methods: $serverMethods, but I support $myMethods");
369        }else{
370            return $this->_raiseError("This server don't support any Auth methods");
371        }
372    }
373
374
375
376
377
378
379    /* Handles the authentication using any known method
380     *
381     * @param string The userid to authenticate as.
382     * @param string The password to authenticate with.
383     * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) )
384     *
385     * @return mixed  string or PEAR_Error
386     *
387     * @access private
388     * @since  1.0
389     */
390    function _cmdAuthenticate($uid , $pwd , $userMethod = null )
391    {
392
393
394        if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) {
395            return $method;
396        }
397
398        switch ($method) {
399            case 'DIGEST-MD5':
400                $result = $this->_authDigest_MD5( $uid , $pwd );
401                break;
402            case 'CRAM-MD5':
403                $result = $this->_authCRAM_MD5( $uid , $pwd );
404                break;
405            case 'LOGIN':
406                $result = $this->_authLOGIN( $uid , $pwd );
407                break;
408            case 'PLAIN':
409                $result = $this->_authPLAIN( $uid , $pwd );
410                break;
411            case 'APOP':
412                $result = $this->_cmdApop( $uid , $pwd );
413                // if APOP fails fallback to USER auth
414                if( PEAR::isError( $result ) ){
415                    //echo "APOP FAILED!!!\n";
416                    $result=$this->_authUSER( $uid , $pwd );
417                }
418                break;
419            case 'USER':
420                $result = $this->_authUSER( $uid , $pwd );
421            break;
422
423
424            default :
425                $result = $this->_raiseError( "$method is not a supported authentication method" );
426                break;
427        }
428        return $result;
429    }
430
431
432
433
434     /* Authenticates the user using the USER-PASS method.
435     *
436     * @param string The userid to authenticate as.
437     * @param string The password to authenticate with.
438     *
439     * @return mixed    true on success or PEAR_Error on failure
440     *
441     * @access private
442     * @since  1.0
443     */
444    function _authUSER($user, $pass  )
445    {
446        if ( PEAR::isError($ret=$this->_cmdUser($user) ) ){
447            return $ret;
448        }
449        if ( PEAR::isError($ret=$this->_cmdPass($pass) ) ){
450            return $ret;
451        }
452        return true;
453    }
454
455
456
457
458
459
460
461
462     /* Authenticates the user using the PLAIN method.
463     *
464     * @param string The userid to authenticate as.
465     * @param string The password to authenticate with.
466     *
467     * @return array Returns an array containing the response
468     *
469     * @access private
470     * @since  1.0
471     */
472    function _authPLAIN($user, $pass  )
473    {
474        $cmd=sprintf('AUTH PLAIN %s', base64_encode( chr(0) . $user . chr(0) . $pass ) );
475
476        if ( PEAR::isError( $ret = $this->_send($cmd) ) ) {
477            return $ret;
478        }
479        if ( PEAR::isError( $challenge = $this->_recvLn() ) ){
480            return $challenge;
481        }
482        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
483            return $ret;
484        }
485       
486        return true;
487    }
488
489
490
491     /* Authenticates the user using the PLAIN method.
492     *
493     * @param string The userid to authenticate as.
494     * @param string The password to authenticate with.
495     *
496     * @return array Returns an array containing the response
497     *
498     * @access private
499     * @since  1.0
500     */
501    function _authLOGIN($user, $pass  )
502    {
503        $this->_send('AUTH LOGIN');
504
505        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
506            return $challenge;
507        }
508        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
509            return $ret;
510        }
511
512
513        if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($user))) ) ) {
514            return $ret;
515        }
516
517        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
518            return $challenge;
519        }
520        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
521            return $ret;
522        }
523
524        if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($pass))) ) ) {
525            return $ret;
526        }
527
528        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
529            return $challenge;
530        }
531        return $this->_checkResponse($challenge);
532    }
533
534
535
536
537
538     /* Authenticates the user using the CRAM-MD5 method.
539     *
540     * @param string The userid to authenticate as.
541     * @param string The password to authenticate with.
542     *
543     * @return array Returns an array containing the response
544     *
545     * @access private
546     * @since  1.0
547     */
548    function _authCRAM_MD5($uid, $pwd )
549    {
550        if ( PEAR::isError( $ret = $this->_send( 'AUTH CRAM-MD5' ) ) ) {
551            return $ret;
552        }
553
554        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
555            return $challenge;
556        }
557        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
558            return $ret;
559        }
560
561        // remove '+ '
562       
563        $challenge=substr($challenge,2);
564       
565        $challenge = base64_decode( $challenge );
566
567        $cram = &Auth_SASL::factory('crammd5');
568        $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) );
569
570
571        if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
572            return $error;
573        }
574        if ( PEAR::isError( $ret = $this->_recvLn() ) ) {
575            return $ret;
576        }
577        //echo "RET:$ret\n";
578        return $this->_checkResponse($ret);
579    }
580
581
582
583     /* Authenticates the user using the DIGEST-MD5 method.
584     *
585     * @param string The userid to authenticate as.
586     * @param string The password to authenticate with.
587     * @param string The efective user
588     *
589     * @return array Returns an array containing the response
590     *
591     * @access private
592     * @since  1.0
593     */
594    function _authDigest_MD5($uid, $pwd)
595    {
596        if ( PEAR::isError( $ret = $this->_send( 'AUTH DIGEST-MD5' ) ) ) {
597            return $ret;
598        }
599
600        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
601            return $challenge;
602        }
603        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
604            return $ret;
605        }
606
607        // remove '+ '
608        $challenge=substr($challenge,2);
609
610        $challenge = base64_decode( $challenge );
611        $digest = &Auth_SASL::factory('digestmd5');
612        $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3" ));
613
614        if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
615            return $error;
616        }
617
618        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
619            return $challenge;
620        }
621        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
622            return $ret;
623        }
624         /*
625         * We don't use the protocol's third step because POP3 doesn't allow
626         * subsequent authentication, so we just silently ignore it.
627         */
628
629        if ( PEAR::isError( $challenge = $this->_send("\r\n") ) ) {
630            return $challenge ;
631        }
632       
633        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
634            return $challenge;
635        }
636       
637        return $this->_checkResponse($challenge);
638       
639
640    }
641
642
643
644
645
646
647
648
649
650
651    /*
652    * Sends the APOP command
653    *
654    * @param  $user Username to send
655    * @param  $pass Password to send
656    * @return bool Success/Failure
657    */
658    function _cmdApop($user, $pass)
659    {
660        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
661
662            if (!empty($this->_timestamp)) {
663                if(PEAR::isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)) ) ){
664                    return $data;
665                }
666                $this->_state = NET_POP3_STATE_TRANSACTION;
667                return true;
668            }
669        }
670        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1');
671    }
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687    /*
688    * Returns the raw headers of the specified message.
689    *
690    * @param  integer $msg_id Message number
691    * @return mixed   Either raw headers or false on error
692    */
693    function getRawHeaders($msg_id)
694    {
695        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
696            return $this->_cmdTop($msg_id, 0);
697        }
698
699        return false;
700    }
701
702    /*
703    * Returns the  headers of the specified message in an
704    * associative array. Array keys are the header names, array
705    * values are the header values. In the case of multiple headers
706    * having the same names, eg Received:, the array value will be
707    * an indexed array of all the header values.
708    *
709    * @param  integer $msg_id Message number
710    * @return mixed   Either array of headers or false on error
711    */
712    function getParsedHeaders($msg_id)
713    {
714        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
715
716            $raw_headers = rtrim($this->getRawHeaders($msg_id));
717
718            $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers
719            $raw_headers = explode("\r\n", $raw_headers);
720            foreach ($raw_headers as $value) {
721                $name  = substr($value, 0, $pos = strpos($value, ':'));
722                $value = ltrim(substr($value, $pos + 1));
723                if (isset($headers[$name]) AND is_array($headers[$name])) {
724                    $headers[$name][] = $value;
725                } elseif (isset($headers[$name])) {
726                    $headers[$name] = array($headers[$name], $value);
727                } else {
728                    $headers[$name] = $value;
729                }
730            }
731
732            return $headers;
733        }
734
735        return false;
736    }
737
738    /*
739    * Returns the body of the message with given message number.
740    *
741    * @param  integer $msg_id Message number
742    * @return mixed   Either message body or false on error
743    */
744    function getBody($msg_id)
745    {
746        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
747            $msg = $this->_cmdRetr($msg_id);
748            return substr($msg, strpos($msg, "\r\n\r\n")+4);
749        }
750
751        return false;
752    }
753
754    /*
755    * Returns the entire message with given message number.
756    *
757    * @param  integer $msg_id Message number
758    * @return mixed   Either entire message or false on error
759    */
760    function getMsg($msg_id)
761    {
762        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
763            return $this->_cmdRetr($msg_id);
764        }
765
766        return false;
767    }
768
769    /*
770    * Returns the size of the maildrop
771    *
772    * @return mixed Either size of maildrop or false on error
773    */
774    function getSize()
775    {
776        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
777            if (isset($this->_maildrop['size'])) {
778                return $this->_maildrop['size'];
779            } else {
780                list(, $size) = $this->_cmdStat();
781                return $size;
782            }
783        }
784
785        return false;
786    }
787
788    /*
789    * Returns number of messages in this maildrop
790    *
791    * @return mixed Either number of messages or false on error
792    */
793    function numMsg()
794    {
795        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
796            if (isset($this->_maildrop['num_msg'])) {
797                return $this->_maildrop['num_msg'];
798            } else {
799                list($num_msg, ) = $this->_cmdStat();
800                return $num_msg;
801            }
802        }
803
804        return false;
805    }
806
807    /*
808    * Marks a message for deletion. Only will be deleted if the
809    * disconnect() method is called.
810    *
811    * @param  integer $msg_id Message to delete
812    * @return bool Success/Failure
813    */
814    function deleteMsg($msg_id)
815    {
816        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
817            return $this->_cmdDele($msg_id);
818        }
819
820        return false;
821    }
822
823    /*
824    * Combination of LIST/UIDL commands, returns an array
825    * of data
826    *
827    * @param  integer $msg_id Optional message number
828    * @return mixed Array of data or false on error
829    */
830    function getListing($msg_id = null)
831    {
832   
833        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
834            if (!isset($msg_id)){
835           
836                $list=array();
837                if ($list = $this->_cmdList()) {
838                    if ($uidl = $this->_cmdUidl()) {
839                        foreach ($uidl as $i => $value) {
840                            $list[$i]['uidl'] = $value['uidl'];
841                        }
842                    }
843                    return $list;
844                }else{
845                    return array();
846                }
847            } else {
848                if ($list = $this->_cmdList($msg_id) AND $uidl = $this->_cmdUidl($msg_id)) {
849                    return array_merge($list, $uidl);
850                }
851            }
852        }
853
854        return false;
855    }
856
857    /*
858    * Sends the USER command
859    *
860    * @param  string $user Username to send
861    * @return bool  Success/Failure
862    */
863    function _cmdUser($user)
864    {
865        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
866            return $this->_sendCmd('USER ' . $user);
867        }
868        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
869    }
870
871
872    /*
873    * Sends the PASS command
874    *
875    * @param  string $pass Password to send
876    * @return bool  Success/Failure
877    */
878    function _cmdPass($pass)
879    {
880        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
881            return $this->_sendCmd('PASS ' . $pass);
882        }
883        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
884    }
885
886
887    /*
888    * Sends the STAT command
889    *
890    * @return mixed Indexed array of number of messages and
891    *               maildrop size, or false on error.
892    */
893    function _cmdStat()
894    {
895        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
896            if(!PEAR::isError($data = $this->_sendCmd('STAT'))){
897                sscanf($data, '+OK %d %d', $msg_num, $size);
898                $this->_maildrop['num_msg'] = $msg_num;
899                $this->_maildrop['size']    = $size;
900
901                return array($msg_num, $size);
902            }
903        }
904        return false;
905    }
906
907
908    /*
909    * Sends the LIST command
910    *
911    * @param  integer $msg_id Optional message number
912    * @return mixed   Indexed array of msg_id/msg size or
913    *                 false on error
914    */
915    function _cmdList($msg_id = null)
916    {
917        $return=array();
918        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
919            if (!isset($msg_id)) {
920                if(!PEAR::isError($data = $this->_sendCmd('LIST') )){
921                    $data = $this->_getMultiline();
922                    $data = explode("\r\n", $data);                   
923                    foreach ($data as $line) {
924                        if($line !=''){
925                            sscanf($line, '%s %s', $msg_id, $size);
926                            $return[] = array('msg_id' => $msg_id, 'size' => $size);
927                        }
928                    }
929                    return $return;
930                }
931            } else {
932                if(!PEAR::isError($data = $this->_sendCmd('LIST ' . $msg_id))){
933                    if($data!=''){
934                        sscanf($data, '+OK %d %d', $msg_id, $size);
935                        return array('msg_id' => $msg_id, 'size' => $size);
936                    }
937                    return array();
938                }
939            }
940        }
941       
942
943        return false;
944    }
945
946
947    /*
948    * Sends the RETR command
949    *
950    * @param  integer $msg_id The message number to retrieve
951    * @return mixed   The message or false on error
952    */
953    function _cmdRetr($msg_id)
954    {
955        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
956            if(!PEAR::isError($data = $this->_sendCmd('RETR ' . $msg_id) )){
957                $data = $this->_getMultiline();
958                return $data;
959            }
960        }
961
962        return false;
963    }
964
965
966    /*
967    * Sends the DELE command
968    *
969    * @param  integer $msg_id Message number to mark as deleted
970    * @return bool Success/Failure
971    */
972    function _cmdDele($msg_id)
973    {
974        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
975            return $this->_sendCmd('DELE ' . $msg_id);
976        }
977
978        return false;
979    }
980
981
982    /*
983    * Sends the NOOP command
984    *
985    * @return bool Success/Failure
986    */
987    function _cmdNoop()
988    {
989        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
990            if(!PEAR::isError($data = $this->_sendCmd('NOOP'))){
991                return true;
992            }
993        }
994
995        return false;
996    }
997
998    /*
999    * Sends the RSET command
1000    *
1001    * @return bool Success/Failure
1002    */
1003    function _cmdRset()
1004    {
1005        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1006            if(!PEAR::isError($data = $this->_sendCmd('RSET'))){
1007                return true;
1008            }
1009        }
1010
1011        return false;
1012    }
1013
1014    /*
1015    * Sends the QUIT command
1016    *
1017    * @return bool Success/Failure
1018    */
1019    function _cmdQuit()
1020    {
1021        $data = $this->_sendCmd('QUIT');
1022        $this->_state = NET_POP3_STATE_DISCONNECTED;
1023        $this->_socket->disconnect();
1024
1025        return (bool)$data;
1026    }
1027
1028
1029    /*
1030    * Sends the TOP command
1031    *
1032    * @param  integer  $msg_id    Message number
1033    * @param  integer  $num_lines Number of lines to retrieve
1034    * @return mixed Message data or false on error
1035    */
1036    function _cmdTop($msg_id, $num_lines)
1037    {
1038        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1039
1040            if(!PEAR::isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))){
1041                return $this->_getMultiline();
1042            }
1043        }
1044
1045        return false;
1046    }
1047
1048    /*
1049    * Sends the UIDL command
1050    *
1051    * @param  integer $msg_id Message number
1052    * @return mixed indexed array of msg_id/uidl or false on error
1053    */
1054    function _cmdUidl($msg_id = null)
1055    {
1056        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1057
1058            if (!isset($msg_id)) {
1059                if(!PEAR::isError($data = $this->_sendCmd('UIDL') )){
1060                    $data = $this->_getMultiline();
1061                    $data = explode("\r\n", $data);
1062                    foreach ($data as $line) {
1063                        sscanf($line, '%d %s', $msg_id, $uidl);
1064                        $return[] = array('msg_id' => $msg_id, 'uidl' => $uidl);
1065                    }
1066
1067                    return $return;
1068                }
1069            } else {
1070
1071                $data = $this->_sendCmd('UIDL ' . $msg_id);
1072                sscanf($data, '+OK %d %s', $msg_id, $uidl);
1073                return array('msg_id' => $msg_id, 'uidl' => $uidl);
1074            }
1075        }
1076
1077        return false;
1078    }
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088    /*
1089    * Sends a command, checks the reponse, and
1090    * if good returns the reponse, other wise
1091    * returns false.
1092    *
1093    * @param  string $cmd  Command to send (\r\n will be appended)
1094    * @return mixed First line of response if successful, otherwise false
1095    */
1096    function _sendCmd($cmd)
1097    {
1098        if (PEAR::isError($result = $this->_send($cmd) )){
1099            return $result ;
1100        }
1101
1102        if (PEAR::isError($data = $this->_recvLn() )){
1103            return $data;
1104        }
1105       
1106        if ( strtoupper(substr($data, 0, 3)) == '+OK') {
1107            return $data;
1108        }
1109       
1110       
1111        return $this->_raiseError($data);
1112    }
1113
1114    /*
1115    * Reads a multiline reponse and returns the data
1116    *
1117    * @return string The reponse.
1118    */
1119    function _getMultiline()
1120    {
1121        $data = '';
1122        while(!PEAR::isError($tmp = $this->_recvLn() ) ) {
1123            if($tmp == '.'){
1124                return substr($data, 0, -2);
1125            }
1126            if (substr($tmp, 0, 2) == '..') {
1127                $tmp = substr($tmp, 1);
1128            }
1129            $data .= $tmp . "\r\n";
1130        }
1131        return substr($data, 0, -2);
1132    }
1133
1134
1135   /**
1136    * Sets the bebug state
1137    *
1138    * @param  bool $debug
1139    * @access public
1140    * @return void
1141    */
1142    function setDebug($debug=true)
1143    {
1144        $this->_debug=$debug;
1145    }
1146
1147
1148
1149
1150
1151   /**
1152     * Send the given string of data to the server.
1153     *
1154     * @param   string  $data       The string of data to send.
1155     *
1156     * @return  mixed   True on success or a PEAR_Error object on failure.
1157     *
1158     * @access  private
1159     * @since   1.0
1160     */
1161    function _send($data)
1162    {
1163        if ($this->_debug) {
1164            echo "C: $data\n";
1165        }
1166
1167        if (PEAR::isError($error = $this->_socket->writeLine($data))) {
1168            return $this->_raiseError('Failed to write to socket: ' . $error->getMessage());
1169        }
1170        return true;
1171    }
1172
1173
1174
1175     /**
1176     * Receive the given string of data from the server.
1177     *
1178     * @return  mixed   a line of response on success or a PEAR_Error object on failure.
1179     *
1180     * @access  private
1181     * @since  1.0
1182     */
1183    function _recvLn()
1184    {
1185        if (PEAR::isError( $lastline = $this->_socket->readLine( 8192 ) ) ) {
1186            return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage() );
1187        }
1188        if($this->_debug){
1189            // S: means this data was sent by  the POP3 Server
1190            echo "S:$lastline\n" ;
1191        }
1192        return $lastline;
1193    }
1194
1195     /**
1196     * Checks de server Response
1197     *
1198     * @param  string $response the response
1199     * @return  mixed   true on success or a PEAR_Error object on failure.
1200     *
1201     * @access  private
1202     * @since  1.3.3
1203     */
1204
1205    function _checkResponse($response)
1206    {
1207        if (@substr(strtoupper($response), 0, 3) == '+OK') {
1208            return true;
1209        }else{
1210            if (@substr(strtoupper($response), 0, 4) == '-ERR') {
1211                return $this->_raiseError($response);
1212            }else{
1213                if (@substr(strtoupper($response), 0, 2) == '+ ') {
1214                    return true;
1215                }
1216            }
1217   
1218        }
1219        return $this->_raiseError("Unknown Response ($response)");
1220    }
1221   
1222
1223
1224}
1225
1226?>
Note: See TracBrowser for help on using the repository browser.