Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * Net_IMAP provides an implementation of the IMAP protocol
4
 *
5
 * PHP Version 4
6
 *
7
 * @category  Networking
8
 * @package   Net_IMAP
9
 * @author    Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
10
 * @copyright 1997-2003 The PHP Group
11
 * @license   PHP license
12
 * @version   CVS: $Id: IMAPProtocol.php,v 1.25 2008/03/09 22:47:35 hudeldudel Exp $
13
 * @link      http://pear.php.net/package/Net_IMAP
14
 */
15
 
16
/**
17
 * Net_IMAP requires Net_Socket
18
 */
19
require_once 'Net/Socket.php';
20
 
21
 
22
/**
23
 * Provides an implementation of the IMAP protocol using PEAR's
24
 * Net_Socket:: class.
25
 *
26
 * @category Networking
27
 * @package  Net_IMAP
28
 * @author   Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
29
 * @license  PHP license
30
 * @link     http://pear.php.net/package/Net_IMAP
31
 */
32
class Net_IMAPProtocol
33
{
34
    /**
35
     * The auth methods this class support
36
     * @var array
37
     */
38
    var $supportedAuthMethods = array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN');
39
 
40
 
41
    /**
42
     * The auth methods this class support
43
     * @var array
44
     */
45
    var $supportedSASLAuthMethods = array('DIGEST-MD5', 'CRAM-MD5');
46
 
47
 
48
    /**
49
     * _serverAuthMethods
50
     * @var boolean
51
     */
52
    var $_serverAuthMethods = null;
53
 
54
 
55
    /**
56
     * The the current mailbox
57
     * @var string
58
     */
59
    var $currentMailbox = 'INBOX';
60
 
61
 
62
    /**
63
     * The socket resource being used to connect to the IMAP server.
64
     * @var resource
65
     */
66
    var $_socket = null;
67
 
68
 
69
    /**
70
     * The timeout for the connection to the IMAP server.
71
     * @var int
72
     */
73
    var $_timeout = null;
74
 
75
 
76
    /**
77
     * The options for SSL/TLS connection
78
     * (see documentation for stream_context_create)
79
     * @var array
80
     */
81
    var $_streamContextOptions = null;
82
 
83
 
84
    /**
85
     * To allow class debuging
86
     * @var boolean
87
     */
88
    var $_debug = false;
89
    var $dbgDialog = '';
90
 
91
 
92
    /**
93
     * Print error messages
94
     * @var boolean
95
     */
96
    var $_printErrors = false;
97
 
98
 
99
    /**
100
     * Command Number
101
     * @var int
102
     */
103
    var $_cmd_counter = 1;
104
 
105
 
106
    /**
107
     * Command Number for IMAP commands
108
     * @var int
109
     */
110
    var $_lastCmdID = 1;
111
 
112
 
113
    /**
114
     * Command Number
115
     * @var boolean
116
     */
117
    var $_unParsedReturn = false;
118
 
119
 
120
    /**
121
     * _connected: checks if there is a connection made to a imap server or not
122
     * @var boolean
123
     */
124
    var $_connected = false;
125
 
126
 
127
    /**
128
     * Capabilities
129
     * @var boolean
130
     */
131
    var $_serverSupportedCapabilities = null;
132
 
133
 
134
    /**
135
     * Use UTF-7 funcionallity
136
     * @var boolean
137
     */
138
    var $_useUTF_7 = true;
139
 
140
 
141
 
142
    /**
143
     * Constructor
144
     *
145
     * Instantiates a new Net_IMAP object.
146
     *
147
     * @since  1.0
148
     */
149
    function Net_IMAPProtocol()
150
    {
151
        $this->_socket = new Net_Socket();
152
 
153
        /*
154
         * Include the Auth_SASL package.  If the package is not available,
155
         * we disable the authentication methods that depend upon it.
156
         */
157
 
158
 
159
        if ((@include_once 'Auth/SASL.php') == false) {
160
            foreach ($this->supportedSASLAuthMethods as $SASLMethod) {
161
                $pos = array_search($SASLMethod, $this->supportedAuthMethods);
162
                unset($this->supportedAuthMethods[$pos]);
163
            }
164
        }
165
    }
166
 
167
 
168
 
169
    /**
170
     * Attempt to connect to the IMAP server.
171
     *
172
     * @param string $host Hostname of the IMAP server
173
     * @param int    $port Port of the IMAP server (default = 143)
174
     *
175
     * @return mixed Returns a PEAR_Error with an error message on any
176
     *               kind of failure, or true on success.
177
     * @access public
178
     * @since  1.0
179
     */
180
    function cmdConnect($host= 'localhost', $port = 143)
181
    {
182
        if ($this->_connected) {
183
            return new PEAR_Error('already connected, logout first!');
184
        }
185
        if (PEAR::isError($error = $this->_socket->connect($host,
186
                                                           $port,
187
                                                           null,
188
                                                           $this->_timeout,
189
                                                           $this->_streamContextOptions))) {
190
            return $error;
191
        }
192
        if (PEAR::isError($this->_getRawResponse())) {
193
            return new PEAR_Error('unable to open socket');
194
        }
195
        $this->_connected = true;
196
        return true;
197
    }
198
 
199
 
200
 
201
    /**
202
     * get the cmd ID
203
     *
204
     * @return string Returns the CmdID and increment the counter
205
     *
206
     * @access private
207
     * @since  1.0
208
     */
209
    function _getCmdId()
210
    {
211
        $this->_lastCmdID = 'A000' . $this->_cmd_counter;
212
        $this->_cmd_counter++;
213
        return $this->_lastCmdID;
214
    }
215
 
216
 
217
 
218
    /**
219
     * get the last cmd ID
220
     *
221
     * @return string Returns the last cmdId
222
     *
223
     * @access public
224
     * @since  1.0
225
     */
226
    function getLastCmdId()
227
    {
228
        return $this->_lastCmdID;
229
    }
230
 
231
 
232
 
233
    /**
234
     * get current mailbox name
235
     *
236
     * @return string Returns the current mailbox
237
     *
238
     * @access public
239
     * @since  1.0
240
     */
241
    function getCurrentMailbox()
242
    {
243
        return $this->currentMailbox;
244
    }
245
 
246
 
247
 
248
    /**
249
     * Sets the debuging information on or off
250
     *
251
     * @param boolean $debug Turn debug on (true) or off (false)
252
     *
253
     * @return nothing
254
     * @access public
255
     * @since  1.0
256
     */
257
    function setDebug($debug = true)
258
    {
259
        $this->_debug = $debug;
260
    }
261
 
262
 
263
 
264
    /**
265
     * get the debug dialog
266
     *
267
     * @return string debug dialog
268
     * @access public
269
     */
270
    function getDebugDialog()
271
    {
272
        return $this->dbgDialog;
273
    }
274
 
275
 
276
 
277
    /**
278
     * Sets printed output of errors on or of
279
     *
280
     * @param boolean $printErrors true to turn on,
281
     *                             false to turn off printed output
282
     *
283
     * @return nothing
284
     * @access public
285
     * @since 1.1
286
     */
287
    function setPrintErrors($printErrors = true)
288
    {
289
        $this->_printErrors = $printErrors;
290
    }
291
 
292
 
293
 
294
    /**
295
     * Send the given string of data to the server.
296
     *
297
     * @param string $data The string of data to send.
298
     *
299
     * @return mixed True on success or a PEAR_Error object on failure.
300
     *
301
     * @access private
302
     * @since 1.0
303
     */
304
    function _send($data)
305
    {
306
        if ($this->_socket->eof()) {
307
            return new PEAR_Error('Failed to write to socket: (connection lost!)');
308
        }
309
        if (PEAR::isError($error = $this->_socket->write($data))) {
310
            return new PEAR_Error('Failed to write to socket: '
311
                                  . $error->getMessage());
312
        }
313
 
314
        if ($this->_debug) {
315
            // C: means this data was sent by  the client (this class)
316
            echo 'C: ' . $data;
317
            $this->dbgDialog .= 'C: ' . $data;
318
        }
319
        return true;
320
    }
321
 
322
    /**
323
     * Receive the given string of data from the server.
324
     *
325
     * @return mixed a line of response on success or a PEAR_Error object on failure.
326
     *
327
     * @access private
328
     * @since 1.0
329
     */
330
    function _recvLn()
331
    {
332
        if (PEAR::isError($this->lastline = $this->_socket->gets(8192))) {
333
            return new PEAR_Error('Failed to write to socket: '
334
                                  . $this->lastline->getMessage());
335
        }
336
        if ($this->_debug) {
337
            // S: means this data was sent by  the IMAP Server
338
            echo 'S: ' . $this->lastline;
339
            $this->dbgDialog .= 'S: ' . $this->lastline;
340
        }
341
        if ($this->lastline == '') {
342
            return new PEAR_Error('Failed to receive from the  socket: ');
343
        }
344
        return $this->lastline;
345
    }
346
 
347
 
348
 
349
    /**
350
     * Send a command to the server with an optional string of arguments.
351
     * A carriage return / linefeed (CRLF) sequence will be appended to each
352
     * command string before it is sent to the IMAP server.
353
     *
354
     * @param string $commandId The IMAP cmdID to send to the server.
355
     * @param string $command   The IMAP command to send to the server.
356
     * @param string $args      A string of optional arguments to append
357
     *                          to the command.
358
     *
359
     * @return mixed The result of the _send() call.
360
     *
361
     * @access private
362
     * @since 1.0
363
     */
364
    function _putCMD($commandId , $command, $args = '')
365
    {
366
        if (!empty($args)) {
367
            return $this->_send($commandId
368
                                . ' '
369
                                . $command
370
                                . ' '
371
                                . $args
372
                                . "\r\n");
373
        }
374
        return $this->_send($commandId . ' ' . $command . "\r\n");
375
    }
376
 
377
 
378
 
379
    /**
380
     * Get a response from the server with an optional string of commandID.
381
     * A carriage return / linefeed (CRLF) sequence will be appended to each
382
     * command string before it is sent to the IMAP server.
383
     *
384
     * @param string $commandId The IMAP commandid retrive from the server.
385
     *
386
     * @return string The result response.
387
     * @access private
388
     */
389
    function _getRawResponse($commandId = '*')
390
    {
391
        $arguments = '';
392
        while (!PEAR::isError($this->_recvLn())) {
393
            $reply_code = strtok($this->lastline, ' ');
394
            $arguments .= $this->lastline;
395
            if (!(strcmp($commandId, $reply_code))) {
396
                return $arguments;
397
            }
398
        }
399
        return $arguments;
400
    }
401
 
402
 
403
 
404
     /**
405
     * get the "returning of the unparsed response" feature status
406
     *
407
     * @return boolean return if the unparsed response is returned or not
408
     *
409
     * @access public
410
     * @since  1.0
411
     *
412
     */
413
    function getUnparsedResponse()
414
    {
415
        return $this->_unParsedReturn;
416
    }
417
 
418
 
419
 
420
    /**
421
     * set the options for a SSL/TLS connection
422
     * (see documentation for stream_context_create)
423
     *
424
     * @param array $options The options for the SSL/TLS connection
425
     *
426
     * @return nothing
427
     * @access public
428
     * @since  1.1
429
     */
430
    function setStreamContextOptions($options)
431
    {
432
        $this->_streamContextOptions = $options;
433
    }
434
 
435
 
436
 
437
    /**
438
     * set the the timeout for the connection to the IMAP server.
439
     *
440
     * @param int $timeout The timeout
441
     *
442
     * @return nothing
443
     * @access public
444
     * @since  1.1
445
     */
446
    function setTimeout($timeout)
447
    {
448
        $this->_timeout = $timeout;
449
    }
450
 
451
 
452
 
453
    /**
454
     * set the "returning of the unparsed response" feature on or off
455
     *
456
     * @param boolean $status true: feature is on
457
     *
458
     * @return nothing
459
     * @access public
460
     * @since  1.0
461
     */
462
    function setUnparsedResponse($status)
463
    {
464
        $this->_unParsedReturn = $status;
465
    }
466
 
467
 
468
 
469
    /**
470
     * Attempt to login to the iMAP server.
471
     *
472
     * @param string $uid The userid to authenticate as.
473
     * @param string $pwd The password to authenticate with.
474
     *
475
     * @return array Returns an array containing the response
476
     * @access public
477
     * @since  1.0
478
     */
479
    function cmdLogin($uid, $pwd)
480
    {
481
        $param = '"' . $uid . '" "'. $pwd .'"';
482
        return $this->_genericCommand('LOGIN', $param);
483
    }
484
 
485
 
486
 
487
    /**
488
     * Attempt to authenticate to the iMAP server.
489
     *
490
     * @param string $uid        The userid to authenticate as.
491
     * @param string $pwd        The password to authenticate with.
492
     * @param string $userMethod The cmdID.
493
     *
494
     * @return array Returns an array containing the response
495
     * @access public
496
     * @since  1.0
497
     */
498
    function cmdAuthenticate($uid, $pwd, $userMethod = null)
499
    {
500
        if (!$this->_connected) {
501
            return new PEAR_Error('not connected!');
502
        }
503
 
504
        $cmdid = $this->_getCmdId();
505
 
506
        if (PEAR::isError($method = $this->_getBestAuthMethod($userMethod))) {
507
            return $method;
508
        }
509
 
510
 
511
        switch ($method) {
512
        case 'DIGEST-MD5':
513
            $result = $this->_authDigestMD5($uid, $pwd, $cmdid);
514
            break;
515
        case 'CRAM-MD5':
516
            $result = $this->_authCramMD5($uid, $pwd, $cmdid);
517
            break;
518
        case 'LOGIN':
519
            $result = $this->_authLOGIN($uid, $pwd, $cmdid);
520
            break;
521
        default:
522
            $result = new PEAR_Error($method
523
                                     . ' is not a supported authentication'
524
                                     . ' method');
525
            break;
526
        }
527
 
528
        $args = $this->_getRawResponse($cmdid);
529
        return $this->_genericImapResponseParser($args, $cmdid);
530
 
531
    }
532
 
533
 
534
 
535
    /**
536
     * Authenticates the user using the DIGEST-MD5 method.
537
     *
538
     * @param string $uid   The userid to authenticate as.
539
     * @param string $pwd   The password to authenticate with.
540
     * @param string $cmdid The cmdID.
541
     *
542
     * @return array Returns an array containing the response
543
     * @access private
544
     * @since  1.0
545
     */
546
    function _authDigestMD5($uid, $pwd, $cmdid)
547
    {
548
        if (PEAR::isError($error = $this->_putCMD($cmdid,
549
                                                  'AUTHENTICATE',
550
                                                  'DIGEST-MD5'))) {
551
            return $error;
552
        }
553
 
554
        if (PEAR::isError($args = $this->_recvLn())) {
555
            return $args;
556
        }
557
 
558
        $this->_getNextToken($args, $plus);
559
        $this->_getNextToken($args, $space);
560
        $this->_getNextToken($args, $challenge);
561
 
562
        $challenge = base64_decode($challenge);
563
        $digest    = &Auth_SASL::factory('digestmd5');
564
        $auth_str  = base64_encode($digest->getResponse($uid,
565
                                                        $pwd,
566
                                                        $challenge,
567
                                                        'localhost',
568
                                                        'imap'));
569
 
570
        if (PEAR::isError($error = $this->_send($auth_str . "\r\n"))) {
571
            return $error;
572
        }
573
 
574
        if (PEAR::isError($args = $this->_recvLn())) {
575
            return $args;
576
        }
577
 
578
        // We don't use the protocol's third step because IMAP doesn't allow
579
        // subsequent authentication, so we just silently ignore it.
580
        if (PEAR::isError($error = $this->_send("\r\n"))) {
581
            return $error;
582
        }
583
    }
584
 
585
 
586
 
587
    /**
588
     * Authenticates the user using the CRAM-MD5 method.
589
     *
590
     * @param string $uid   The userid to authenticate as.
591
     * @param string $pwd   The password to authenticate with.
592
     * @param string $cmdid The cmdID.
593
     *
594
     * @return array Returns an array containing the response
595
     * @access private
596
     * @since 1.0
597
     */
598
    function _authCramMD5($uid, $pwd, $cmdid)
599
    {
600
        if (PEAR::isError($error = $this->_putCMD($cmdid,
601
                                                  'AUTHENTICATE',
602
                                                  'CRAM-MD5'))) {
603
            return $error;
604
        }
605
 
606
        if (PEAR::isError($args = $this->_recvLn())) {
607
            return $args;
608
        }
609
 
610
        $this->_getNextToken($args, $plus);
611
        $this->_getNextToken($args, $space);
612
        $this->_getNextToken($args, $challenge);
613
 
614
        $challenge = base64_decode($challenge);
615
        $cram      = &Auth_SASL::factory('crammd5');
616
        $auth_str  = base64_encode($cram->getResponse($uid,
617
                                                      $pwd,
618
                                                      $challenge));
619
 
620
        if (PEAR::isError($error = $this->_send($auth_str . "\r\n"))) {
621
            return $error;
622
        }
623
    }
624
 
625
 
626
 
627
    /**
628
     * Authenticates the user using the LOGIN method.
629
     *
630
     * @param string $uid   The userid to authenticate as.
631
     * @param string $pwd   The password to authenticate with.
632
     * @param string $cmdid The cmdID.
633
     *
634
     * @return array Returns an array containing the response
635
     * @access private
636
     * @since 1.0
637
     */
638
    function _authLOGIN($uid, $pwd, $cmdid)
639
    {
640
        if (PEAR::isError($error = $this->_putCMD($cmdid,
641
                                                  'AUTHENTICATE',
642
                                                  'LOGIN'))) {
643
            return $error;
644
        }
645
 
646
        if (PEAR::isError($args = $this->_recvLn())) {
647
            return $args;
648
        }
649
 
650
        $this->_getNextToken($args, $plus);
651
        $this->_getNextToken($args, $space);
652
        $this->_getNextToken($args, $challenge);
653
 
654
        $challenge = base64_decode($challenge);
655
        $auth_str  = base64_encode($uid);
656
 
657
        if (PEAR::isError($error = $this->_send($auth_str . "\r\n"))) {
658
            return $error;
659
        }
660
 
661
        if (PEAR::isError($args = $this->_recvLn())) {
662
            return $args;
663
        }
664
 
665
        $auth_str = base64_encode($pwd);
666
 
667
        if (PEAR::isError($error = $this->_send($auth_str . "\r\n"))) {
668
            return $error;
669
        }
670
    }
671
 
672
 
673
 
674
    /**
675
     * Returns the name of the best authentication method that the server
676
     * has advertised.
677
     *
678
     * @param string $userMethod If !=null, authenticate with this
679
     *                           method ($userMethod).
680
     *
681
     * @return mixed Returns a string containing the name of the best
682
     *               supported authentication method or a PEAR_Error object
683
     *               if a failure condition is encountered.
684
     * @access private
685
     * @since 1.0
686
     */
687
    function _getBestAuthMethod($userMethod = null)
688
    {
689
        $this->cmdCapability();
690
 
691
        if ($userMethod != null) {
692
            $methods   = array();
693
            $methods[] = $userMethod;
694
        } else {
695
            $methods = $this->supportedAuthMethods;
696
        }
697
 
698
        if (($methods != null) && ($this->_serverAuthMethods != null)) {
699
            foreach ($methods as $method) {
700
                if (in_array($method, $this->_serverAuthMethods)) {
701
                    return $method;
702
                }
703
            }
704
            $serverMethods = implode(',', $this->_serverAuthMethods);
705
            $myMethods     = implode(',', $this->supportedAuthMethods);
706
 
707
            return new PEAR_Error($method . ' NOT supported authentication'
708
                                  . ' method! This IMAP server supports these'
709
                                  . ' methods: ' . $serverMethods . ', but I'
710
                                  . ' support ' . $myMethods);
711
        } else {
712
            return new PEAR_Error('This IMAP server don\'t support any Auth'
713
                                  . ' methods');
714
        }
715
    }
716
 
717
 
718
 
719
    /**
720
     * Attempt to disconnect from the iMAP server.
721
     *
722
     * @return array Returns an array containing the response
723
     *
724
     * @access public
725
     * @since  1.0
726
     */
727
    function cmdLogout()
728
    {
729
        if (!$this->_connected) {
730
            return new PEAR_Error('not connected!');
731
        }
732
 
733
        if (PEAR::isError($args = $this->_genericCommand('LOGOUT'))) {
734
            return $args;
735
        }
736
        if (PEAR::isError($this->_socket->disconnect())) {
737
            return new PEAR_Error('socket disconnect failed');
738
        }
739
 
740
        return $args;
741
        // not for now
742
        //return $this->_genericImapResponseParser($args,$cmdid);
743
    }
744
 
745
 
746
 
747
    /**
748
     * Send the NOOP command.
749
     *
750
     * @return array Returns an array containing the response
751
     * @access public
752
     * @since  1.0
753
     */
754
    function cmdNoop()
755
    {
756
        return $this->_genericCommand('NOOP');
757
    }
758
 
759
 
760
 
761
    /**
762
     * Send the CHECK command.
763
     *
764
     * @return array Returns an array containing the response
765
     * @access public
766
     * @since  1.0
767
     */
768
    function cmdCheck()
769
    {
770
        return $this->_genericCommand('CHECK');
771
    }
772
 
773
 
774
 
775
    /**
776
     * Send the  Select Mailbox Command
777
     *
778
     * @param string $mailbox The mailbox to select.
779
     *
780
     * @return array Returns an array containing the response
781
     * @access public
782
     * @since  1.0
783
     */
784
    function cmdSelect($mailbox)
785
    {
786
        $mailbox_name = $this->_createQuotedString($mailbox);
787
        if (!PEAR::isError($ret = $this->_genericCommand('SELECT',
788
                                                         $mailbox_name))) {
789
            $this->currentMailbox = $mailbox;
790
        }
791
        return $ret;
792
    }
793
 
794
 
795
 
796
    /**
797
     * Send the  EXAMINE  Mailbox Command
798
     *
799
     * @param string $mailbox The mailbox to examine.
800
     *
801
     * @return array Returns an array containing the response
802
     * @access public
803
     * @since  1.0
804
     */
805
    function cmdExamine($mailbox)
806
    {
807
        $mailbox_name = $this->_createQuotedString($mailbox);
808
        $ret          = $this->_genericCommand('EXAMINE', $mailbox_name);
809
        $parsed       = '';
810
 
811
        if (isset($ret['PARSED'])) {
812
            for ($i=0; $i<count($ret['PARSED']); $i++) {
813
                $command               = $ret['PARSED'][$i]['EXT'];
814
                $parsed[key($command)] = $command[key($command)];
815
            }
816
        }
817
 
818
        return array('PARSED'   => $parsed,
819
                     'RESPONSE' => $ret['RESPONSE']);
820
    }
821
 
822
 
823
 
824
    /**
825
     * Send the  CREATE Mailbox Command
826
     *
827
     * @param string $mailbox The mailbox to create.
828
     * @param array  $options Options to pass to create
829
     *
830
     * @return array Returns an array containing the response
831
     * @access public
832
     * @since 1.0
833
     */
834
    function cmdCreate($mailbox, $options = null)
835
    {
836
        $args         = '';
837
        $mailbox_name = $this->_createQuotedString($mailbox);
838
        $args         = $this->_getCreateParams($options);
839
 
840
        return $this->_genericCommand('CREATE', $mailbox_name . $args);
841
    }
842
 
843
 
844
 
845
    /**
846
     * Send the  RENAME Mailbox Command
847
     *
848
     * @param string $mailbox     The old mailbox name.
849
     * @param string $new_mailbox The new (renamed) mailbox name.
850
     * @param array  $options     options to pass to create
851
     *
852
     * @return array Returns an array containing the response
853
     * @access public
854
     * @since  1.0
855
     */
856
    function cmdRename($mailbox, $new_mailbox, $options = null)
857
    {
858
        $mailbox_name     = $this->_createQuotedString($mailbox);
859
        $new_mailbox_name = $this->_createQuotedString($new_mailbox);
860
        $args             = $this->_getCreateParams($options);
861
 
862
        return $this->_genericCommand('RENAME',
863
                                      $mailbox_name . ' ' . $new_mailbox_name
864
                                      . $args);
865
    }
866
 
867
 
868
 
869
    /**
870
     * Send the  DELETE Mailbox Command
871
     *
872
     * @param string $mailbox The mailbox name to delete.
873
     *
874
     * @return array Returns an array containing the response
875
     * @access public
876
     * @since 1.0
877
     */
878
    function cmdDelete($mailbox)
879
    {
880
        $mailbox_name = $this->_createQuotedString($mailbox);
881
        return $this->_genericCommand('DELETE', $mailbox_name);
882
    }
883
 
884
 
885
 
886
    /**
887
     * Send the  SUSCRIBE  Mailbox Command
888
     *
889
     * @param string $mailbox The mailbox name to suscribe.
890
     *
891
     * @return array Returns an array containing the response
892
     * @access public
893
     * @since 1.0
894
     */
895
    function cmdSubscribe($mailbox)
896
    {
897
        $mailbox_name = $this->_createQuotedString($mailbox);
898
        return $this->_genericCommand('SUBSCRIBE', $mailbox_name);
899
    }
900
 
901
 
902
 
903
    /**
904
     * Send the  UNSUBSCRIBE  Mailbox Command
905
     *
906
     * @param string $mailbox The mailbox name to unsubscribe
907
     *
908
     * @return mixed Returns a PEAR_Error with an error message on any
909
     *               kind of failure, or true on success.
910
     * @access public
911
     * @since 1.0
912
     */
913
    function cmdUnsubscribe($mailbox)
914
    {
915
        $mailbox_name = $this->_createQuotedString($mailbox);
916
        return $this->_genericCommand('UNSUBSCRIBE', $mailbox_name);
917
    }
918
 
919
 
920
 
921
    /**
922
     * Send the  FETCH Command
923
     *
924
     * @param string $msgset     msgset
925
     * @param string $fetchparam fetchparam
926
     *
927
     * @return mixed Returns a PEAR_Error with an error message on any
928
     *               kind of failure, or true on success.
929
     * @access public
930
     * @since 1.0
931
     */
932
    function cmdFetch($msgset, $fetchparam)
933
    {
934
        return $this->_genericCommand('FETCH', $msgset . ' ' . $fetchparam);
935
    }
936
 
937
 
938
 
939
    /**
940
     * Send the  CAPABILITY Command
941
     *
942
     * @return mixed Returns a PEAR_Error with an error message on any
943
     *               kind of failure, or true on success.
944
     * @access public
945
     * @since 1.0
946
     */
947
    function cmdCapability()
948
    {
949
        $ret = $this->_genericCommand('CAPABILITY');
950
 
951
        if (isset($ret['PARSED'])) {
952
            $ret['PARSED'] = $ret['PARSED'][0]['EXT']['CAPABILITY'];
953
 
954
            // fill the $this->_serverAuthMethods
955
            // and $this->_serverSupportedCapabilities arrays
956
            foreach ($ret['PARSED']['CAPABILITIES'] as $auth_method) {
957
                if (strtoupper(substr($auth_method, 0, 5)) == 'AUTH=') {
958
                    $this->_serverAuthMethods[] = substr($auth_method, 5);
959
                }
960
            }
961
 
962
            // Keep the capabilities response to use ir later
963
            $this->_serverSupportedCapabilities = $ret['PARSED']['CAPABILITIES'];
964
        }
965
 
966
        return $ret;
967
    }
968
 
969
 
970
 
971
    /**
972
     * Send the  CAPABILITY Command
973
     *
974
     * @return mixed Returns a PEAR_Error with an error message on any
975
     *               kind of failure, or true on success.
976
     * @access public
977
     * @since 1.0
978
     */
979
    function cmdNamespace()
980
    {
981
        $ret = $this->_genericCommand('NAMESPACE');
982
 
983
        if (isset($ret['PARSED'])) {
984
            $ret['PARSED'] = $ret['PARSED'][0]['EXT']['NAMESPACE'];
985
 
986
            // Keep the namespace response for later use
987
            $this->_namespace = $ret['PARSED']['NAMESPACES'];
988
        }
989
 
990
        return $ret;
991
    }
992
 
993
 
994
 
995
    /**
996
     * Send the  STATUS Mailbox Command
997
     *
998
     * @param string $mailbox The mailbox name
999
     * @param mixed  $request The request status
1000
     *                        it could be an array or space separated string of
1001
     *                        MESSAGES | RECENT | UIDNEXT
1002
     *                        UIDVALIDITY | UNSEEN
1003
     *
1004
     * @return array Returns a Parsed Response
1005
     * @access public
1006
     * @since 1.0
1007
     */
1008
    function cmdStatus($mailbox, $request)
1009
    {
1010
        $mailbox_name = $this->_createQuotedString($mailbox);
1011
 
1012
        // make array from $request if it is none
1013
        if (!is_array($request)) {
1014
            $request = explode(' ', $request);
1015
        }
1016
 
1017
        // see RFC 3501
1018
        $valid_status_data = array('MESSAGES',
1019
                                   'RECENT',
1020
                                   'UIDNEXT',
1021
                                   'UIDVALIDITY',
1022
                                   'UNSEEN');
1023
 
1024
        foreach ($request as $status_data) {
1025
            if (!in_array($status_data, $valid_status_data)) {
1026
                $this->_protError('request "' . $status_data . '" is invalid! '
1027
                                  . 'See RFC 3501!!!!',
1028
                                  __LINE__,
1029
                                  __FILE__);
1030
            }
1031
        }
1032
 
1033
        // back to space separated string
1034
        $request = implode(' ', $request);
1035
 
1036
        $ret = $this->_genericCommand('STATUS',
1037
                                      $mailbox_name . ' (' . $request . ')');
1038
        if (isset($ret['PARSED'])) {
1039
            $ret['PARSED'] = $ret['PARSED'][count($ret['PARSED'])-1]['EXT'];
1040
        }
1041
        return $ret;
1042
    }
1043
 
1044
 
1045
 
1046
    /**
1047
     * Send the  LIST  Command
1048
     *
1049
     * @param string $mailbox_base mailbox_base
1050
     * @param string $mailbox      The mailbox name
1051
     *
1052
     * @return mixed Returns a PEAR_Error with an error message on any
1053
     *               kind of failure, or true on success.
1054
     * @access public
1055
     * @since  1.0
1056
     */
1057
    function cmdList($mailbox_base, $mailbox)
1058
    {
1059
        $mailbox_name = $this->_createQuotedString($mailbox);
1060
        $mailbox_base = $this->_createQuotedString($mailbox_base);
1061
        return $this->_genericCommand('LIST',
1062
                                      $mailbox_base . ' ' . $mailbox_name);
1063
    }
1064
 
1065
 
1066
 
1067
    /**
1068
     * Send the  LSUB  Command
1069
     *
1070
     * @param string $mailbox_base mailbox_base
1071
     * @param string $mailbox      The mailbox name
1072
     *
1073
     * @return mixed Returns a PEAR_Error with an error message on any
1074
     *               kind of failure, or true on success.
1075
     * @access public
1076
     * @since 1.0
1077
     */
1078
    function cmdLsub($mailbox_base, $mailbox)
1079
    {
1080
        $mailbox_name = $this->_createQuotedString($mailbox);
1081
        $mailbox_base = $this->_createQuotedString($mailbox_base);
1082
        return $this->_genericCommand('LSUB',
1083
                                      $mailbox_base . ' ' . $mailbox_name);
1084
    }
1085
 
1086
 
1087
 
1088
    /**
1089
     * Send the  APPEND  Command
1090
     *
1091
     * @param string $mailbox    Mailbox name
1092
     * @param string $msg        Message
1093
     * @param string $flags_list Flags list
1094
     * @param string $time       Time
1095
     *
1096
     * @return mixed Returns a PEAR_Error with an error message on any
1097
     *               kind of failure, or true on success.
1098
     * @access public
1099
     * @since 1.0
1100
     */
1101
    function cmdAppend($mailbox, $msg, $flags_list = '', $time = '')
1102
    {
1103
        if (!$this->_connected) {
1104
            return new PEAR_Error('not connected!');
1105
        }
1106
 
1107
        $cmdid    = $this->_getCmdId();
1108
        $msg_size = $this->_getLineLength($msg);
1109
 
1110
        $mailbox_name = $this->_createQuotedString($mailbox);
1111
        if ($flags_list != '') {
1112
            $flags_list = ' (' . $flags_list . ')';
1113
        }
1114
 
1115
        if ($this->hasCapability('LITERAL+') == true) {
1116
            if ($time != '') {
1117
                $timeAsString = date("d-M-Y H:i:s O", $time);
1118
                $param = sprintf("%s %s\"%s\"{%s+}\r\n%s",
1119
                                 $mailbox_name,
1120
                                 $flags_list,
1121
                                 $timeAsString,
1122
                                 $msg_size,
1123
                                 $msg);
1124
            } else {
1125
                $param = sprintf("%s%s {%s+}\r\n%s",
1126
                                 $mailbox_name,
1127
                                 $flags_list,
1128
                                 $msg_size,
1129
                                 $msg);
1130
            }
1131
            if (PEAR::isError($error = $this->_putCMD($cmdid,
1132
                                                      'APPEND',
1133
                                                      $param))) {
1134
                return $error;
1135
            }
1136
        } else {
1137
            $param = sprintf("%s%s {%s}",
1138
                             $mailbox_name,
1139
                             $flags_list,
1140
                             $msg_size);
1141
            if (PEAR::isError($error = $this->_putCMD($cmdid,
1142
                                                      'APPEND',
1143
                                                      $param))) {
1144
                return $error;
1145
            }
1146
            if (PEAR::isError($error = $this->_recvLn())) {
1147
                return $error;
1148
            }
1149
 
1150
            if (PEAR::isError($error = $this->_send($msg . "\r\n"))) {
1151
                return $error;
1152
            }
1153
        }
1154
 
1155
        $args = $this->_getRawResponse($cmdid);
1156
        $ret  = $this->_genericImapResponseParser($args, $cmdid);
1157
        return $ret;
1158
    }
1159
 
1160
 
1161
 
1162
    /**
1163
     * Send the CLOSE command.
1164
     *
1165
     * @return mixed Returns a PEAR_Error with an error message on any
1166
     *               kind of failure, or true on success.
1167
     * @access public
1168
     * @since 1.0
1169
     */
1170
    function cmdClose()
1171
    {
1172
        return $this->_genericCommand('CLOSE');
1173
    }
1174
 
1175
 
1176
 
1177
    /**
1178
     * Send the EXPUNGE command.
1179
     *
1180
     * @return mixed Returns a PEAR_Error with an error message on any
1181
     *               kind of failure, or true on success.
1182
     * @access public
1183
     * @since  1.0
1184
     */
1185
    function cmdExpunge()
1186
    {
1187
        $ret = $this->_genericCommand('EXPUNGE');
1188
 
1189
        if (isset($ret['PARSED'])) {
1190
            $parsed = $ret['PARSED'];
1191
            unset($ret["PARSED"]);
1192
            foreach ($parsed as $command) {
1193
                if (strtoupper($command['COMMAND']) == 'EXPUNGE') {
1194
                    $ret['PARSED'][$command['COMMAND']][] = $command['NRO'];
1195
                } else {
1196
                    $ret['PARSED'][$command['COMMAND']] = $command['NRO'];
1197
                }
1198
            }
1199
        }
1200
        return $ret;
1201
    }
1202
 
1203
 
1204
 
1205
    /**
1206
     * Send the SEARCH command.
1207
     *
1208
     * @param string $search_cmd Search command
1209
     *
1210
     * @return mixed Returns a PEAR_Error with an error message on any
1211
     *               kind of failure, or true on success.
1212
     * @access public
1213
     * @since 1.0
1214
     */
1215
    function cmdSearch($search_cmd)
1216
    {
1217
        /*        if($_charset != '' )
1218
                    $_charset = "[$_charset] ";
1219
                $param=sprintf("%s%s",$charset,$search_cmd);
1220
        */
1221
        $ret = $this->_genericCommand('SEARCH', $search_cmd);
1222
        if (isset($ret['PARSED'])) {
1223
            $ret['PARSED'] = $ret['PARSED'][0]['EXT'];
1224
        }
1225
        return $ret;
1226
    }
1227
 
1228
 
1229
 
1230
    /**
1231
     * Send the SORT command.
1232
     *
1233
     * @param string $sort_cmd Sort command
1234
     *
1235
     * @return mixed Returns a PEAR_Error with an error message on any
1236
     *               kind of failure, or true on success.
1237
     * @access public
1238
     * @since 1.1
1239
     */
1240
    function cmdSort($sort_cmd)
1241
    {
1242
        /*
1243
        if ($_charset != '' )
1244
            $_charset = "[$_charset] ";
1245
        $param = sprintf("%s%s",$charset,$search_cmd);
1246
        */
1247
        $ret = $this->_genericCommand('SORT', $sort_cmd);
1248
        if (isset($ret['PARSED'])) {
1249
            $ret['PARSED'] = $ret['PARSED'][0]['EXT'];
1250
        }
1251
        return $ret;
1252
    }
1253
 
1254
 
1255
 
1256
    /**
1257
     * Send the STORE command.
1258
     *
1259
     * @param string $message_set The sessage_set
1260
     * @param string $dataitem    The way we store the flags
1261
     *                            FLAGS: replace the flags whith $value
1262
     *                            FLAGS.SILENT: replace the flags whith $value
1263
     *                             but don't return untagged responses
1264
     *                            +FLAGS: Add the flags whith $value
1265
     *                            +FLAGS.SILENT: Add the flags whith $value
1266
     *                             but don't return untagged responses
1267
     *                            -FLAGS: Remove the flags whith $value
1268
     *                            -FLAGS.SILENT: Remove the flags whith $value
1269
     *                             but don't return untagged responses
1270
     * @param string $value       Value
1271
     *
1272
     * @return mixed Returns a PEAR_Error with an error message on any
1273
     *               kind of failure, or true on success.
1274
     * @access public
1275
     * @since 1.0
1276
     */
1277
    function cmdStore($message_set, $dataitem, $value)
1278
    {
1279
        /* As said in RFC2060...
1280
        C: A003 STORE 2:4 +FLAGS (\Deleted)
1281
        S: * 2 FETCH FLAGS (\Deleted \Seen)
1282
        S: * 3 FETCH FLAGS (\Deleted)
1283
        S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen)
1284
        S: A003 OK STORE completed
1285
        */
1286
        if ($dataitem != 'FLAGS'
1287
            && $dataitem != 'FLAGS.SILENT'
1288
            && $dataitem != '+FLAGS'
1289
            && $dataitem != '+FLAGS.SILENT'
1290
            && $dataitem != '-FLAGS'
1291
            && $dataitem != '-FLAGS.SILENT') {
1292
            $this->_protError('dataitem "' . $dataitem . '" is invalid! '
1293
                              . 'See RFC2060!!!!',
1294
                              __LINE__,
1295
                              __FILE__);
1296
        }
1297
        $param = sprintf("%s %s (%s)", $message_set, $dataitem, $value);
1298
        return $this->_genericCommand('STORE', $param);
1299
    }
1300
 
1301
 
1302
 
1303
    /**
1304
     * Send the COPY command.
1305
     *
1306
     * @param string $message_set Message set
1307
     * @param string $mailbox     Mailbox name
1308
     *
1309
     * @return mixed Returns a PEAR_Error with an error message on any
1310
     *               kind of failure, or true on success.
1311
     * @access public
1312
     * @since 1.0
1313
     */
1314
    function cmdCopy($message_set, $mailbox)
1315
    {
1316
        $mailbox_name = $this->_createQuotedString($mailbox);
1317
        return $this->_genericCommand('COPY',
1318
                                      sprintf("%s %s",
1319
                                              $message_set,
1320
                                              $mailbox_name));
1321
    }
1322
 
1323
 
1324
 
1325
    /**
1326
     * The UID FETH command
1327
     *
1328
     * @param string $msgset     Msgset
1329
     * @param string $fetchparam Fetchparm
1330
     *
1331
     * @return mixed Returns a PEAR_Error with an error message on any
1332
     *               kind of failure, or true on success.
1333
     * @access public
1334
     * @since 1.0
1335
     */
1336
    function cmdUidFetch($msgset, $fetchparam)
1337
    {
1338
        return $this->_genericCommand('UID FETCH',
1339
                                      sprintf("%s %s", $msgset, $fetchparam));
1340
    }
1341
 
1342
 
1343
 
1344
    /**
1345
     * The UID COPY command
1346
     *
1347
     * @param string $message_set Msgset
1348
     * @param string $mailbox     Mailbox name
1349
     *
1350
     * @return mixed Returns a PEAR_Error with an error message on any
1351
     *               kind of failure, or true on success.
1352
     * @access public
1353
     * @since 1.0
1354
     */
1355
    function cmdUidCopy($message_set, $mailbox)
1356
    {
1357
        $mailbox_name = $this->_createQuotedString($mailbox);
1358
        return $this->_genericCommand('UID COPY',
1359
                                      sprintf("%s %s",
1360
                                              $message_set,
1361
                                              $mailbox_name));
1362
    }
1363
 
1364
 
1365
 
1366
    /**
1367
     * Send the UID STORE command.
1368
     *
1369
     * @param string $message_set The sessage_set
1370
     * @param string $dataitem    The way we store the flags
1371
     *                            FLAGS: replace the flags whith $value
1372
     *                            FLAGS.SILENT: replace the flags whith $value
1373
     *                             but don't return untagged responses
1374
     *                            +FLAGS: Add the flags whith $value
1375
     *                            +FLAGS.SILENT: Add the flags whith $value
1376
     *                             but don't return untagged responses
1377
     *                            -FLAGS: Remove the flags whith $value
1378
     *                            -FLAGS.SILENT: Remove the flags whith $value
1379
     *                             but don't return untagged responses
1380
     * @param string $value       Value
1381
     *
1382
     * @return mixed Returns a PEAR_Error with an error message on any
1383
     *               kind of failure, or true on success.
1384
     * @access public
1385
     * @since  1.0
1386
     */
1387
    function cmdUidStore($message_set, $dataitem, $value)
1388
    {
1389
        /* As said in RFC2060...
1390
        C: A003 STORE 2:4 +FLAGS (\Deleted)
1391
        S: * 2 FETCH FLAGS (\Deleted \Seen)
1392
        S: * 3 FETCH FLAGS (\Deleted)
1393
        S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen)
1394
        S: A003 OK STORE completed
1395
        */
1396
        if ($dataitem != 'FLAGS'
1397
            && $dataitem != 'FLAGS.SILENT'
1398
            && $dataitem != '+FLAGS'
1399
            && $dataitem != '+FLAGS.SILENT'
1400
            && $dataitem != '-FLAGS'
1401
            && $dataitem != '-FLAGS.SILENT') {
1402
                $this->_protError('dataitem "' . $dataitem . '" is invalid! '
1403
                                  . 'See RFC2060!!!!',
1404
                                  __LINE__,
1405
                                  __FILE__);
1406
        }
1407
 
1408
        return $this->_genericCommand('UID STORE',
1409
                                      sprintf("%s %s (%s)",
1410
                                              $message_set,
1411
                                              $dataitem,
1412
                                              $value));
1413
    }
1414
 
1415
 
1416
 
1417
    /**
1418
     * Send the SEARCH command.
1419
     *
1420
     * @param string $search_cmd Search command
1421
     *
1422
     * @return mixed Returns a PEAR_Error with an error message on any
1423
     *               kind of failure, or true on success.
1424
     * @access public
1425
     * @since 1.0
1426
     */
1427
    function cmdUidSearch($search_cmd)
1428
    {
1429
        $ret = $this->_genericCommand('UID SEARCH',
1430
                                      sprintf("%s", $search_cmd));
1431
        if (isset($ret['PARSED'])) {
1432
            $ret['PARSED'] = $ret['PARSED'][0]['EXT'];
1433
        }
1434
        return $ret;
1435
    }
1436
 
1437
 
1438
 
1439
    /**
1440
     * Send the UID SORT command.
1441
     *
1442
     * @param string $sort_cmd Sort command
1443
     *
1444
     * @return mixed Returns a PEAR_Error with an error message on any
1445
     *               kind of failure, or true on success.
1446
     * @access public
1447
     * @since 1.1
1448
     */
1449
    function cmdUidSort($sort_cmd)
1450
    {
1451
        $ret = $this->_genericCommand('UID SORT', sprintf("%s", $sort_cmd));
1452
        if (isset($ret['PARSED'])) {
1453
            $ret['PARSED'] = $ret['PARSED'][0]['EXT'];
1454
        }
1455
        return $ret;
1456
    }
1457
 
1458
 
1459
 
1460
    /**
1461
     * Send the X command.
1462
     *
1463
     * @param string $atom       Atom
1464
     * @param string $parameters Parameters
1465
     *
1466
     * @return mixed Returns a PEAR_Error with an error message on any
1467
     *               kind of failure, or true on success.
1468
     * @access public
1469
     * @since 1.0
1470
     */
1471
    function cmdX($atom, $parameters)
1472
    {
1473
        return $this->_genericCommand('X' . $atom, $parameters);
1474
    }
1475
 
1476
 
1477
 
1478
    /********************************************************************
1479
    ***
1480
    **             HERE ENDS the RFC2060 IMAPS FUNCTIONS
1481
    **             AND BEGIN THE EXTENSIONS FUNCTIONS
1482
    **
1483
    *******************************************************************/
1484
 
1485
 
1486
 
1487
    /*******************************************************************
1488
    **             RFC2087 IMAP4 QUOTA extension BEGINS HERE
1489
    *******************************************************************/
1490
 
1491
    /**
1492
     * Send the GETQUOTA command.
1493
     *
1494
     * @param string $mailbox_name The mailbox name to query for quota data
1495
     *
1496
     * @return mixed Returns a PEAR_Error with an error message on any
1497
     *               kind of failure, or quota data on success
1498
     * @access public
1499
     * @since 1.0
1500
     */
1501
    function cmdGetQuota($mailbox_name)
1502
    {
1503
        //Check if the IMAP server has QUOTA support
1504
        if (!$this->hasQuotaSupport()) {
1505
            return new PEAR_Error('This IMAP server doen\'t support QUOTA\'s!');
1506
        }
1507
 
1508
        $mailbox_name = sprintf("%s", $this->utf7Encode($mailbox_name));
1509
        $ret          = $this->_genericCommand('GETQUOTA', $mailbox_name);
1510
 
1511
        if (isset($ret['PARSED'])) {
1512
            // remove the array index because the quota response returns
1513
            // only 1 line of output
1514
            $ret['PARSED'] = $ret['PARSED'][0];
1515
        }
1516
        return $ret;
1517
    }
1518
 
1519
 
1520
 
1521
    /**
1522
     * Send the GETQUOTAROOT command.
1523
     *
1524
     * @param string $mailbox_name The ailbox name to query for quota data
1525
     *
1526
     * @return mixed Returns a PEAR_Error with an error message on any
1527
     *               kind of failure, or quota data on success
1528
     * @access public
1529
     * @since 1.0
1530
     */
1531
    function cmdGetQuotaRoot($mailbox_name)
1532
    {
1533
        //Check if the IMAP server has QUOTA support
1534
        if (!$this->hasQuotaSupport()) {
1535
            return new PEAR_Error('This IMAP server doesn\'t support QUOTA\'s!');
1536
        }
1537
 
1538
        $mailbox_name = sprintf("%s", $this->utf7Encode($mailbox_name));
1539
        $ret          = $this->_genericCommand('GETQUOTAROOT', $mailbox_name);
1540
 
1541
        if (isset($ret['PARSED'])) {
1542
            // remove the array index because the quota response returns
1543
            // only 1 line of output
1544
            $ret['PARSED'] = $ret['PARSED'][1];
1545
        }
1546
        return $ret;
1547
    }
1548
 
1549
 
1550
 
1551
    /**
1552
     * Send the SETQUOTA command.
1553
     *
1554
     * @param string $mailbox_name  The mailbox name to query for quota data
1555
     * @param string $storageQuota  Sets the max number of bytes this mailbox
1556
     *                              can handle
1557
     * @param string $messagesQuota Sets the max number of messages this
1558
     *                              mailbox can handle
1559
     *
1560
     * @return mixed Returns a PEAR_Error with an error message on any
1561
     *               kind of failure, or quota data on success
1562
     * @access public
1563
     * @since 1.0
1564
     */
1565
    function cmdSetQuota($mailbox_name,
1566
                         $storageQuota = null,
1567
                         $messagesQuota = null)
1568
    {
1569
        // ToDo:  implement the quota by number of emails!!
1570
 
1571
        //Check if the IMAP server has QUOTA support
1572
        if (!$this->hasQuotaSupport()) {
1573
            return new PEAR_Error('This IMAP server doesn\'t support QUOTA\'s!');
1574
        }
1575
 
1576
        if (($messagesQuota == null) && ($storageQuota == null)) {
1577
            return new PEAR_Error('$storageQuota and $messagesQuota parameters '
1578
                                  . 'can\'t be both null if you want to use '
1579
                                  . 'quota');
1580
        }
1581
 
1582
        $mailbox_name = $this->_createQuotedString($mailbox_name);
1583
        //Make the command request
1584
        $param = sprintf("%s (", $mailbox_name);
1585
 
1586
        if ($storageQuota != null) {
1587
            if ($storageQuota == -1) {
1588
                // set -1 to remove a quota
1589
                $param = sprintf("%s", $param);
1590
            } elseif ($storageQuota == strtolower('remove')) {
1591
                // this is a cyrus rmquota specific feature
1592
                // see http://email.uoa.gr/projects/cyrus/quota-patches/rmquota/
1593
                $param = sprintf("%sREMOVE 1", $param);
1594
            } else {
1595
                $param = sprintf("%sSTORAGE %s", $param, $storageQuota);
1596
            }
1597
 
1598
            if ($messagesQuota != null) {
1599
                // if we have both types of quota on the same call we must
1600
                // append an space between those parameters
1601
                $param = sprintf("%s ", $param);
1602
            }
1603
        }
1604
        if ($messagesQuota != null) {
1605
            $param = sprintf("%sMESSAGES %s", $param, $messagesQuota);
1606
        }
1607
        $param = sprintf("%s)", $param);
1608
 
1609
        return $this->_genericCommand('SETQUOTA', $param);
1610
    }
1611
 
1612
 
1613
 
1614
    /**
1615
     * Send the SETQUOTAROOT command.
1616
     *
1617
     * @param string $mailbox_name  The mailbox name to query for quota data
1618
     * @param string $storageQuota  Sets the max number of bytes this mailbox
1619
     *                              can handle
1620
     * @param string $messagesQuota Sets the max number of messages this
1621
     *                              mailbox can handle
1622
     *
1623
     * @return mixed Returns a PEAR_Error with an error message on any
1624
     *               kind of failure, or quota data on success
1625
     * @access public
1626
     * @since 1.0
1627
     */
1628
    function cmdSetQuotaRoot($mailbox_name,
1629
                             $storageQuota = null,
1630
                             $messagesQuota = null)
1631
    {
1632
        //Check if the IMAP server has QUOTA support
1633
        if (!$this->hasQuotaSupport()) {
1634
            return new PEAR_Error('This IMAP server doesn\'t support QUOTA\'s!');
1635
        }
1636
 
1637
        if (($messagesQuota == null) && ($storageQuota == null)) {
1638
            return new PEAR_Error('$storageQuota and $messagesQuota parameters '
1639
                                  . 'can\'t be both null if you want to use '
1640
                                  . 'quota');
1641
        }
1642
 
1643
        $mailbox_name = $this->_createQuotedString($mailbox_name);
1644
        //Make the command request
1645
        $param = sprintf("%s (", $mailbox_name);
1646
 
1647
        if ($storageQuota != null) {
1648
            $param = sprintf("%sSTORAGE %s", $param, $storageQuota);
1649
            if ($messagesQuota != null) {
1650
                // if we have both types of quota on the same call we must
1651
                // append an space between those parameters
1652
                $param = sprintf("%s ", $param);
1653
            }
1654
        }
1655
 
1656
        if ($messagesQuota != null) {
1657
            $param = sprintf("%sMESSAGES %s", $param, $messagesQuota);
1658
        }
1659
        $param = sprintf("%s)", $param);
1660
 
1661
        return $this->_genericCommand('SETQUOTAROOT', $param);
1662
    }
1663
 
1664
 
1665
 
1666
    /********************************************************************
1667
    ***             RFC2087 IMAP4 QUOTA extension ENDS HERE
1668
    ********************************************************************/
1669
 
1670
 
1671
 
1672
    /********************************************************************
1673
    ***             RFC2086 IMAP4 ACL extension BEGINS HERE
1674
    ********************************************************************/
1675
 
1676
    /**
1677
     * Send the SETACL command.
1678
     *
1679
     * @param string $mailbox_name Mailbox name
1680
     * @param string $user         User
1681
     * @param string $acl          ACL string
1682
     *
1683
     * @return mixed Returns a PEAR_Error with an error message on any
1684
     *               kind of failure, or true on success
1685
     * @access public
1686
     * @since 1.0
1687
     */
1688
    function cmdSetACL($mailbox_name, $user, $acl)
1689
    {
1690
        //Check if the IMAP server has ACL support
1691
        if (!$this->hasAclSupport()) {
1692
            return new PEAR_Error('This IMAP server does not support ACL\'s!');
1693
        }
1694
 
1695
        $mailbox_name = $this->_createQuotedString($mailbox_name);
1696
        $user_name    = $this->_createQuotedString($user);
1697
 
1698
        if (is_array($acl)) {
1699
            $acl = implode('', $acl);
1700
        }
1701
 
1702
        return $this->_genericCommand('SETACL',
1703
                                      sprintf("%s %s \"%s\"",
1704
                                              $mailbox_name,
1705
                                              $user_name,
1706
                                              $acl));
1707
    }
1708
 
1709
 
1710
 
1711
    /**
1712
     * Send the DELETEACL command.
1713
     *
1714
     * @param string $mailbox_name Mailbox name
1715
     * @param string $user         User
1716
     *
1717
     * @return mixed Returns a PEAR_Error with an error message on any
1718
     *               kind of failure, or true on success
1719
     * @access public
1720
     * @since 1.0
1721
     */
1722
    function cmdDeleteACL($mailbox_name, $user)
1723
    {
1724
        //Check if the IMAP server has ACL support
1725
        if (!$this->hasAclSupport()) {
1726
            return new PEAR_Error('This IMAP server does not support ACL\'s!');
1727
        }
1728
 
1729
        $mailbox_name = $this->_createQuotedString($mailbox_name);
1730
 
1731
        return $this->_genericCommand('DELETEACL',
1732
                                      sprintf("%s \"%s\"",
1733
                                              $mailbox_name,
1734
                                              $user));
1735
    }
1736
 
1737
 
1738
 
1739
    /**
1740
     * Send the GETACL command.
1741
     *
1742
     * @param string $mailbox_name Mailbox name
1743
     *
1744
     * @return mixed Returns a PEAR_Error with an error message on any
1745
     *               kind of failure, or ACL list on success
1746
     * @access public
1747
     * @since 1.0
1748
     */
1749
    function cmdGetACL($mailbox_name)
1750
    {
1751
        //Check if the IMAP server has ACL support
1752
        if (!$this->hasAclSupport()) {
1753
            return new PEAR_Error('This IMAP server does not support ACL\'s!');
1754
        }
1755
 
1756
        $mailbox_name = $this->_createQuotedString($mailbox_name);
1757
        $ret          = $this->_genericCommand('GETACL',
1758
                                               sprintf("%s", $mailbox_name));
1759
 
1760
        if (isset($ret['PARSED'])) {
1761
            $ret['PARSED'] = $ret['PARSED'][0]['EXT'];
1762
        }
1763
        return $ret;
1764
    }
1765
 
1766
 
1767
 
1768
    /**
1769
     * Send the LISTRIGHTS command.
1770
     *
1771
     * @param string $mailbox_name Mailbox name
1772
     * @param string $user         User
1773
     *
1774
     * @return mixed Returns a PEAR_Error with an error message on any
1775
     *               kind of failure, or list of users rights
1776
     * @access public
1777
     * @since 1.0
1778
     */
1779
    function cmdListRights($mailbox_name, $user)
1780
    {
1781
        //Check if the IMAP server has ACL support
1782
        if (!$this->hasAclSupport()) {
1783
            return new PEAR_Error('This IMAP server does not support ACL\'s!');
1784
        }
1785
 
1786
        $mailbox_name = $this->_createQuotedString($mailbox_name);
1787
        $ret          = $this->_genericCommand('LISTRIGHTS',
1788
                                               sprintf("%s \"%s\"",
1789
                                                       $mailbox_name,
1790
                                                       $user));
1791
        if (isset($ret['PARSED'])) {
1792
            $ret['PARSED'] = $ret['PARSED'][0]['EXT'];
1793
        }
1794
        return $ret;
1795
    }
1796
 
1797
 
1798
 
1799
    /**
1800
     * Send the MYRIGHTS command.
1801
     *
1802
     * @param string $mailbox_name Mailbox name
1803
     *
1804
     * @return mixed Returns a PEAR_Error with an error message on any
1805
     *               kind of failure, or MYRIGHTS response on success
1806
     * @access public
1807
     * @since 1.0
1808
     */
1809
    function cmdMyRights($mailbox_name)
1810
    {
1811
        // Check if the IMAP server has ACL support
1812
        if (!$this->hasAclSupport()) {
1813
            return new PEAR_Error('This IMAP server does not support ACL\'s!');
1814
        }
1815
 
1816
        $mailbox_name = $this->_createQuotedString($mailbox_name);
1817
        $ret          = $this->_genericCommand('MYRIGHTS',
1818
                                               sprintf("%s", $mailbox_name));
1819
        if (isset($ret['PARSED'])) {
1820
            $ret['PARSED'] = $ret['PARSED'][0]['EXT'];
1821
        }
1822
        return $ret;
1823
    }
1824
 
1825
 
1826
 
1827
    /********************************************************************
1828
    ***             RFC2086 IMAP4 ACL extension ENDs HERE
1829
    ********************************************************************/
1830
 
1831
 
1832
    /********************************************************************
1833
    ***  draft-daboo-imap-annotatemore-05 IMAP4 ANNOTATEMORE extension
1834
    ***  BEGINS HERE
1835
    ********************************************************************/
1836
 
1837
    /**
1838
     * Send the SETANNOTATION command.
1839
     *
1840
     * @param string $mailboxName Mailbox name
1841
     * @param string $entry       Entry
1842
     * @param string $values      Value
1843
     *
1844
     * @return mixed Returns a PEAR_Error with an error message on any
1845
     *               kind of failure, or true on success
1846
     * @access public
1847
     * @since 1.0
1848
     */
1849
    function cmdSetAnnotation($mailboxName, $entry, $values)
1850
    {
1851
        // Check if the IMAP server has ANNOTATEMORE support
1852
        if (!$this->hasAnnotateMoreSupport()) {
1853
            return new PEAR_Error('This IMAP server does not support the '
1854
                                  . 'ANNOTATEMORE extension!');
1855
        }
1856
 
1857
        if (!is_array($values)) {
1858
            return new PEAR_Error('Invalid $values argument passed to '
1859
                                  . 'cmdSetAnnotation');
1860
        }
1861
 
1862
        $mailboxName = $this->_createQuotedString($mailboxName);
1863
 
1864
        $vallist = '';
1865
        foreach ($values as $name => $value) {
1866
            $vallist .= '"' . $name . '" "' . $value . '"';
1867
        }
1868
        $vallist = rtrim($vallist);
1869
 
1870
        return $this->_genericCommand('SETANNOTATION',
1871
                                      sprintf('%s "%s" (%s)',
1872
                                              $mailboxName,
1873
                                              $entry,
1874
                                              $vallist));
1875
    }
1876
 
1877
 
1878
 
1879
    /**
1880
     * Send the DELETEANNOTATION command.
1881
     *
1882
     * @param string $mailboxName Mailbox name
1883
     * @param string $entry       Entry
1884
     * @param string $values      Value
1885
     *
1886
     * @return mixed Returns a PEAR_Error with an error message on any
1887
     *               kind of failure, or true on success
1888
     * @access public
1889
     * @since 1.0
1890
     */
1891
    function cmdDeleteAnnotation($mailboxName, $entry, $values)
1892
    {
1893
        // Check if the IMAP server has ANNOTATEMORE support
1894
        if (!$this->hasAnnotateMoreSupport()) {
1895
            return new PEAR_Error('This IMAP server does not support the '
1896
                                  . 'ANNOTATEMORE extension!');
1897
        }
1898
 
1899
        if (!is_array($values)) {
1900
            return new PEAR_Error('Invalid $values argument passed to '
1901
                                  . 'cmdDeleteAnnotation');
1902
        }
1903
 
1904
        $mailboxName = $this->_createQuotedString($mailboxName);
1905
 
1906
        $vallist = '';
1907
        foreach ($values as $name) {
1908
            $vallist .= '"' . $name . '" NIL';
1909
        }
1910
        $vallist = rtrim($vallist);
1911
 
1912
        return $this->_genericCommand('SETANNOTATION',
1913
                                      sprintf('%s "%s" (%s)',
1914
                                              $mailboxName,
1915
                                              $entry,
1916
                                              $vallist));
1917
    }
1918
 
1919
 
1920
 
1921
    /**
1922
     * Send the GETANNOTATION command.
1923
     *
1924
     * @param string $mailboxName Mailbox name
1925
     * @param string $entries     Entries
1926
     * @param string $values      Value
1927
     *
1928
     * @return mixed Returns a PEAR_Error with an error message on any
1929
     *               kind of failure, or GETANNOTATION result on success
1930
     * @access public
1931
     * @since 1.0
1932
     */
1933
    function cmdGetAnnotation($mailboxName, $entries, $values)
1934
    {
1935
        // Check if the IMAP server has ANNOTATEMORE support
1936
        if (!$this->hasAnnotateMoreSupport()) {
1937
            return new PEAR_Error('This IMAP server does not support the '
1938
                                  . 'ANNOTATEMORE extension!');
1939
        }
1940
 
1941
        $entlist = '';
1942
 
1943
        if (!is_array($entries)) {
1944
            $entries = array($entries);
1945
        }
1946
 
1947
        foreach ($entries as $name) {
1948
            $entlist .= '"' . $name . '"';
1949
        }
1950
        $entlist = rtrim($entlist);
1951
        if (count($entries) > 1) {
1952
            $entlist = '(' . $entlist . ')';
1953
        }
1954
 
1955
        $vallist = '';
1956
        if (!is_array($values)) {
1957
            $values = array($values);
1958
        }
1959
 
1960
        foreach ($values as $name) {
1961
            $vallist .= '"' . $name . '"';
1962
        }
1963
        $vallist = rtrim($vallist);
1964
        if (count($values) > 1) {
1965
            $vallist = '(' . $vallist . ')';
1966
        }
1967
 
1968
        $mailboxName = $this->_createQuotedString($mailboxName);
1969
 
1970
        return $this->_genericCommand('GETANNOTATION',
1971
                                      sprintf('%s %s %s',
1972
                                              $mailboxName,
1973
                                              $entlist,
1974
                                              $vallist));
1975
    }
1976
 
1977
 
1978
    /***********************************************************************
1979
    ***  draft-daboo-imap-annotatemore-05 IMAP4 ANNOTATEMORE extension
1980
    ***  ENDs HERE
1981
    ************************************************************************/
1982
 
1983
 
1984
    /********************************************************************
1985
    ***
1986
    ***             HERE ENDS THE EXTENSIONS FUNCTIONS
1987
    ***             AND BEGIN THE AUXILIARY FUNCTIONS
1988
    ***
1989
    ********************************************************************/
1990
 
1991
    /**
1992
     * tell if the server has capability $capability
1993
     *
1994
     * @return true or false
1995
     * @access public
1996
     * @since 1.0
1997
     */
1998
    function getServerAuthMethods()
1999
    {
2000
        if ($this->_serverAuthMethods == null) {
2001
            $this->cmdCapability();
2002
            return $this->_serverAuthMethods;
2003
        }
2004
        return false;
2005
    }
2006
 
2007
 
2008
 
2009
    /**
2010
     * tell if the server has capability $capability
2011
     *
2012
     * @param string $capability Capability
2013
     *
2014
     * @return true or false
2015
     * @access public
2016
     * @since 1.0
2017
     */
2018
    function hasCapability($capability)
2019
    {
2020
        if ($this->_serverSupportedCapabilities == null) {
2021
            $this->cmdCapability();
2022
        }
2023
        if ($this->_serverSupportedCapabilities != null) {
2024
            if (in_array($capability, $this->_serverSupportedCapabilities)) {
2025
                return true;
2026
            }
2027
        }
2028
        return false;
2029
    }
2030
 
2031
 
2032
 
2033
    /**
2034
     * tell if the server has Quota support
2035
     *
2036
     * @return true or false
2037
     * @access public
2038
     * @since 1.0
2039
     */
2040
    function hasQuotaSupport()
2041
    {
2042
        return $this->hasCapability('QUOTA');
2043
    }
2044
 
2045
 
2046
 
2047
    /**
2048
     * tell if the server has Quota support
2049
     *
2050
     * @return true or false
2051
     * @access public
2052
     * @since 1.0
2053
     */
2054
    function hasAclSupport()
2055
    {
2056
        return $this->hasCapability('ACL');
2057
    }
2058
 
2059
 
2060
 
2061
    /**
2062
     * tell if the server has support for the ANNOTATEMORE extension
2063
     *
2064
     * @return true or false
2065
     * @access public
2066
     * @since 1.0
2067
     */
2068
    function hasAnnotateMoreSupport()
2069
    {
2070
        return $this->hasCapability('ANNOTATEMORE');
2071
    }
2072
 
2073
 
2074
    /**
2075
     * Create a quoted string
2076
     *
2077
     * @param string $str String
2078
     *
2079
     * @return string Quoted $str
2080
     * @access public
2081
     * @since 1.0
2082
     */
2083
    function _createQuotedString($str)
2084
    {
2085
        $search  = array('\\', '"');
2086
        $replace = array('\\\\', '\\"');
2087
 
2088
        $mailbox_name = str_replace($search, $replace, $str);
2089
        $mailbox_name = sprintf("\"%s\"", $this->utf7Encode($mailbox_name));
2090
 
2091
        return $mailbox_name;
2092
    }
2093
 
2094
 
2095
 
2096
    /**
2097
     * Parses the responses like RFC822.SIZE and INTERNALDATE
2098
     *
2099
     * @param string &$str The IMAP's server response
2100
     * @param int    $line Line number
2101
     * @param string $file File
2102
     *
2103
     * @return string next token
2104
     * @access private
2105
     * @since 1.0
2106
     */
2107
    function _parseOneStringResponse(&$str, $line, $file)
2108
    {
2109
        $this->_parseSpace($str, $line, $file);
2110
        $size = $this->_getNextToken($str, $uid);
2111
        return $uid;
2112
    }
2113
 
2114
 
2115
 
2116
    /**
2117
     * Parses the FLAG response
2118
     *
2119
     * @param string &$str The IMAP's server response
2120
     *
2121
     * @return Array containing  the parsed  response
2122
     * @access private
2123
     * @since 1.0
2124
     */
2125
    function _parseFLAGSresponse(&$str)
2126
    {
2127
        $this->_parseSpace($str, __LINE__, __FILE__);
2128
        $params_arr[] = $this->_arrayfyContent($str);
2129
        $flags_arr    = array();
2130
        for ($i = 0; $i < count($params_arr[0]); $i++) {
2131
            $flags_arr[] = $params_arr[0][$i];
2132
        }
2133
        return $flags_arr;
2134
    }
2135
 
2136
 
2137
 
2138
    /**
2139
     * Parses the BODY response
2140
     *
2141
     * @param string &$str    The IMAP's server response
2142
     * @param string $command Command
2143
     *
2144
     * @return array The parsed response
2145
     * @access private
2146
     * @since 1.0
2147
     */
2148
    function _parseBodyResponse(&$str, $command)
2149
    {
2150
        $this->_parseSpace($str, __LINE__, __FILE__);
2151
        while ($str[0] != ')' && $str != '') {
2152
            $params_arr[] = $this->_arrayfyContent($str);
2153
        }
2154
 
2155
        return $params_arr;
2156
    }
2157
 
2158
 
2159
 
2160
    /**
2161
     * Makes the content an Array
2162
     *
2163
     * @param string &$str The IMAP's server response
2164
     *
2165
     * @return array The parsed response
2166
     * @access private
2167
     * @since 1.0
2168
     */
2169
    function _arrayfyContent(&$str)
2170
    {
2171
        $params_arr = array();
2172
        $this->_getNextToken($str, $params);
2173
        if ($params != '(') {
2174
            return $params;
2175
        }
2176
        $this->_getNextToken($str, $params, false, false);
2177
        while ($str != '' && $params != ')') {
2178
            if ($params != '') {
2179
                if ($params[0] == '(') {
2180
                    $params = $this->_arrayfyContent($params);
2181
                }
2182
                if ($params != ' ') {
2183
                    // I don't remove the colons (") to handle the case of
2184
                    // retriving " "
2185
                    // If I remove the colons the parser will interpret this
2186
                    // field as an imap separator (space) instead of a valid
2187
                    // field so I remove the colons here
2188
                    if ($params == '""') {
2189
                        $params = '';
2190
                    } else {
2191
                        if ($params[0] == '"') {
2192
                            $params = $this->_getSubstr($params,
2193
                                                        1,
2194
                                                        $this->_getLineLength($params)-2);
2195
                        }
2196
                    }
2197
                    $params_arr[] = $params;
2198
                }
2199
            } else {
2200
                // if params if empty (for example i'm parsing 2 quotes ("")
2201
                // I'll append an array entry to mantain compatibility
2202
                $params_arr[] = $params;
2203
            }
2204
            $this->_getNextToken($str, $params, false, false);
2205
        }
2206
        $this->arrayfy_content_level--;
2207
        return $params_arr;
2208
    }
2209
 
2210
 
2211
 
2212
    /**
2213
     * Parses the BODY[],BODY[TEXT],.... responses
2214
     *
2215
     * @param string &$str    The IMAP's server response
2216
     * @param string $command Command
2217
     *
2218
     * @return array The parsed response
2219
     * @access private
2220
     * @since 1.0
2221
    */
2222
    function _parseContentresponse(&$str, $command)
2223
    {
2224
        $content = '';
2225
        $this->_parseSpace($str, __LINE__, __FILE__);
2226
        $size = $this->_getNextToken($str, $content);
2227
        return array('CONTENT' => $content, 'CONTENT_SIZE' => $size);
2228
    }
2229
 
2230
 
2231
 
2232
    /**
2233
     * Parses the ENVELOPE response
2234
     *
2235
     * @param string &$str The IMAP's server response
2236
     *
2237
     * @return array The parsed response
2238
     * @access private
2239
     * @since 1.0
2240
     */
2241
    function _parseENVELOPEresponse(&$str)
2242
    {
2243
        $content = '';
2244
        $this->_parseSpace($str, __LINE__, __FILE__);
2245
 
2246
        $this->_getNextToken($str, $parenthesis);
2247
        if ($parenthesis != '(') {
2248
            $this->_protError('must be a "(" but is a "' . $parenthesis .'" '
2249
                              . '!!!!',
2250
                              __LINE__,
2251
                              __FILE__);
2252
        }
2253
        // Get the email's Date
2254
        $this->_getNextToken($str, $date);
2255
 
2256
        $this->_parseSpace($str, __LINE__, __FILE__);
2257
 
2258
        // Get the email's Subject:
2259
        $this->_getNextToken($str, $subject);
2260
        //$subject = $this->decode($subject);
2261
 
2262
        $this->_parseSpace($str, __LINE__, __FILE__);
2263
 
2264
        //FROM LIST;
2265
        $from_arr = $this->_getAddressList($str);
2266
 
2267
        $this->_parseSpace($str, __LINE__, __FILE__);
2268
 
2269
        //"SENDER LIST\n";
2270
        $sender_arr = $this->_getAddressList($str);
2271
 
2272
        $this->_parseSpace($str, __LINE__, __FILE__);
2273
 
2274
        //"REPLY-TO LIST\n";
2275
        $reply_to_arr = $this->_getAddressList($str);
2276
 
2277
        $this->_parseSpace($str, __LINE__, __FILE__);
2278
 
2279
        //"TO LIST\n";
2280
        $to_arr = $this->_getAddressList($str);
2281
 
2282
        $this->_parseSpace($str, __LINE__, __FILE__);
2283
 
2284
        //"CC LIST\n";
2285
        $cc_arr = $this->_getAddressList($str);
2286
 
2287
        $this->_parseSpace($str, __LINE__, __FILE__);
2288
 
2289
        //"BCC LIST|$str|\n";
2290
        $bcc_arr = $this->_getAddressList($str);
2291
 
2292
        $this->_parseSpace($str, __LINE__, __FILE__);
2293
 
2294
        $this->_getNextToken($str, $in_reply_to);
2295
 
2296
        $this->_parseSpace($str, __LINE__, __FILE__);
2297
 
2298
        $this->_getNextToken($str, $message_id);
2299
 
2300
        $this->_getNextToken($str, $parenthesis);
2301
 
2302
        if ($parenthesis != ')') {
2303
            $this->_protError('must be a ")" but is a "' . $parenthesis .'" '
2304
                              . '!!!!',
2305
                              __LINE__,
2306
                              __FILE__);
2307
        }
2308
 
2309
        return array('DATE'        => $date,
2310
                     'SUBJECT'     => $subject,
2311
                     'FROM'        => $from_arr,
2312
                     'SENDER'      => $sender_arr,
2313
                     'REPLY_TO'    => $reply_to_arr,
2314
                     'TO'          => $to_arr,
2315
                     'CC'          => $cc_arr,
2316
                     'BCC'         => $bcc_arr,
2317
                     'IN_REPLY_TO' => $in_reply_to,
2318
                     'MESSAGE_ID'  => $message_id);
2319
    }
2320
 
2321
 
2322
 
2323
    /**
2324
     * Parses the ARRDLIST as defined in RFC
2325
     *
2326
     * @param string &$str The IMAP's server response
2327
     *
2328
     * @return array The parsed response
2329
     * @access private
2330
     * @since 1.0
2331
     */
2332
    function _getAddressList(&$str)
2333
    {
2334
        $params_arr = $this->_arrayfyContent($str);
2335
        if (!isset($params_arr)) {
2336
            return $params_arr;
2337
        }
2338
 
2339
        if (is_array($params_arr)) {
2340
            foreach ($params_arr as $index => $address_arr) {
2341
                $personal_name  = $address_arr[0];
2342
                $at_domain_list = $address_arr[1];
2343
                $mailbox_name   = $address_arr[2];
2344
                $host_name      = $address_arr[3];
2345
                if ($mailbox_name != '' && $host_name != '') {
2346
                    $email = $mailbox_name . "@" . $host_name;
2347
                } else {
2348
                    $email = false;
2349
                }
2350
                if ($email == false) {
2351
                    $rfc822_email = false;
2352
                } else {
2353
                    if (!isset($personal_name)) {
2354
                        $rfc822_email = '<' . $email . '>';
2355
                    } else {
2356
                        $rfc822_email = '"' . $personal_name . '" <'
2357
                                        . $email . '>';
2358
                    }
2359
                }
2360
                $email_arr[] = array('PERSONAL_NAME'  => $personal_name,
2361
                                     'AT_DOMAIN_LIST' => $at_domain_list,
2362
                                     'MAILBOX_NAME'   => $this->utf7Decode($mailbox_name),
2363
                                     'HOST_NAME'      => $host_name,
2364
                                     'EMAIL'          => $email ,
2365
                                     'RFC822_EMAIL'   => $rfc822_email );
2366
            }
2367
            return $email_arr;
2368
        }
2369
        return array();
2370
    }
2371
 
2372
 
2373
 
2374
    /**
2375
     * Utility funcion to find the closing parenthesis ")" Position it takes
2376
     * care of quoted ones
2377
     *
2378
     * @param string $str_line   String
2379
     * @param string $startDelim Start delimiter
2380
     * @param string $stopDelim  Stop delimiter
2381
     *
2382
     * @return int the pos of the closing parenthesis ")"
2383
     * @access private
2384
     * @since 1.0
2385
    */
2386
    function _getClosingBracesPos($str_line,
2387
                                  $startDelim = '(',
2388
                                  $stopDelim = ')')
2389
    {
2390
        $len = $this->_getLineLength($str_line);
2391
        $pos = 0;
2392
        // ignore all extra characters
2393
        // If inside of a string, skip string -- Boundary IDs and other
2394
        // things can have ) in them.
2395
        if ($str_line[$pos] != $startDelim) {
2396
            $this->_protError('_getClosingParenthesisPos: must start with a '
2397
                              . '"' . $startDelim . '" but is a '
2398
                              . '"' . $str_line[$pos] . '"!!!!'
2399
                              . 'STR_LINE: ' . $str_line
2400
                              . ' |size: ' . $len
2401
                              . ' |POS: ' . $pos,
2402
                              __LINE__,
2403
                              __FILE__);
2404
            return( $len );
2405
        }
2406
        for ($pos = 1; $pos < $len; $pos++) {
2407
            if ($str_line[$pos] == $stopDelim) {
2408
                break;
2409
            }
2410
            if ($str_line[$pos] == '"') {
2411
                $this->_advanceOverStr($str_line,
2412
                                       $pos,
2413
                                       $len,
2414
                                       $startDelim,
2415
                                       $stopDelim);
2416
            }
2417
            if ($str_line[$pos] == $startDelim) {
2418
                $str_line_aux = $this->_getSubstr($str_line, $pos);
2419
                $pos_aux      = $this->_getClosingBracesPos($str_line_aux);
2420
                $pos         += $pos_aux;
2421
                if ($pos == $len-1) {
2422
                    break;
2423
                }
2424
            }
2425
        }
2426
        if ($str_line[$pos] != $stopDelim) {
2427
            $this->_protError('_getClosingBracesPos: must be a '
2428
                              . '"' . $stopDelim . '" but is a '
2429
                              . '"' . $str_line[$pos] . '"'
2430
                              . ' |POS: ' . $pos
2431
                              . ' |STR_LINE: ' . $str_line . '!!!!',
2432
                              __LINE__,
2433
                              __FILE__);
2434
        }
2435
 
2436
        if ($pos >= $len) {
2437
            return false;
2438
        }
2439
        return $pos;
2440
    }
2441
 
2442
 
2443
 
2444
    /**
2445
     * Advances the position $pos in $str over an correct escaped string
2446
     *
2447
     * Examples: $str='"\\\"First Last\\\""', $pos=0
2448
     *      --> returns true and $pos=strlen($str)-1
2449
     *
2450
     * @param string $str        String
2451
     * @param int    &$pos       Current position in $str pointing to a
2452
     *                            double quote ("), on return pointing
2453
     *                            on the closing double quote
2454
     * @param int    $len        Length of $str in bytes(!)
2455
     * @param string $startDelim Start delimiter
2456
     * @param string $stopDelim  Stop delimiter
2457
     *
2458
     * @return boolean true if we advanced over a correct string,
2459
     *                 false otherwise
2460
     * @access private
2461
     * @author Nigel Vickers
2462
     * @author Ralf Becker
2463
     * @since 1.1
2464
     */
2465
    function _advanceOverStr($str,
2466
                             &$pos,
2467
                             $len,
2468
                             $startDelim ='(',
2469
                             $stopDelim = ')')
2470
    {
2471
        if ($str[$pos] !== '"') {
2472
            // start condition failed
2473
            return false;
2474
        }
2475
 
2476
        $pos++;
2477
 
2478
        while ($str[$pos] !== '"' && $pos < $len) {
2479
            // this is a fix to stop before the delimiter, in broken
2480
            // string messages containing an odd number of double quotes
2481
            // the idea is to check for a stopDelimited followed by
2482
            // eiter a new startDelimiter or an other stopDelimiter
2483
            // that allows to have something
2484
            // like '"Name (Nick)" <email>' containing one delimiter
2485
 
2486
            if ($str[$pos] === $stopDelim
2487
                && ($str[$pos+1] === $startDelim
2488
                || $str[$pos+1] === $stopDelim)) {
2489
                // stopDelimited need to be parsed outside!
2490
                $pos--;
2491
                return false;
2492
            }
2493
 
2494
            // all escaped chars are overread (eg. \\,  \", \x)
2495
            if ($str[$pos] === '\\') {
2496
                $pos++;
2497
            }
2498
            $pos++;
2499
        }
2500
 
2501
        return $pos < $len && $str[$pos] === '"';
2502
    }
2503
 
2504
 
2505
 
2506
    /**
2507
     * Utility funcion to get from here to the end of the line
2508
     *
2509
     * @param string  &$str      String
2510
     * @param boolean $including true for Including EOL
2511
     *                           false to not include EOL
2512
     *
2513
     * @return string The string to the first EOL
2514
     * @access private
2515
     * @since  1.0
2516
     */
2517
    function _getToEOL(&$str, $including = true)
2518
    {
2519
        $len = $this->_getLineLength($str);
2520
        if ($including) {
2521
            for ($i=0; $i<$len; $i++) {
2522
                if ($str[$i] == "\n") {
2523
                    break;
2524
                }
2525
            }
2526
            $content = $this->_getSubstr($str, 0, $i + 1);
2527
            $str     = $this->_getSubstr($str, $i + 1);
2528
        } else {
2529
            for ($i = 0 ; $i < $len ; $i++ ) {
2530
                if ($str[$i] == "\n" || $str[$i] == "\r") {
2531
                    break;
2532
                }
2533
            }
2534
            $content = $this->_getSubstr($str, 0, $i);
2535
            $str     = $this->_getSubstr($str, $i);
2536
        }
2537
        return $content;
2538
    }
2539
 
2540
 
2541
 
2542
    /**
2543
     * Fetches the next IMAP token or parenthesis
2544
     *
2545
     * @param string  &$str               The IMAP's server response
2546
     * @param string  &$content           The next token
2547
     * @param boolean $parenthesisIsToken true: the parenthesis IS a token,
2548
     *                                    false: I consider all the response
2549
     *                                    in parenthesis as a token
2550
     * @param boolean $colonIsToken       true: the colin IS a token
2551
     *                                    false:
2552
     *
2553
     * @return int The content size
2554
     * @access private
2555
     * @since 1.0
2556
     */
2557
    function _getNextToken(&$str,
2558
                           &$content,
2559
                           $parenthesisIsToken = true,
2560
                           $colonIsToken = true)
2561
    {
2562
        $len          = $this->_getLineLength($str);
2563
        $pos          = 0;
2564
        $content_size = false;
2565
        $content      = false;
2566
        if ($str == '' || $len < 2) {
2567
            $content = $str;
2568
            return $len;
2569
        }
2570
        switch ($str[0]) {
2571
        case '{':
2572
            if ($posClosingBraces = $this->_getClosingBracesPos($str,
2573
                                                                '{',
2574
                                                                '}') == false) {
2575
                $this->_protError('_getClosingBracesPos() error!!!',
2576
                                  __LINE__,
2577
                                  __FILE__);
2578
            }
2579
            if (!is_numeric(($strBytes = $this->_getSubstr($str,
2580
                                                           1,
2581
                                                           $posClosingBraces - 1)))) {
2582
                $this->_protError('must be a number but is a '
2583
                                  . '"' . $strBytes . '" !!!',
2584
                                  __LINE__,
2585
                                  __FILE__);
2586
            }
2587
            if ($str[$posClosingBraces] != '}') {
2588
                $this->_protError('must be a "}" but is a '
2589
                                  . '"' . $str[$posClosingBraces] . '"!!!',
2590
                                  __LINE__,
2591
                                  __FILE__);
2592
            }
2593
            if ($str[$posClosingBraces + 1] != "\r") {
2594
                $this->_protError('must be a "\r" but is a '
2595
                                  . '"' . $str[$posClosingBraces + 1] . '"!!!',
2596
                                  __LINE__,
2597
                                  __FILE__);
2598
            }
2599
            if ($str[$posClosingBraces + 2] != "\n") {
2600
                $this->_protError('must be a "\n" but is a '
2601
                                  . '"' . $str[$posClosingBraces + 2] . '"!!!',
2602
                                  __LINE__,
2603
                                  __FILE__);
2604
            }
2605
            $content = $this->_getSubstr($str,
2606
                                         $posClosingBraces + 3,
2607
                                         $strBytes);
2608
            if ($this->_getLineLength($content) != $strBytes) {
2609
                $this->_protError('content size is '
2610
                                  . '"' . $this->_getLineLength($content) . '"'
2611
                                  . ' but the string reports a size of '
2612
                                  . $strBytes .'!!!!',
2613
                                  __LINE__,
2614
                                  __FILE__);
2615
            }
2616
            $content_size = $strBytes;
2617
            //Advance the string
2618
            $str = $this->_getSubstr($str, $posClosingBraces + $strBytes + 3);
2619
            break;
2620
 
2621
        case '"':
2622
            if ($colonIsToken) {
2623
                for ($pos=1; $pos<$len; $pos++) {
2624
                    if ($str[$pos] == '"') {
2625
                        break;
2626
                    }
2627
                    if ($str[$pos] == "\\" && $str[$pos + 1 ] == '"') {
2628
                        $pos++;
2629
                    }
2630
                    if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\\") {
2631
                        $pos++;
2632
                    }
2633
                }
2634
                if ($str[$pos] != '"') {
2635
                    $this->_protError('must be a "\"" but is a '
2636
                                      . '"' . $str[$pos] . '"!!!!',
2637
                                      __LINE__,
2638
                                      __FILE__);
2639
                }
2640
                $content_size = $pos;
2641
                $content      = $this->_getSubstr($str, 1, $pos - 1);
2642
                //Advance the string
2643
                $str = $this->_getSubstr($str, $pos + 1);
2644
            } else {
2645
                for ($pos=1; $pos<$len; $pos++) {
2646
                    if ($str[$pos] == '"') {
2647
                        break;
2648
                    }
2649
                    if ($str[$pos] == "\\" && $str[$pos + 1 ] == '"' ) {
2650
                        $pos++;
2651
                    }
2652
                    if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\\" ) {
2653
                        $pos++;
2654
                    }
2655
                }
2656
                if ($str[$pos] != '"') {
2657
                    $this->_protError('must be a "\"" but is a '
2658
                                      . '"' . $str[$pos] . '"!!!!',
2659
                                      __LINE__,
2660
                                      __FILE__);
2661
                }
2662
                $content_size = $pos;
2663
                $content      = $this->_getSubstr($str, 0, $pos + 1);
2664
                //Advance the string
2665
                $str = $this->_getSubstr($str, $pos + 1);
2666
 
2667
            }
2668
            // we need to strip slashes for a quoted string
2669
            $content = stripslashes($content);
2670
            break;
2671
 
2672
        case "\r":
2673
            $pos = 1;
2674
            if ($str[1] == "\n") {
2675
                $pos++;
2676
            }
2677
            $content_size = $pos;
2678
            $content      = $this->_getSubstr($str, 0, $pos);
2679
            $str          = $this->_getSubstr($str, $pos);
2680
            break;
2681
 
2682
        case "\n":
2683
            $pos          = 1;
2684
            $content_size = $pos;
2685
            $content      = $this->_getSubstr($str, 0, $pos);
2686
            $str          = $this->_getSubstr($str, $pos);
2687
            break;
2688
 
2689
        case '(':
2690
            if ($parenthesisIsToken == false) {
2691
                $pos          = $this->_getClosingBracesPos($str);
2692
                $content_size = $pos + 1;
2693
                $content      = $this->_getSubstr($str, 0, $pos + 1);
2694
                $str          = $this->_getSubstr($str, $pos + 1);
2695
            } else {
2696
                $pos          = 1;
2697
                $content_size = $pos;
2698
                $content      = $this->_getSubstr($str, 0, $pos);
2699
                $str          = $this->_getSubstr($str, $pos);
2700
            }
2701
            break;
2702
 
2703
        case ')':
2704
            $pos          = 1;
2705
            $content_size = $pos;
2706
            $content      = $this->_getSubstr($str, 0, $pos);
2707
            $str          = $this->_getSubstr($str, $pos);
2708
            break;
2709
 
2710
        case ' ':
2711
            $pos          = 1;
2712
            $content_size = $pos;
2713
            $content      = $this->_getSubstr($str, 0, $pos);
2714
            $str          = $this->_getSubstr($str, $pos);
2715
            break;
2716
 
2717
        default:
2718
            for ($pos = 0; $pos < $len; $pos++) {
2719
                if ($this->_getSubstr($str, 0, 5) == 'BODY['
2720
                    || $this->_getSubstr($str, 0, 5) == 'BODY.') {
2721
                    if ($str[$pos] == ']') {
2722
                        $pos++;
2723
                        break;
2724
                    }
2725
                } elseif ($str[$pos] == ' '
2726
                          || $str[$pos] == "\r"
2727
                          || $str[$pos] == ')'
2728
                          || $str[$pos] == '('
2729
                          || $str[$pos] == "\n" ) {
2730
                    break;
2731
                }
2732
                if ($str[$pos] == "\\" && $str[$pos + 1 ] == ' ') {
2733
                    $pos++;
2734
                }
2735
                if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\\") {
2736
                    $pos++;
2737
                }
2738
            }
