/zf/library/Zend/Mail/Protocol/Imap.php
PHP | 838 lines | 431 code | 71 blank | 336 comment | 114 complexity | 390607e16032e5efc992c0f0cd58359a MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
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-2011 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 23775 2011-03-01 17:25:24Z ralph $ 21 */ 22 23 24/** 25 * @category Zend 26 * @package Zend_Mail 27 * @subpackage Protocol 28 * @copyright Copyright (c) 2005-2011 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}