PageRenderTime 35ms CodeModel.GetById 12ms app.highlight 16ms RepoModel.GetById 2ms app.codeStats 0ms

/framework/vendor/zend/Zend/Mail/Protocol/Imap.php

http://zoop.googlecode.com/
PHP | 838 lines | 431 code | 71 blank | 336 comment | 114 complexity | 7d6e23de50222381de8d8698c73913ea MD5 | raw file
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Mail
 17 * @subpackage Protocol
 18 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 20 * @version    $Id: Imap.php 20096 2010-01-06 02:05:09Z bkarwin $
 21 */
 22
 23
 24/**
 25 * @category   Zend
 26 * @package    Zend_Mail
 27 * @subpackage Protocol
 28 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 29 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 30 */
 31class Zend_Mail_Protocol_Imap
 32{
 33    /**
 34     * Default timeout in seconds for initiating session
 35     */
 36    const TIMEOUT_CONNECTION = 30;
 37
 38    /**
 39     * socket to imap server
 40     * @var resource|null
 41     */
 42    protected $_socket;
 43
 44    /**
 45     * counter for request tag
 46     * @var int
 47     */
 48    protected $_tagCount = 0;
 49
 50    /**
 51     * Public constructor
 52     *
 53     * @param  string   $host  hostname or IP address of IMAP server, if given connect() is called
 54     * @param  int|null $port  port of IMAP server, null for default (143 or 993 for ssl)
 55     * @param  bool     $ssl   use ssl? 'SSL', 'TLS' or false
 56     * @throws Zend_Mail_Protocol_Exception
 57     */
 58    function __construct($host = '', $port = null, $ssl = false)
 59    {
 60        if ($host) {
 61            $this->connect($host, $port, $ssl);
 62        }
 63    }
 64
 65    /**
 66     * Public destructor
 67     */
 68    public function __destruct()
 69    {
 70        $this->logout();
 71    }
 72
 73    /**
 74     * Open connection to IMAP server
 75     *
 76     * @param  string      $host  hostname or IP address of IMAP server
 77     * @param  int|null    $port  of IMAP server, default is 143 (993 for ssl)
 78     * @param  string|bool $ssl   use 'SSL', 'TLS' or false
 79     * @return string welcome message
 80     * @throws Zend_Mail_Protocol_Exception
 81     */
 82    public function connect($host, $port = null, $ssl = false)
 83    {
 84        if ($ssl == 'SSL') {
 85            $host = 'ssl://' . $host;
 86        }
 87
 88        if ($port === null) {
 89            $port = $ssl === 'SSL' ? 993 : 143;
 90        }
 91
 92        $errno  =  0;
 93        $errstr = '';
 94        $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
 95        if (!$this->_socket) {
 96            /**
 97             * @see Zend_Mail_Protocol_Exception
 98             */
 99            require_once 'Zend/Mail/Protocol/Exception.php';
100            throw new Zend_Mail_Protocol_Exception('cannot connect to host; error = ' . $errstr .
101                                                   ' (errno = ' . $errno . ' )');
102        }
103
104        if (!$this->_assumedNextLine('* OK')) {
105            /**
106             * @see Zend_Mail_Protocol_Exception
107             */
108            require_once 'Zend/Mail/Protocol/Exception.php';
109            throw new Zend_Mail_Protocol_Exception('host doesn\'t allow connection');
110        }
111
112        if ($ssl === 'TLS') {
113            $result = $this->requestAndResponse('STARTTLS');
114            $result = $result && stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
115            if (!$result) {
116                /**
117                 * @see Zend_Mail_Protocol_Exception
118                 */
119                require_once 'Zend/Mail/Protocol/Exception.php';
120                throw new Zend_Mail_Protocol_Exception('cannot enable TLS');
121            }
122        }
123    }
124
125    /**
126     * get the next line from socket with error checking, but nothing else
127     *
128     * @return string next line
129     * @throws Zend_Mail_Protocol_Exception
130     */
131    protected function _nextLine()
132    {
133        $line = @fgets($this->_socket);
134        if ($line === false) {
135            /**
136             * @see Zend_Mail_Protocol_Exception
137             */
138            require_once 'Zend/Mail/Protocol/Exception.php';
139            throw new Zend_Mail_Protocol_Exception('cannot read - connection closed?');
140        }
141
142        return $line;
143    }
144
145    /**
146     * get next line and assume it starts with $start. some requests give a simple
147     * feedback so we can quickly check if we can go on.
148     *
149     * @param  string $start the first bytes we assume to be in the next line
150     * @return bool line starts with $start
151     * @throws Zend_Mail_Protocol_Exception
152     */
153    protected function _assumedNextLine($start)
154    {
155        $line = $this->_nextLine();
156        return strpos($line, $start) === 0;
157    }
158
159    /**
160     * get next line and split the tag. that's the normal case for a response line
161     *
162     * @param  string $tag tag of line is returned by reference
163     * @return string next line
164     * @throws Zend_Mail_Protocol_Exception
165     */
166    protected function _nextTaggedLine(&$tag)
167    {
168        $line = $this->_nextLine();
169
170        // seperate tag from line
171        list($tag, $line) = explode(' ', $line, 2);
172
173        return $line;
174    }
175
176    /**
177     * split a given line in tokens. a token is literal of any form or a list
178     *
179     * @param  string $line line to decode
180     * @return array tokens, literals are returned as string, lists as array
181     * @throws Zend_Mail_Protocol_Exception
182     */
183    protected function _decodeLine($line)
184    {
185        $tokens = array();
186        $stack = array();
187
188        /*
189            We start to decode the response here. The unterstood tokens are:
190                literal
191                "literal" or also "lit\\er\"al"
192                {bytes}<NL>literal
193                (literals*)
194            All tokens are returned in an array. Literals in braces (the last unterstood
195            token in the list) are returned as an array of tokens. I.e. the following response:
196                "foo" baz {3}<NL>bar ("f\\\"oo" bar)
197            would be returned as:
198                array('foo', 'baz', 'bar', array('f\\\"oo', 'bar'));
199
200            // TODO: add handling of '[' and ']' to parser for easier handling of response text
201        */
202        //  replace any trailling <NL> including spaces with a single space
203        $line = rtrim($line) . ' ';
204        while (($pos = strpos($line, ' ')) !== false) {
205            $token = substr($line, 0, $pos);
206            while ($token[0] == '(') {
207                array_push($stack, $tokens);
208                $tokens = array();
209                $token = substr($token, 1);
210            }
211            if ($token[0] == '"') {
212                if (preg_match('%^\(*"((.|\\\\|\\")*?)" *%', $line, $matches)) {
213                    $tokens[] = $matches[1];
214                    $line = substr($line, strlen($matches[0]));
215                    continue;
216                }
217            }
218            if ($token[0] == '{') {
219                $endPos = strpos($token, '}');
220                $chars = substr($token, 1, $endPos - 1);
221                if (is_numeric($chars)) {
222                    $token = '';
223                    while (strlen($token) < $chars) {
224                        $token .= $this->_nextLine();
225                    }
226                    $line = '';
227                    if (strlen($token) > $chars) {
228                        $line = substr($token, $chars);
229                        $token = substr($token, 0, $chars);
230                    } else {
231                        $line .= $this->_nextLine();
232                    }
233                    $tokens[] = $token;
234                    $line = trim($line) . ' ';
235                    continue;
236                }
237            }
238            if ($stack && $token[strlen($token) - 1] == ')') {
239                // closing braces are not seperated by spaces, so we need to count them
240                $braces = strlen($token);
241                $token = rtrim($token, ')');
242                // only count braces if more than one
243                $braces -= strlen($token) + 1;
244                // only add if token had more than just closing braces
245                if (rtrim($token) != '') {
246                    $tokens[] = rtrim($token);
247                }
248                $token = $tokens;
249                $tokens = array_pop($stack);
250                // special handline if more than one closing brace
251                while ($braces-- > 0) {
252                    $tokens[] = $token;
253                    $token = $tokens;
254                    $tokens = array_pop($stack);
255                }
256            }
257            $tokens[] = $token;
258            $line = substr($line, $pos + 1);
259        }
260
261        // maybe the server forgot to send some closing braces
262        while ($stack) {
263            $child = $tokens;
264            $tokens = array_pop($stack);
265            $tokens[] = $child;
266        }
267
268        return $tokens;
269    }
270
271    /**
272     * read a response "line" (could also be more than one real line if response has {..}<NL>)
273     * and do a simple decode
274     *
275     * @param  array|string  $tokens    decoded tokens are returned by reference, if $dontParse
276     *                                  is true the unparsed line is returned here
277     * @param  string        $wantedTag check for this tag for response code. Default '*' is
278     *                                  continuation tag.
279     * @param  bool          $dontParse if true only the unparsed line is returned $tokens
280     * @return bool if returned tag matches wanted tag
281     * @throws Zend_Mail_Protocol_Exception
282     */
283    public function readLine(&$tokens = array(), $wantedTag = '*', $dontParse = false)
284    {
285        $line = $this->_nextTaggedLine($tag);
286        if (!$dontParse) {
287            $tokens = $this->_decodeLine($line);
288        } else {
289            $tokens = $line;
290        }
291
292        // if tag is wanted tag we might be at the end of a multiline response
293        return $tag == $wantedTag;
294    }
295
296    /**
297     * read all lines of response until given tag is found (last line of response)
298     *
299     * @param  string       $tag       the tag of your request
300     * @param  string|array $filter    you can filter the response so you get only the
301     *                                 given response lines
302     * @param  bool         $dontParse if true every line is returned unparsed instead of
303     *                                 the decoded tokens
304     * @return null|bool|array tokens if success, false if error, null if bad request
305     * @throws Zend_Mail_Protocol_Exception
306     */
307    public function readResponse($tag, $dontParse = false)
308    {
309        $lines = array();
310        while (!$this->readLine($tokens, $tag, $dontParse)) {
311            $lines[] = $tokens;
312        }
313
314        if ($dontParse) {
315            // last to chars are still needed for response code
316            $tokens = array(substr($tokens, 0, 2));
317        }
318        // last line has response code
319        if ($tokens[0] == 'OK') {
320            return $lines ? $lines : true;
321        } else if ($tokens[0] == 'NO'){
322            return false;
323        }
324        return null;
325    }
326
327    /**
328     * send a request
329     *
330     * @param  string $command your request command
331     * @param  array  $tokens  additional parameters to command, use escapeString() to prepare
332     * @param  string $tag     provide a tag otherwise an autogenerated is returned
333     * @return null
334     * @throws Zend_Mail_Protocol_Exception
335     */
336    public function sendRequest($command, $tokens = array(), &$tag = null)
337    {
338        if (!$tag) {
339            ++$this->_tagCount;
340            $tag = 'TAG' . $this->_tagCount;
341        }
342
343        $line = $tag . ' ' . $command;
344
345        foreach ($tokens as $token) {
346            if (is_array($token)) {
347                if (@fputs($this->_socket, $line . ' ' . $token[0] . "\r\n") === false) {
348                    /**
349                     * @see Zend_Mail_Protocol_Exception
350                     */
351                    require_once 'Zend/Mail/Protocol/Exception.php';
352                    throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
353                }
354                if (!$this->_assumedNextLine('+ ')) {
355                    /**
356                     * @see Zend_Mail_Protocol_Exception
357                     */
358                    require_once 'Zend/Mail/Protocol/Exception.php';
359                    throw new Zend_Mail_Protocol_Exception('cannot send literal string');
360                }
361                $line = $token[1];
362            } else {
363                $line .= ' ' . $token;
364            }
365        }
366
367        if (@fputs($this->_socket, $line . "\r\n") === false) {
368            /**
369             * @see Zend_Mail_Protocol_Exception
370             */
371            require_once 'Zend/Mail/Protocol/Exception.php';
372            throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
373        }
374    }
375
376    /**
377     * send a request and get response at once
378     *
379     * @param  string $command   command as in sendRequest()
380     * @param  array  $tokens    parameters as in sendRequest()
381     * @param  bool   $dontParse if true unparsed lines are returned instead of tokens
382     * @return mixed response as in readResponse()
383     * @throws Zend_Mail_Protocol_Exception
384     */
385    public function requestAndResponse($command, $tokens = array(), $dontParse = false)
386    {
387        $this->sendRequest($command, $tokens, $tag);
388        $response = $this->readResponse($tag, $dontParse);
389
390        return $response;
391    }
392
393    /**
394     * escape one or more literals i.e. for sendRequest
395     *
396     * @param  string|array $string the literal/-s
397     * @return string|array escape literals, literals with newline ar returned
398     *                      as array('{size}', 'string');
399     */
400    public function escapeString($string)
401    {
402        if (func_num_args() < 2) {
403            if (strpos($string, "\n") !== false) {
404                return array('{' . strlen($string) . '}', $string);
405            } else {
406                return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $string) . '"';
407            }
408        }
409        $result = array();
410        foreach (func_get_args() as $string) {
411            $result[] = $this->escapeString($string);
412        }
413        return $result;
414    }
415
416    /**
417     * escape a list with literals or lists
418     *
419     * @param  array $list list with literals or lists as PHP array
420     * @return string escaped list for imap
421     */
422    public function escapeList($list)
423    {
424        $result = array();
425        foreach ($list as $k => $v) {
426            if (!is_array($v)) {
427//              $result[] = $this->escapeString($v);
428                $result[] = $v;
429                continue;
430            }
431            $result[] = $this->escapeList($v);
432        }
433        return '(' . implode(' ', $result) . ')';
434    }
435
436    /**
437     * Login to IMAP server.
438     *
439     * @param  string $user      username
440     * @param  string $password  password
441     * @return bool success
442     * @throws Zend_Mail_Protocol_Exception
443     */
444    public function login($user, $password)
445    {
446        return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true);
447    }
448
449    /**
450     * logout of imap server
451     *
452     * @return bool success
453     */
454    public function logout()
455    {
456        $result = false;
457        if ($this->_socket) {
458            try {
459                $result = $this->requestAndResponse('LOGOUT', array(), true);
460            } catch (Zend_Mail_Protocol_Exception $e) {
461                // ignoring exception
462            }
463            fclose($this->_socket);
464            $this->_socket = null;
465        }
466        return $result;
467    }
468
469
470    /**
471     * Get capabilities from IMAP server
472     *
473     * @return array list of capabilities
474     * @throws Zend_Mail_Protocol_Exception
475     */
476    public function capability()
477    {
478        $response = $this->requestAndResponse('CAPABILITY');
479
480        if (!$response) {
481            return $response;
482        }
483
484        $capabilities = array();
485        foreach ($response as $line) {
486            $capabilities = array_merge($capabilities, $line);
487        }
488        return $capabilities;
489    }
490
491    /**
492     * Examine and select have the same response. The common code for both
493     * is in this method
494     *
495     * @param  string $command can be 'EXAMINE' or 'SELECT' and this is used as command
496     * @param  string $box which folder to change to or examine
497     * @return bool|array false if error, array with returned information
498     *                    otherwise (flags, exists, recent, uidvalidity)
499     * @throws Zend_Mail_Protocol_Exception
500     */
501    public function examineOrSelect($command = 'EXAMINE', $box = 'INBOX')
502    {
503        $this->sendRequest($command, array($this->escapeString($box)), $tag);
504
505        $result = array();
506        while (!$this->readLine($tokens, $tag)) {
507            if ($tokens[0] == 'FLAGS') {
508                array_shift($tokens);
509                $result['flags'] = $tokens;
510                continue;
511            }
512            switch ($tokens[1]) {
513                case 'EXISTS':
514                case 'RECENT':
515                    $result[strtolower($tokens[1])] = $tokens[0];
516                    break;
517                case '[UIDVALIDITY':
518                    $result['uidvalidity'] = (int)$tokens[2];
519                    break;
520                default:
521                    // ignore
522            }
523        }
524
525        if ($tokens[0] != 'OK') {
526            return false;
527        }
528        return $result;
529    }
530
531    /**
532     * change folder
533     *
534     * @param  string $box change to this folder
535     * @return bool|array see examineOrselect()
536     * @throws Zend_Mail_Protocol_Exception
537     */
538    public function select($box = 'INBOX')
539    {
540        return $this->examineOrSelect('SELECT', $box);
541    }
542
543    /**
544     * examine folder
545     *
546     * @param  string $box examine this folder
547     * @return bool|array see examineOrselect()
548     * @throws Zend_Mail_Protocol_Exception
549     */
550    public function examine($box = 'INBOX')
551    {
552        return $this->examineOrSelect('EXAMINE', $box);
553    }
554
555    /**
556     * fetch one or more items of one or more messages
557     *
558     * @param  string|array $items items to fetch from message(s) as string (if only one item)
559     *                             or array of strings
560     * @param  int          $from  message for items or start message if $to !== null
561     * @param  int|null     $to    if null only one message ($from) is fetched, else it's the
562     *                             last message, INF means last message avaible
563     * @return string|array if only one item of one message is fetched it's returned as string
564     *                      if items of one message are fetched it's returned as (name => value)
565     *                      if one items of messages are fetched it's returned as (msgno => value)
566     *                      if items of messages are fetchted it's returned as (msgno => (name => value))
567     * @throws Zend_Mail_Protocol_Exception
568     */
569    public function fetch($items, $from, $to = null)
570    {
571        if (is_array($from)) {
572            $set = implode(',', $from);
573        } else if ($to === null) {
574            $set = (int)$from;
575        } else if ($to === INF) {
576            $set = (int)$from . ':*';
577        } else {
578            $set = (int)$from . ':' . (int)$to;
579        }
580
581        $items = (array)$items;
582        $itemList = $this->escapeList($items);
583
584        $this->sendRequest('FETCH', array($set, $itemList), $tag);
585
586        $result = array();
587        while (!$this->readLine($tokens, $tag)) {
588            // ignore other responses
589            if ($tokens[1] != 'FETCH') {
590                continue;
591            }
592            // ignore other messages
593            if ($to === null && !is_array($from) && $tokens[0] != $from) {
594                continue;
595            }
596            // if we only want one item we return that one directly
597            if (count($items) == 1) {
598                if ($tokens[2][0] == $items[0]) {
599                    $data = $tokens[2][1];
600                } else {
601                    // maybe the server send an other field we didn't wanted
602                    $count = count($tokens[2]);
603                    // we start with 2, because 0 was already checked
604                    for ($i = 2; $i < $count; $i += 2) {
605                        if ($tokens[2][$i] != $items[0]) {
606                            continue;
607                        }
608                        $data = $tokens[2][$i + 1];
609                        break;
610                    }
611                }
612            } else {
613                $data = array();
614                while (key($tokens[2]) !== null) {
615                    $data[current($tokens[2])] = next($tokens[2]);
616                    next($tokens[2]);
617                }
618            }
619            // if we want only one message we can ignore everything else and just return
620            if ($to === null && !is_array($from) && $tokens[0] == $from) {
621                // we still need to read all lines
622                while (!$this->readLine($tokens, $tag));
623                return $data;
624            }
625            $result[$tokens[0]] = $data;
626        }
627
628        if ($to === null && !is_array($from)) {
629            /**
630             * @see Zend_Mail_Protocol_Exception
631             */
632            require_once 'Zend/Mail/Protocol/Exception.php';
633            throw new Zend_Mail_Protocol_Exception('the single id was not found in response');
634        }
635
636        return $result;
637    }
638
639    /**
640     * get mailbox list
641     *
642     * this method can't be named after the IMAP command 'LIST', as list is a reserved keyword
643     *
644     * @param  string $reference mailbox reference for list
645     * @param  string $mailbox   mailbox name match with wildcards
646     * @return array mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..))
647     * @throws Zend_Mail_Protocol_Exception
648     */
649    public function listMailbox($reference = '', $mailbox = '*')
650    {
651        $result = array();
652        $list = $this->requestAndResponse('LIST', $this->escapeString($reference, $mailbox));
653        if (!$list || $list === true) {
654            return $result;
655        }
656
657        foreach ($list as $item) {
658            if (count($item) != 4 || $item[0] != 'LIST') {
659                continue;
660            }
661            $result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]);
662        }
663
664        return $result;
665    }
666
667    /**
668     * set flags
669     *
670     * @param  array       $flags  flags to set, add or remove - see $mode
671     * @param  int         $from   message for items or start message if $to !== null
672     * @param  int|null    $to     if null only one message ($from) is fetched, else it's the
673     *                             last message, INF means last message avaible
674     * @param  string|null $mode   '+' to add flags, '-' to remove flags, everything else sets the flags as given
675     * @param  bool        $silent if false the return values are the new flags for the wanted messages
676     * @return bool|array new flags if $silent is false, else true or false depending on success
677     * @throws Zend_Mail_Protocol_Exception
678     */
679    public function store(array $flags, $from, $to = null, $mode = null, $silent = true)
680    {
681        $item = 'FLAGS';
682        if ($mode == '+' || $mode == '-') {
683            $item = $mode . $item;
684        }
685        if ($silent) {
686            $item .= '.SILENT';
687        }
688
689        $flags = $this->escapeList($flags);
690        $set = (int)$from;
691        if ($to != null) {
692            $set .= ':' . ($to == INF ? '*' : (int)$to);
693        }
694
695        $result = $this->requestAndResponse('STORE', array($set, $item, $flags), $silent);
696
697        if ($silent) {
698            return $result ? true : false;
699        }
700
701        $tokens = $result;
702        $result = array();
703        foreach ($tokens as $token) {
704            if ($token[1] != 'FETCH' || $token[2][0] != 'FLAGS') {
705                continue;
706            }
707            $result[$token[0]] = $token[2][1];
708        }
709
710        return $result;
711    }
712
713    /**
714     * append a new message to given folder
715     *
716     * @param string $folder  name of target folder
717     * @param string $message full message content
718     * @param array  $flags   flags for new message
719     * @param string $date    date for new message
720     * @return bool success
721     * @throws Zend_Mail_Protocol_Exception
722     */
723    public function append($folder, $message, $flags = null, $date = null)
724    {
725        $tokens = array();
726        $tokens[] = $this->escapeString($folder);
727        if ($flags !== null) {
728            $tokens[] = $this->escapeList($flags);
729        }
730        if ($date !== null) {
731            $tokens[] = $this->escapeString($date);
732        }
733        $tokens[] = $this->escapeString($message);
734
735        return $this->requestAndResponse('APPEND', $tokens, true);
736    }
737
738    /**
739     * copy message set from current folder to other folder
740     *
741     * @param string   $folder destination folder
742     * @param int|null $to     if null only one message ($from) is fetched, else it's the
743     *                         last message, INF means last message avaible
744     * @return bool success
745     * @throws Zend_Mail_Protocol_Exception
746     */
747    public function copy($folder, $from, $to = null)
748    {
749        $set = (int)$from;
750        if ($to != null) {
751            $set .= ':' . ($to == INF ? '*' : (int)$to);
752        }
753
754        return $this->requestAndResponse('COPY', array($set, $this->escapeString($folder)), true);
755    }
756
757    /**
758     * create a new folder (and parent folders if needed)
759     *
760     * @param string $folder folder name
761     * @return bool success
762     */
763    public function create($folder)
764    {
765        return $this->requestAndResponse('CREATE', array($this->escapeString($folder)), true);
766    }
767
768    /**
769     * rename an existing folder
770     *
771     * @param string $old old name
772     * @param string $new new name
773     * @return bool success
774     */
775    public function rename($old, $new)
776    {
777        return $this->requestAndResponse('RENAME', $this->escapeString($old, $new), true);
778    }
779
780    /**
781     * remove a folder
782     *
783     * @param string $folder folder name
784     * @return bool success
785     */
786    public function delete($folder)
787    {
788        return $this->requestAndResponse('DELETE', array($this->escapeString($folder)), true);
789    }
790
791    /**
792     * permanently remove messages
793     *
794     * @return bool success
795     */
796    public function expunge()
797    {
798        // TODO: parse response?
799        return $this->requestAndResponse('EXPUNGE');
800    }
801
802    /**
803     * send noop
804     *
805     * @return bool success
806     */
807    public function noop()
808    {
809        // TODO: parse response
810        return $this->requestAndResponse('NOOP');
811    }
812
813    /**
814     * do a search request
815     *
816     * This method is currently marked as internal as the API might change and is not
817     * safe if you don't take precautions.
818     *
819     * @internal
820     * @return array message ids
821     */
822    public function search(array $params)
823    {
824        $response = $this->requestAndResponse('SEARCH', $params);
825        if (!$response) {
826            return $response;
827        }
828
829        foreach ($response as $ids) {
830            if ($ids[0] == 'SEARCH') {
831                array_shift($ids);
832                return $ids;
833            }
834        }
835        return array();
836    }
837
838}