2739
            //Advance the string
2740
            if ($pos == 0) {
2741
                $content_size = 1;
2742
                $content      = $this->_getSubstr($str, 0, 1);
2743
                $str          = $this->_getSubstr($str, 1);
2744
            } else {
2745
                $content_size = $pos;
2746
                $content      = $this->_getSubstr($str, 0, $pos);
2747
                if ($pos < $len) {
2748
                    $str = $this->_getSubstr($str, $pos);
2749
                } else {
2750
                    //if this is the end of the string... exit the switch
2751
                    break;
2752
                }
2753
            }
2754
            break;
2755
        }
2756
        return $content_size;
2757
    }
2758
 
2759
 
2760
 
2761
    /**
2762
     * Utility funcion to display to console the protocol errors
2763
     * printErrors() additionally has to be set to true
2764
     *
2765
     * @param string  $str        The error message
2766
     * @param int     $line       The line producing the error
2767
     * @param string  $file       File where the error was produced
2768
     * @param boolean $printError true: print the error
2769
     *                            false: do not print the error
2770
     *
2771
     * @return nothing
2772
     * @access private
2773
     * @since 1.0
2774
     */
2775
    function _protError($str , $line , $file, $printError = true)
2776
    {
2777
        // ToDo: all real errors should be returned as PEAR error, others
2778
        // hidden by default
2779
        // NO extra output from this class!
2780
        if ($this->_printErrors && $printError) {
2781
            echo "$line,$file,PROTOCOL ERROR!:$str\n";
2782
        }
2783
    }
