Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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