2784
 
2785
 
2786
 
2787
    /**
2788
     * get EXT array from string
2789
     *
2790
     * @param string &$str       String
2791
     * @param string $startDelim Start delimiter
2792
     * @param string $stopDelim  Stop delimiter
2793
     *
2794
     * @return array EXT array
2795
     * @access private
2796
     * @since 1.0
2797
     */
2798
    function _getEXTarray(&$str, $startDelim = '(', $stopDelim = ')')
2799
    {
2800
        /* I let choose the $startDelim  and $stopDelim to allow parsing
2801
           the OK response  so I also can parse a response like this
2802
           * OK [UIDNEXT 150] Predicted next UID
2803
        */
2804
        $this->_getNextToken($str, $parenthesis);
2805
        if ($parenthesis != $startDelim) {
2806
            $this->_protError('must be a "' . $startDelim . '" but is a '
2807
                              . '"' . $parenthesis . '" !!!!',
2808
                              __LINE__,
2809
                              __FILE__);
2810
        }
2811
 
2812
        $parenthesis = '';
2813
        $struct_arr  = array();
2814
        while ($parenthesis != $stopDelim && $str != '') {
2815
            // The command
2816
            $this->_getNextToken($str, $token);
2817
            $token = strtoupper($token);
2818
 
2819
            if (($ret = $this->_retrParsedResponse($str, $token)) != false) {
2820
                //$struct_arr[$token] = $ret;
2821
                $struct_arr = array_merge($struct_arr, $ret);
2822
            }
2823
 
2824
            $parenthesis = $token;
2825
 
2826
        } //While
2827
 
2828
        if ($parenthesis != $stopDelim ) {
2829
            $this->_protError('1_must be a "' . $stopDelim . '" but is a '
2830
                              . '"' . $parenthesis . '"!!!!',
2831
                              __LINE__,
2832
                              __FILE__);
2833
        }
2834
        return $struct_arr;
2835
    }
2836
 
2837
 
2838
 
2839
    /**
2840
     * retrieve parsed response
2841
     *
2842
     * @param string &$str          String
2843
     * @param string $token         Token
2844
     * @param string $previousToken Previous token
2845
     *
2846
     * @return array Parsed response
2847
     * @access private
2848
     * @since 1.0
2849
     */
2850
    function _retrParsedResponse(&$str, $token, $previousToken = null)
2851
    {
2852
        //echo "\n\nTOKEN:$token\r\n";
2853
        $token = strtoupper($token);
2854
 
2855
        switch ($token) {
2856
        case 'RFC822.SIZE':
2857
            return array($token => $this->_parseOneStringResponse($str,
2858
                                                                  __LINE__,
2859
                                                                  __FILE__));
2860
            break;
2861
 
2862
        // case 'RFC822.TEXT':
2863
 
2864
        // case 'RFC822.HEADER':
2865
 
2866
        case 'RFC822':
2867
            return array($token => $this->_parseContentresponse($str,
2868
                                                                $token));
2869
            break;
2870
 
2871
        case 'FLAGS':
2872
        case 'PERMANENTFLAGS':
2873
            return array($token => $this->_parseFLAGSresponse($str));
2874
            break;
2875
 
2876
        case 'ENVELOPE':
2877
            return array($token => $this->_parseENVELOPEresponse($str));
2878
            break;
2879
 
2880
        case 'EXPUNGE':
2881
            return false;
2882
            break;
2883
 
2884
        case 'NOMODSEQ':
2885
            // ToDo: implement RFC 4551
2886
            return array($token=>'');
2887
            break;
2888
 
2889
        case 'UID':
2890
        case 'UIDNEXT':
2891
        case 'UIDVALIDITY':
2892
        case 'UNSEEN':
2893
        case 'MESSAGES':
2894
        case 'UIDNEXT':
2895
        case 'UIDVALIDITY':
2896
        case 'UNSEEN':
2897
        case 'INTERNALDATE':
2898
            return array($token => $this->_parseOneStringResponse($str,
2899
                                                                  __LINE__,
2900
                                                                  __FILE__));
2901
            break;
2902
 
2903
        case 'BODY':
2904
        case 'BODYSTRUCTURE':
2905
            return array($token => $this->_parseBodyResponse($str, $token));
2906
            break;
2907
 
2908
        case 'RECENT':
2909
            if ($previousToken != null) {
2910
                $aux['RECENT'] = $previousToken;
2911
                return $aux;
2912
            } else {
2913
                return array($token => $this->_parseOneStringResponse($str,
2914
                                                                      __LINE__,
2915
                                                                      __FILE__));
2916
            }
2917
            break;
2918
 
2919
        case 'EXISTS':
2920
            return array($token => $previousToken);
2921
            break;
2922
 
2923
        case 'READ-WRITE':
2924
        case 'READ-ONLY':
2925
            return array($token => $token);
2926
            break;
2927
 
2928
        case 'QUOTA':
2929
            /*
2930
            A tipical GETQUOTA DIALOG IS AS FOLLOWS
2931
 
2932
                C: A0004 GETQUOTA user.damian
2933
                S: * QUOTA user.damian (STORAGE 1781460 4000000)
2934
                S: A0004 OK Completed
2935
 
2936
            another example of QUOTA response from GETQUOTAROOT:
2937
                C: A0008 GETQUOTAROOT INBOX
2938
                S: * QUOTAROOT INBOX ""
2939
                S: * QUOTA "" (STORAGE 0 1024000 MESSAGE 0 40000)
2940
                S: A0008 OK GETQUOTAROOT finished.
2941
 
2942
            RFC 2087 section 5.1 says the list could be empty:
2943
 
2944
                C: A0004 GETQUOTA user.damian
2945
                S: * QUOTA user.damian ()
2946
                S: A0004 OK Completed
2947
 
2948
            quota_list      ::= "(" #quota_resource ")"
2949
            quota_resource  ::= atom SP number SP number
2950
            quota_response  ::= "QUOTA" SP astring SP quota_list
2951
            */
2952
 
2953
            $mailbox = $this->_parseOneStringResponse($str, __LINE__, __FILE__);
2954
            $ret_aux = array('MAILBOX' => $this->utf7Decode($mailbox));
2955
 
2956
            // courier fix
2957
            if ($str[0] . $str[1] == "\r\n") {
2958
                return array($token => $ret_aux);
2959
            }
2960
            // end courier fix
2961
 
2962
            $this->_parseSpace($str, __LINE__, __FILE__);
2963
            $this->_parseString($str, '(', __LINE__, __FILE__);
2964
 
2965
            // fetching quota resources
2966
            // (BNF ::= #quota_resource  but space separated instead of comma)
2967
            $this->_getNextToken($str, $quota_resp);
2968
            while ($quota_resp != ')') {
2969
                if (($ext = $this->_retrParsedResponse($str,
2970
                                                       $quota_resp)) == false) {
2971
                    $this->_protError('bogus response!!!!',
2972
                                      __LINE__,
2973
                                      __FILE__);
2974
                }
2975
                $ret_aux = array_merge($ret_aux, $ext);
2976
 
2977
                $this->_getNextToken($str, $quota_resp);
2978
                if ($quota_resp == ' ') {
2979
                    $this->_getNextToken($str, $quota_resp);
2980
                }
2981
            }
2982
 
2983
            // if empty list, apparently no STORAGE or MESSAGE quota set
2984
            return array($token => $ret_aux);
2985
            break;
2986
 
2987
        case 'QUOTAROOT':
2988
            /*
2989
            A tipical GETQUOTA DIALOG IS AS FOLLOWS
2990
 
2991
                C: A0004 GETQUOTA user.damian
2992
                S: * QUOTA user.damian (STORAGE 1781460 4000000)
2993
                S: A0004 OK Completed
2994
            */
2995
            $mailbox = $this->utf7Decode($this->_parseOneStringResponse($str,
2996
                                                            __LINE__,
2997
                                                            __FILE__));
2998
 
2999
            $str_line = rtrim(substr($this->_getToEOL($str, false), 0));
3000
            if (empty($str_line)) {
3001
                $ret = @array('MAILBOX' => $this->utf7Decode($mailbox));
3002
            } else {
3003
                $quotaroot = $this->_parseOneStringResponse($str_line,
3004
                                                            __LINE__,
3005
                                                            __FILE__);
3006
                $ret       = @array('MAILBOX' => $this->utf7Decode($mailbox),
3007
                                    $token    => $quotaroot);
3008
            }
3009
            return array($token => $ret);
3010
            break;
3011
 
3012
        case 'STORAGE':
3013
            $used = $this->_parseOneStringResponse($str, __LINE__, __FILE__);
3014
            $qmax = $this->_parseOneStringResponse($str, __LINE__, __FILE__);
3015
            return array($token => array('USED' => $used, 'QMAX' => $qmax));
3016
            break;
3017
 
3018
        case 'MESSAGE':
3019
            $mused = $this->_parseOneStringResponse($str, __LINE__, __FILE__);
3020
            $mmax  = $this->_parseOneStringResponse($str, __LINE__, __FILE__);
3021
            return array($token=>array("MUSED"=> $mused, "MMAX" => $mmax));
3022
            break;
3023
 
3024
        case 'FETCH':
3025
            $this->_parseSpace($str, __LINE__, __FILE__);
3026
            // Get the parsed pathenthesis
3027
            $struct_arr = $this->_getEXTarray($str);
3028
            return $struct_arr;
3029
            break;
3030
 
3031
        case 'NAMESPACE':
3032
            $this->_parseSpace($str, __LINE__, __FILE__);
3033
            $this->_getNextToken($str, $personal, false);
3034
            $struct_arr['NAMESPACES']['personal'] = $this->_arrayfyContent($personal);
3035
            $this->_parseSpace($str, __LINE__, __FILE__);
3036
            $this->_getNextToken($str, $others, false);
3037
            $struct_arr['NAMESPACES']['others'] = $this->_arrayfyContent($others);
3038
 
3039
            $this->_parseSpace($str, __LINE__, __FILE__);
3040
            $this->_getNextToken($str, $shared, false);
3041
            $struct_arr['NAMESPACES']['shared'] = $this->_arrayfyContent($shared);
3042
 
3043
            return array($token => $struct_arr);
3044
            break;
3045
 
3046
        case 'CAPABILITY':
3047
            $this->_parseSpace($str, __LINE__, __FILE__);
3048
            $str_line = rtrim(substr($this->_getToEOL($str, false), 0));
3049
 
3050
            $struct_arr['CAPABILITIES'] = explode(' ', $str_line);
3051
            return array($token => $struct_arr);
3052
            break;
3053
 
3054
        case 'STATUS':
3055
            $mailbox = $this->_parseOneStringResponse($str, __LINE__, __FILE__);
3056
            $this->_parseSpace($str, __LINE__, __FILE__);
3057
            $ext                      = $this->_getEXTarray($str);
3058
            $struct_arr['MAILBOX']    = $this->utf7Decode($mailbox);
3059
            $struct_arr['ATTRIBUTES'] = $ext;
3060
            return array($token => $struct_arr);
3061
            break;
3062
 
3063
        case 'LIST':
3064
            $this->_parseSpace($str, __LINE__, __FILE__);
3065
            $params_arr = $this->_arrayfyContent($str);
3066
 
3067
            $this->_parseSpace($str, __LINE__, __FILE__);
3068
            $this->_getNextToken($str, $hierarchydelim);
3069
 
3070
            $this->_parseSpace($str, __LINE__, __FILE__);
3071
            $this->_getNextToken($str, $mailbox_name);
3072
 
3073
            $result_array = array('NAME_ATTRIBUTES'    => $params_arr,
3074
                                  'HIERACHY_DELIMITER' => $hierarchydelim,
3075
                                  'MAILBOX_NAME'       => $this->utf7Decode($mailbox_name));
3076
            return array($token => $result_array);
3077
            break;
3078
 
3079
        case 'LSUB':
3080
            $this->_parseSpace($str, __LINE__, __FILE__);
3081
            $params_arr = $this->_arrayfyContent($str);
3082
 
3083
            $this->_parseSpace($str, __LINE__, __FILE__);
3084
            $this->_getNextToken($str, $hierarchydelim);
3085
 
3086
            $this->_parseSpace($str, __LINE__, __FILE__);
3087
            $this->_getNextToken($str, $mailbox_name);
3088
 
3089
            $result_array = array('NAME_ATTRIBUTES'    => $params_arr,
3090
                                  'HIERACHY_DELIMITER' => $hierarchydelim,
3091
                                  'MAILBOX_NAME'       => $this->utf7Decode($mailbox_name));
3092
            return array($token => $result_array);
3093
            break;
3094
 
3095
        case 'SEARCH':
3096
        case 'SORT':
3097
            $str_line = rtrim(substr($this->_getToEOL($str, false), 1));
3098
 
3099
            $struct_arr[$token . '_LIST'] = explode(' ', $str_line);
3100
            if (count($struct_arr[$token . '_LIST']) == 1
3101
                && $struct_arr[$token . '_LIST'][0] == '') {
3102
                $struct_arr[$token . '_LIST'] = null;
3103
            }
3104
            return array($token => $struct_arr);
3105
            break;
3106
 
3107
        case 'OK':
3108
            /* TODO:
3109
                parse the [ .... ] part of the response, use the method
3110
                _getEXTarray(&$str,'[',$stopDelim=']')
3111
            */
3112
            $str_line = rtrim(substr($this->_getToEOL($str, false), 1));
3113
            if ($str_line[0] == '[') {
3114
                $braceLen = $this->_getClosingBracesPos($str_line, '[', ']');
3115
                $str_aux  = '('. substr($str_line, 1, $braceLen -1). ')';
3116
                $ext_arr  = $this->_getEXTarray($str_aux);
3117
                //$ext_arr=array($token=>$this->_getEXTarray($str_aux));
3118
            } else {
3119
                $ext_arr = $str_line;
3120
                //$ext_arr=array($token=>$str_line);
3121
            }
3122
            $result_array =  $ext_arr;
3123
            return $result_array;
3124
            break;
3125
 
3126
        case 'NO':
3127
            /* TODO:
3128
                parse the [ .... ] part of the response, use the method
3129
                _getEXTarray(&$str,'[',$stopDelim=']')
3130
            */
3131
            $str_line       = rtrim(substr($this->_getToEOL($str, false), 1));
3132
            $result_array[] = @array('COMMAND' => $token, 'EXT' => $str_line);
3133
            return $result_array;
3134
            break;
3135
 
3136
        case 'BAD':
3137
            /* TODO:
3138
                parse the [ .... ] part of the response, use the method
3139
                _getEXTarray(&$str,'[',$stopDelim=']')
3140
            */
3141
            $str_line       = rtrim(substr($this->_getToEOL($str, false), 1));
3142
            $result_array[] = array('COMMAND' => $token, 'EXT' => $str_line);
3143
            return $result_array;
3144
            break;
3145
 
3146
        case 'BYE':
3147
            /* TODO:
3148
                parse the [ .... ] part of the response, use the method
3149
                _getEXTarray(&$str,'[',$stopDelim=']')
3150
            */
3151
            $str_line       = rtrim(substr($this->_getToEOL($str, false), 1));
3152
            $result_array[] = array('COMMAND' => $token, 'EXT' => $str_line);
3153
            return $result_array;
3154
            break;
3155
 
3156
        case 'LISTRIGHTS':
3157
            $this->_parseSpace($str, __LINE__, __FILE__);
3158
            $this->_getNextToken($str, $mailbox);
3159
            $this->_parseSpace($str, __LINE__, __FILE__);
3160
            $this->_getNextToken($str, $user);
3161
            $this->_parseSpace($str, __LINE__, __FILE__);
3162
            $this->_getNextToken($str, $granted);
3163
 
3164
            $ungranted = explode(' ', rtrim(substr($this->_getToEOL($str, false), 1)));
3165
 
3166
            $result_array = @array('MAILBOX'   => $this->utf7Decode($mailbox),
3167
                                   'USER'      => $user,
3168
                                   'GRANTED'   => $granted,
3169
                                   'UNGRANTED' => $ungranted);
3170
            return $result_array;
3171
            break;
3172
 
3173
        case 'MYRIGHTS':
3174
            $this->_parseSpace($str, __LINE__, __FILE__);
3175
            $this->_getNextToken($str, $mailbox);
3176
            // Patch to handle the alternate MYRIGHTS response from
3177
            // Courier-IMAP
3178
            if ($str==')') {
3179
                $granted = $mailbox;
3180
                $mailbox = $this->currentMailbox;
3181
            } else {
3182
                $this->_parseSpace($str, __LINE__, __FILE__);
3183
                $this->_getNextToken($str, $granted);
3184
            }
3185
            // End Patch
3186
 
3187
            $result_array = array('MAILBOX' => $this->utf7Decode($mailbox),
3188
                                  'GRANTED' => $granted);
3189
            return $result_array;
3190
            break;
3191
 
3192
        case 'ACL':
3193
            /*
3194
            RFC 4314:
3195
            acl-data        = "ACL" SP mailbox *(SP identifier SP rights)
3196
            identifier      = astring
3197
            rights          = astring ;; only lowercase ASCII letters and
3198
                                         digits are allowed.
3199
            */
3200
            //$str = " INBOX\r\nA0006 OK Completed\r\n";
3201
            $this->_parseSpace($str, __LINE__, __FILE__);
3202
            $this->_getNextToken($str, $mailbox);
3203
 
3204
            $arr = array();
3205
            while (substr($str, 0, 2) != "\r\n") {
3206
                $this->_parseSpace($str, __LINE__, __FILE__);
3207
                $this->_getNextToken($str, $acl_user);
3208
                $this->_parseSpace($str, __LINE__, __FILE__);
3209
                $this->_getNextToken($str, $acl_rights);
3210
                $arr[] = array('USER' => $acl_user, 'RIGHTS' => $acl_rights);
3211
            }
3212
 
3213
            $result_array = array('MAILBOX' => $this->utf7Decode($mailbox),
3214
                                  'USERS'   => $arr);
3215
            return $result_array;
3216
            break;
3217
 
3218
        case 'ANNOTATION':
3219
            $this->_parseSpace($str, __LINE__, __FILE__);
3220
            $this->_getNextToken($str, $mailbox);
3221
 
3222
            $this->_parseSpace($str, __LINE__, __FILE__);
3223
            $this->_getNextToken($str, $entry);
3224
 
3225
            $this->_parseSpace($str, __LINE__, __FILE__);
3226
            $attrs = $this->_arrayfyContent($str);
3227
 
3228
            $result_array = array('MAILBOX'    => $mailbox,
3229
                                  'ENTRY'      => $entry,
3230
                                  'ATTRIBUTES' => $attrs);
3231
            return $result_array;
3232
            break;
3233
 
3234
        case '':
3235
            $this->_protError('PROTOCOL ERROR!:str empty!!',
3236
                              __LINE__,
3237
                              __FILE__);
3238
            break;
3239
 
3240
        case '(':
3241
            $this->_protError('OPENING PARENTHESIS ERROR!!!',
3242
                              __LINE__,
3243
                              __FILE__);
3244
            break;
3245
 
3246
        case ')':
3247
            //"CLOSING PARENTHESIS BREAK!!!!!!!"
3248
            break;
3249
 
3250
        case "\r\n":
3251
            $this->_protError('BREAK!!!', __LINE__, __FILE__);
3252
            break;
3253
 
3254
        case ' ':
3255
            // this can happen and we just ignore it
3256
            // This happens when - for example - fetch returns more than 1
3257
            // parammeter
3258
            // for example you ask to get RFC822.SIZE and UID
3259
            // $this->_protError('SPACE BREAK!!!', __LINE__, __FILE__);
3260
            break;
3261
 
3262
        default:
3263
            $body_token   = strtoupper(substr($token, 0, 5));
3264
            $rfc822_token = strtoupper(substr($token, 0, 7));
3265
 
3266
            if ($body_token == 'BODY['
3267
                || $body_token == 'BODY.'
3268
                || $rfc822_token == 'RFC822.') {
3269
                //echo "TOKEN:$token\n";
3270
                //$this->_getNextToken( $str , $mailbox );
3271
                return array($token => $this->_parseContentresponse($str,
3272
                                                                    $token));
3273
            } else {
3274
                $this->_protError('UNIMPLEMMENTED! I don\'t know the '
3275
                                  . 'parameter "' . $token . '"!!!',
3276
                                  __LINE__,
3277
                                  __FILE__);
3278
            }
3279
            break;
3280
        }
3281
 
3282
        return false;
3283
    }
3284
 
3285
 
3286
 
3287
    /**
3288
     * Verifies that the next character IS a space
3289
     *
3290
     * @param string  &$str       String
3291
     * @param int     $line       Line number
3292
     * @param string  $file       File name
3293
     * @param boolean $printError Print errors
3294
     *
3295
     * @return string First character of $str
3296
     * @access private
3297
     */
3298
    function _parseSpace(&$str, $line, $file, $printError = true)
3299
    {
3300
        /*
3301
        This code repeats a lot in this class
3302
        so i make it a function to make all the code shorter
3303
        */
3304
        $this->_getNextToken($str, $space);
3305
        if ($space != ' ') {
3306
            $this->_protError('must be a " " but is a "' . $space . '"!!!!',
3307
                              $line,
3308
                              $file,
3309
                              $printError);
3310
        }
3311
        return $space;
3312
    }
3313
 
3314
 
3315
 
3316
    /**
3317
     * parse string for next character after token
3318
     *
3319
     * @param string &$str String
3320
     * @param string $char Next character
3321
     * @param int    $line Line number
3322
     * @param string $file File name
3323
     *
3324
     * @return string Character after next token
3325
     * @access private
3326
     */
3327
    function _parseString(&$str, $char, $line, $file)
3328
    {
3329
        /*
3330
        This code repeats a lot in this class
3331
        so i make it a function to make all the code shorter
3332
        */
3333
        $this->_getNextToken($str, $char_aux);
3334
        if (strtoupper($char_aux) != strtoupper($char)) {
3335
            $this->_protError('must be a "' . $char . '" but is a '
3336
                              . '"' . $char_aux . '"!!!!',
3337
                              $line,
3338
                              $file);
3339
        }
3340
        return $char_aux;
3341
    }
3342
 
3343
 
3344
 
3345
    /**
3346
     * parse IMAP response
3347
     *
3348
     * @param string &$str  Response string
3349
     * @param int    $cmdid Command ID
3350
     *
3351
     * @return array Response array
3352
     * @access private
3353
     */
3354
    function _genericImapResponseParser(&$str, $cmdid = null)
3355
    {
3356
        $result_array = array();
3357
        if ($this->_unParsedReturn) {
3358
            $unparsed_str = $str;
3359
        }
3360
 
3361
        $this->_getNextToken($str, $token);
3362
 
3363
        while ($token != $cmdid && $str != '') {
3364
            if ($token == '+' ) {
3365
                //if the token  is + ignore the line
3366
                // TODO: verify that this is correct!!!
3367
                $this->_getToEOL($str);
3368
                $this->_getNextToken($str, $token);
3369
            }
3370
 
3371
            $this->_parseString($str, ' ', __LINE__, __FILE__);
3372
 
3373
            $this->_getNextToken($str, $token);
3374
            if ($token == '+') {
3375
                $this->_getToEOL($str);
3376
                $this->_getNextToken($str, $token);
3377
            } else {
3378
                if (is_numeric($token)) {
3379
                    // The token is a NUMBER so I store it
3380
                    $msg_nro = $token;
3381
                    $this->_parseSpace($str, __LINE__, __FILE__);
3382
 
3383
                    // I get the command
3384
                    $this->_getNextToken($str, $command);
3385
 
3386
                    if (($ext_arr = $this->_retrParsedResponse($str, $command, $msg_nro)) == false) {
3387
                        // if this bogus response is a FLAGS () or EXPUNGE
3388
                        // response the ignore it
3389
                        if ($command != 'FLAGS' && $command != 'EXPUNGE') {
3390
                            $this->_protError('bogus response!!!!',
3391
                                              __LINE__,
3392
                                              __FILE__,
3393
                                              false);
3394
                        }
3395
                    }
3396
                    $result_array[] = array('COMMAND' => $command,
3397
                                            'NRO'     => $msg_nro,
3398
                                            'EXT'     => $ext_arr);
3399
                } else {
3400
                    // OK the token is not a NUMBER so it MUST be a COMMAND
3401
                    $command = $token;
3402
 
3403
                    /* Call the parser return the array
3404
                        take care of bogus responses!
3405
                    */
3406
 
3407
                    if (($ext_arr = $this->_retrParsedResponse($str, $command)) == false) {
3408
                        $this->_protError('bogus response!!!! (COMMAND:'
3409
                                          . $command. ')',
3410
                                          __LINE__,
3411
                                          __FILE__);
3412
                    }
3413
                    $result_array[] = array('COMMAND' => $command,
3414
                                            'EXT'     => $ext_arr);
3415
                }
3416
            }
3417
 
3418
 
3419
            $this->_getNextToken($str, $token);
3420
 
3421
            $token = strtoupper($token);
3422
            if ($token != "\r\n" && $token != '') {
3423
                $this->_protError('PARSE ERROR!!! must be a "\r\n" here but '
3424
                                  . 'is a "' . $token . '"!!!! (getting the '
3425
                                  . 'next line)|STR:|' . $str. '|',
3426
                                  __LINE__,
3427
                                  __FILE__);
3428
            }
3429
            $this->_getNextToken($str, $token);
3430
 
3431
            if ($token == '+') {
3432
                //if the token  is + ignore the line
3433
                // TODO: verify that this is correct!!!
3434
                $this->_getToEOL($str);
3435
                $this->_getNextToken($str, $token);
3436
            }
3437
        } //While
3438
        // OK we finish the UNTAGGED Response now we must parse
3439
        // the FINAL TAGGED RESPONSE
3440
        // TODO: make this a litle more elegant!
3441
        $this->_parseSpace($str, __LINE__, __FILE__, false);
3442
 
3443
        $this->_getNextToken($str, $cmd_status);
3444
 
3445
        $str_line = rtrim(substr($this->_getToEOL($str), 1));
3446
 
3447
 
3448
        $response['RESPONSE'] = array('CODE'     => $cmd_status,
3449
                                      'STR_CODE' => $str_line,
3450
                                      'CMDID'    => $cmdid);
3451
 
3452
        $ret = $response;
3453
        if (!empty($result_array)) {
3454
            $ret = array_merge($ret, array('PARSED' => $result_array));
3455
        }
3456
 
3457
        if ($this->_unParsedReturn) {
3458
            $unparsed['UNPARSED'] = $unparsed_str;
3459
            $ret                  = array_merge($ret, $unparsed);
3460
        }
3461
 
3462
        if (isset($status_arr)) {
3463
            $status['STATUS'] = $status_arr;
3464
            $ret              = array_merge($ret, $status);
3465
        }
3466
 
3467
        return $ret;
3468
    }
3469
 
3470
 
3471
 
3472
    /**
3473
     * Send generic command
3474
     *
3475
     * @param string $command Command
3476
     * @param string $params  Parameters
3477
     *
3478
     * @return array Parsed response
3479
     * @access private
3480
     */
3481
    function _genericCommand($command, $params = '')
3482
    {
3483
        if (!$this->_connected) {
3484
            return new PEAR_Error('not connected! (CMD:$command)');
3485
        }
3486
        $cmdid = $this->_getCmdId();
3487
        $this->_putCMD($cmdid, $command, $params);
3488
        $args = $this->_getRawResponse($cmdid);
3489
        return $this->_genericImapResponseParser($args, $cmdid);
3490
    }
3491
 
3492
 
3493
 
3494
    /**
3495
     * Encode string to UTF7
3496
     *
3497
     * Use utf7Encode() instead. This method is only for BC.
3498
     *
3499
     * @param string $str String
3500
     *
3501
     * @return string UTF7 encoded string
3502
     * @access public
3503
     * @deprecated Use utf7Encode() instead
3504
     */
3505
    function utf_7_encode($str)
3506
    {
3507
        return utf7Encode($str);
3508
    }
3509
 
3510
 
3511
 
3512
    /**
3513
     * Encode string to UTF7
3514
     *
3515
     * @param string $str String
3516
     *
3517
     * @return string UTF7 encoded string
3518
     * @access public
3519
     */
3520
    function utf7Encode($str)
3521
    {
3522
        if ($this->_useUTF_7 == false) {
3523
            return $str;
3524
        }
3525
 
3526
        if (function_exists('mb_convert_encoding')) {
3527
            return mb_convert_encoding($str, 'UTF7-IMAP', 'ISO-8859-1');
3528
        }
3529
 
3530
        $encoded_utf7 = '';
3531
        $base64_part  = '';
3532
        if (is_array($str)) {
3533
            return new PEAR_Error('error');
3534
        }
3535
 
3536
        for ($i = 0; $i < $this->_getLineLength($str); $i++) {
3537
            //those chars should be base64 encoded
3538
            if (((ord($str[$i]) >= 39) && (ord($str[$i]) <= 126))
3539
                || ((ord($str[$i]) >= 32) && (ord($str[$i]) <= 37))) {
3540
                if ($base64_part) {
3541
                    $encoded_utf7 = sprintf("%s&%s-",
3542
                                            $encoded_utf7,
3543
                                            str_replace('=', '', base64_encode($base64_part)));
3544
                    $base64_part  = '';
3545
                }
3546
                $encoded_utf7 = sprintf("%s%s", $encoded_utf7, $str[$i]);
3547
            } else {
3548
                //handle &
3549
                if (ord($str[$i]) == 38 ) {
3550
                    if ($base64_part) {
3551
                        $encoded_utf7 = sprintf("%s&%s-",
3552
                                                $encoded_utf7,
3553
                                                str_replace('=', '', base64_encode($base64_part)));
3554
                        $base64_part  = '';
3555
                    }
3556
                    $encoded_utf7 = sprintf("%s&-", $encoded_utf7);
3557
                } else {
3558
                    $base64_part = sprintf("%s%s", $base64_part, $str[$i]);
3559
                    //$base64_part = sprintf("%s%s%s",
3560
                    //                       $base64_part,
3561
                    //                       chr(0),
3562
                    //                       $str[$i]);
3563
                }
3564
            }
3565
        }
3566
        if ($base64_part) {
3567
            $encoded_utf7 = sprintf("%s&%s-",
3568
                                    $encoded_utf7,
3569
                                    str_replace('=', '', base64_encode($base64_part)));
3570
            $base64_part  = '';
3571
        }
3572
 
3573
        return $encoded_utf7;
3574
    }
3575
 
3576
 
3577
 
3578
    /**
3579
     * Decode string from UTF7
3580
     *
3581
     * Use utf7Decode() instead. This method is only for BC.
3582
     *
3583
     * @param string $str UTF7 encoded string
3584
     *
3585
     * @return string Decoded string
3586
     * @access public
3587
     * @deprecated Use utf7Decode() instead
3588
     */
3589
    function utf_7_decode($str)
3590
    {
3591
        utf7Decode($str);
3592
    }
3593
 
3594
 
3595
 
3596
    /**
3597
     * Decode string from UTF7
3598
     *
3599
     * @param string $str UTF7 encoded string
3600
     *
3601
     * @return string Decoded string
3602
     * @access public
3603
     */
3604
    function utf7Decode($str)
3605
    {
3606
        if ($this->_useUTF_7 == false) {
3607
            return $str;
3608
        }
3609
 
3610
        //return imap_utf7_decode($str);
3611
 
3612
        if (function_exists('mb_convert_encoding')) {
3613
            return mb_convert_encoding($str, 'ISO-8859-1', 'UTF7-IMAP');
3614
        }
3615
 
3616
        $base64_part  = '';
3617
        $decoded_utf7 = '';
3618
 
3619
        for ($i = 0; $i < $this->_getLineLength($str); $i++) {
3620
            if ($this->_getLineLength($base64_part) > 0) {
3621
                if ($str[$i] == '-') {
3622
                    if ($base64_part == '&') {
3623
                        $decoded_utf7 = sprintf("%s&", $decoded_utf7);
3624
                    } else {
3625
                        $next_part_decoded = base64_decode(substr($base64_part, 1));
3626
                        $decoded_utf7      = sprintf("%s%s",
3627
                                                $decoded_utf7,
3628
                                                $next_part_decoded);
3629
                    }
3630
                    $base64_part = '';
3631
 
3632
                } else {
3633
                    $base64_part = sprintf("%s%s", $base64_part, $str[$i]);
3634
                }
3635
            } else {
3636
                if ($str[$i] == '&') {
3637
                    $base64_part = '&';
3638
                } else {
3639
                    $decoded_utf7 = sprintf("%s%s", $decoded_utf7, $str[$i]);
3640
                }
3641
            }
3642
        }
3643
        return $decoded_utf7;
3644
    }
3645
 
3646
 
3647
 
3648
    /**
3649
     * Make  CREATE/RENAME compatible option params
3650
     *
3651
     * @param array $options options to format
3652
     *
3653
     * @return string Returns a string for formatted parameters
3654
     * @access private
3655
     * @since 1.1
3656
     */
3657
    function _getCreateParams($options)
3658
    {
3659
        $args = '';
3660
        if (is_null($options) === false && is_array($options) === true) {
3661
            foreach ($options as $opt => $data) {
3662
                switch (strtoupper($opt)) {
3663
                case 'PARTITION':
3664
                    $args .= sprintf(" %s", $this->utf7Encode($data));
3665
                    break;
3666
 
3667
                default:
3668
                    // ignore any unknown options
3669
                    break;
3670
 
3671
                }
3672
            }
3673
        }
3674
        return $args;
3675
    }
3676
 
3677
 
3678
 
3679
    /**
3680
     * Return true if the TLS negotiation was successful
3681
     *
3682
     * @access public
3683
     * @return mixed true on success, PEAR_Error on failure
3684
     */
3685
    function cmdStartTLS()
3686
    {
3687
        if (PEAR::isError($res = $this->_genericCommand('STARTTLS'))) {
3688
            return $res;
3689
        }
3690
 
3691
        if (stream_socket_enable_crypto($this->_socket->fp,
3692
                                        true,
3693
                                        STREAM_CRYPTO_METHOD_TLS_CLIENT) == false) {
3694
            $msg = 'Failed to establish TLS connection';
3695
            return new PEAR_Error($msg);
3696
        }
3697
 
3698
        if ($this->_debug === true) {
3699
            echo "STARTTLS Negotiation Successful\n";
3700
        }
3701
 
3702
        // RFC says we need to query the server capabilities again
3703
        if (PEAR::isError($res = $this->cmdCapability())) {
3704
            $msg = 'Failed to connect, server said: ' . $res->getMessage();
3705
            return new PEAR_Error($msg);
3706
        }
3707
        return true;
3708
    }
3709
 
3710
 
3711
 
3712
    /**
3713
     * get the length of string
3714
     *
3715
     * @param string $string String
3716
     *
3717
     * @return int Line length
3718
     * @access private
3719
     */
3720
    function _getLineLength($string)
3721
    {
3722
        if (extension_loaded('mbstring')) {
3723
            return mb_strlen($string, 'latin1');
3724
        } else {
3725
            return strlen($string);
3726
        }
3727
    }
3728
 
3729
 
3730
 
3731
    /**
3732
     * get substring from string
3733
     *
3734
     * @param string $string String
3735
     * @param int    $start  Position to start from
3736
     * @param int    $length Number of characters
3737
     *
3738
     * @return string Substring
3739
     * @access private
3740
     */
3741
    function _getSubstr($string, $start, $length = false)
3742
    {
3743
        if (extension_loaded('mbstring')) {
3744
            if ($length !== false) {
3745
                return mb_substr($string, $start, $length, 'latin1');
3746
            } else {
3747
                $strlen = mb_strlen($string, 'latin1');
3748
                return mb_substr($string, $start, $strlen, 'latin1');
3749
            }
3750
        } else {
3751
            if ($length !== false) {
3752
                return substr($string, $start, $length);
3753
            } else {
3754
                return substr($string, $start);
3755
            }
3756
        }
3757
    }
3758
 
3759
}//Class
3760
?>