Changeset e361bfe in github
- Timestamp:
- Dec 8, 2011 4:51:39 AM (18 months ago)
- Branches:
- master, HEAD, courier-fix, dev-browser-capabilities, pdo, release-0.8
- Children:
- 3d4e8a9
- Parents:
- 13ed1e0
- Location:
- program/include
- Files:
-
- 2 edited
-
rcube_imap.php (modified) (1 diff)
-
rcube_imap_generic.php (modified) (28 diffs)
Legend:
- Unmodified
- Added
- Removed
-
program/include/rcube_imap.php
r40c45e9 re361bfe 2373 2373 return false; 2374 2374 2375 $deleted = $this->conn-> delete($mailbox, $uids);2375 $deleted = $this->conn->flag($mailbox, $uids, 'DELETED'); 2376 2376 2377 2377 if ($deleted) { -
program/include/rcube_imap_generic.php
r40c45e9 re361bfe 154 154 * @param bool $endln True if CRLF need to be added at the end of command 155 155 * 156 * @ param intNumber of bytes sent, False on error156 * @return int|bool Number of bytes sent, False on error 157 157 */ 158 158 function putLineC($string, $endln=true) 159 159 { 160 if (!$this->fp) 161 return false; 162 163 if ($endln) 160 if (!$this->fp) { 161 return false; 162 } 163 164 if ($endln) { 164 165 $string .= "\r\n"; 165 166 } 166 167 167 168 $res = 0; … … 199 200 } 200 201 202 /** 203 * Reads line from the connection stream 204 * 205 * @param int $size Buffer size 206 * 207 * @return string Line of text response 208 */ 201 209 function readLine($size=1024) 202 210 { … … 227 235 } 228 236 237 /** 238 * Reads more data from the connection stream when provided 239 * data contain string literal 240 * 241 * @param string $line Response text 242 * @param bool $escape Enables escaping 243 * 244 * @return string Line of text response 245 */ 229 246 function multLine($line, $escape = false) 230 247 { … … 248 265 } 249 266 267 /** 268 * Reads specified number of bytes from the connection stream 269 * 270 * @param int $bytes Number of bytes to get 271 * 272 * @return string Response text 273 */ 250 274 function readBytes($bytes) 251 275 { … … 269 293 } 270 294 295 /** 296 * Reads complete response to the IMAP command 297 * 298 * @param array $untagged Will be filled with untagged response lines 299 * 300 * @return string Response text 301 */ 271 302 function readReply(&$untagged=null) 272 303 { … … 284 315 } 285 316 317 /** 318 * Response parser. 319 * 320 * @param string $string Response text 321 * @param string $err_prefix Error message prefix 322 * 323 * @return int Response status 324 */ 286 325 function parseResult($string, $err_prefix='') 287 326 { … … 327 366 } 328 367 368 /** 369 * Checks connection stream state. 370 * 371 * @return bool True if connection is closed 372 */ 329 373 private function eof() 330 374 { … … 337 381 $start = microtime(true); 338 382 339 if (feof($this->fp) || 383 if (feof($this->fp) || 340 384 ($this->prefs['timeout'] && (microtime(true) - $start > $this->prefs['timeout'])) 341 385 ) { … … 347 391 } 348 392 393 /** 394 * Closes connection stream. 395 */ 349 396 private function closeSocket() 350 397 { … … 353 400 } 354 401 402 /** 403 * Error code/message setter. 404 */ 355 405 function setError($code, $msg='') 356 406 { … … 359 409 } 360 410 361 // check if $string starts with $match (or * BYE/BAD) 411 /** 412 * Checks response status. 413 * Checks if command response line starts with specified prefix (or * BYE/BAD) 414 * 415 * @param string $string Response text 416 * @param string $match Prefix to match with (case-sensitive) 417 * @param bool $error Enables BYE/BAD checking 418 * @param bool $nonempty Enables empty response checking 419 * 420 * @return bool True any check is true or connection is closed. 421 */ 362 422 function startsWith($string, $match, $error=false, $nonempty=false) 363 423 { 364 $len = strlen($match);365 if ($len == 0) {366 return false;367 }368 424 if (!$this->fp) { 369 425 return true; 370 426 } 371 if (strncmp($string, $match, $len) == 0) {427 if (strncmp($string, $match, strlen($match)) == 0) { 372 428 return true; 373 429 } … … 609 665 610 666 /** 611 * Gets thedelimiter667 * Detects hierarchy delimiter 612 668 * 613 669 * @return string The delimiter … … 669 725 } 670 726 727 /** 728 * Connects to IMAP server and authenticates. 729 * 730 * @param string $host Server hostname or IP 731 * @param string $user User name 732 * @param string $password Password 733 * @param array $options Connection and class options 734 * 735 * @return bool True on success, False on failure 736 */ 671 737 function connect($host, $user, $password, $options=null) 672 738 { … … 856 922 } 857 923 924 /** 925 * Checks connection status 926 * 927 * @return bool True if connection is active and user is logged in, False otherwise. 928 */ 858 929 function connected() 859 930 { … … 861 932 } 862 933 934 /** 935 * Closes connection with logout. 936 */ 863 937 function closeConnection() 864 938 { … … 1073 1147 1074 1148 /** 1075 * Executes SUBSCRIBE command1149 * Folder subscription (SUBSCRIBE) 1076 1150 * 1077 1151 * @param string $mailbox Mailbox name … … 1088 1162 1089 1163 /** 1090 * Executes UNSUBSCRIBE command1164 * Folder unsubscription (UNSUBSCRIBE) 1091 1165 * 1092 1166 * @param string $mailbox Mailbox name … … 1097 1171 { 1098 1172 $result = $this->execute('UNSUBSCRIBE', array($this->escape($mailbox)), 1173 self::COMMAND_NORESPONSE); 1174 1175 return ($result == self::ERROR_OK); 1176 } 1177 1178 /** 1179 * Folder creation (CREATE) 1180 * 1181 * @param string $mailbox Mailbox name 1182 * 1183 * @return bool True on success, False on error 1184 */ 1185 function createFolder($mailbox) 1186 { 1187 $result = $this->execute('CREATE', array($this->escape($mailbox)), 1188 self::COMMAND_NORESPONSE); 1189 1190 return ($result == self::ERROR_OK); 1191 } 1192 1193 /** 1194 * Folder renaming (RENAME) 1195 * 1196 * @param string $mailbox Mailbox name 1197 * 1198 * @return bool True on success, False on error 1199 */ 1200 function renameFolder($from, $to) 1201 { 1202 $result = $this->execute('RENAME', array($this->escape($from), $this->escape($to)), 1099 1203 self::COMMAND_NORESPONSE); 1100 1204 … … 1142 1246 1143 1247 /** 1248 * Returns list of mailboxes 1249 * 1250 * @param string $ref Reference name 1251 * @param string $mailbox Mailbox name 1252 * @param array $status_opts (see self::_listMailboxes) 1253 * @param array $select_opts (see self::_listMailboxes) 1254 * 1255 * @return array List of mailboxes or hash of options if $status_opts argument 1256 * is non-empty. 1257 */ 1258 function listMailboxes($ref, $mailbox, $status_opts=array(), $select_opts=array()) 1259 { 1260 return $this->_listMailboxes($ref, $mailbox, false, $status_opts, $select_opts); 1261 } 1262 1263 /** 1264 * Returns list of subscribed mailboxes 1265 * 1266 * @param string $ref Reference name 1267 * @param string $mailbox Mailbox name 1268 * @param array $status_opts (see self::_listMailboxes) 1269 * 1270 * @return array List of mailboxes or hash of options if $status_opts argument 1271 * is non-empty. 1272 */ 1273 function listSubscribed($ref, $mailbox, $status_opts=array()) 1274 { 1275 return $this->_listMailboxes($ref, $mailbox, true, $status_opts, NULL); 1276 } 1277 1278 /** 1279 * IMAP LIST/LSUB command 1280 * 1281 * @param string $ref Reference name 1282 * @param string $mailbox Mailbox name 1283 * @param bool $subscribed Enables returning subscribed mailboxes only 1284 * @param array $status_opts List of STATUS options (RFC5819: LIST-STATUS) 1285 * Possible: MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, UNSEEN 1286 * @param array $select_opts List of selection options (RFC5258: LIST-EXTENDED) 1287 * Possible: SUBSCRIBED, RECURSIVEMATCH, REMOTE 1288 * 1289 * @return array List of mailboxes or hash of options if $status_ops argument 1290 * is non-empty. 1291 */ 1292 private function _listMailboxes($ref, $mailbox, $subscribed=false, 1293 $status_opts=array(), $select_opts=array()) 1294 { 1295 if (!strlen($mailbox)) { 1296 $mailbox = '*'; 1297 } 1298 1299 $args = array(); 1300 1301 if (!empty($select_opts) && $this->getCapability('LIST-EXTENDED')) { 1302 $select_opts = (array) $select_opts; 1303 1304 $args[] = '(' . implode(' ', $select_opts) . ')'; 1305 } 1306 1307 $args[] = $this->escape($ref); 1308 $args[] = $this->escape($mailbox); 1309 1310 if (!empty($status_opts) && $this->getCapability('LIST-STATUS')) { 1311 $status_opts = (array) $status_opts; 1312 $lstatus = true; 1313 1314 $args[] = 'RETURN (STATUS (' . implode(' ', $status_opts) . '))'; 1315 } 1316 1317 list($code, $response) = $this->execute($subscribed ? 'LSUB' : 'LIST', $args); 1318 1319 if ($code == self::ERROR_OK) { 1320 $folders = array(); 1321 $last = 0; 1322 $pos = 0; 1323 $response .= "\r\n"; 1324 1325 while ($pos = strpos($response, "\r\n", $pos+1)) { 1326 // literal string, not real end-of-command-line 1327 if ($response[$pos-1] == '}') { 1328 continue; 1329 } 1330 1331 $line = substr($response, $last, $pos - $last); 1332 $last = $pos + 2; 1333 1334 if (!preg_match('/^\* (LIST|LSUB|STATUS) /i', $line, $m)) { 1335 continue; 1336 } 1337 $cmd = strtoupper($m[1]); 1338 $line = substr($line, strlen($m[0])); 1339 1340 // * LIST (<options>) <delimiter> <mailbox> 1341 if ($cmd == 'LIST' || $cmd == 'LSUB') { 1342 list($opts, $delim, $mailbox) = $this->tokenizeResponse($line, 3); 1343 1344 // Add to result array 1345 if (!$lstatus) { 1346 $folders[] = $mailbox; 1347 } 1348 else { 1349 $folders[$mailbox] = array(); 1350 } 1351 1352 // Add to options array 1353 if (empty($this->data['LIST'][$mailbox])) 1354 $this->data['LIST'][$mailbox] = $opts; 1355 else if (!empty($opts)) 1356 $this->data['LIST'][$mailbox] = array_unique(array_merge( 1357 $this->data['LIST'][$mailbox], $opts)); 1358 } 1359 // * STATUS <mailbox> (<result>) 1360 else if ($cmd == 'STATUS') { 1361 list($mailbox, $status) = $this->tokenizeResponse($line, 2); 1362 1363 for ($i=0, $len=count($status); $i<$len; $i += 2) { 1364 list($name, $value) = $this->tokenizeResponse($status, 2); 1365 $folders[$mailbox][$name] = $value; 1366 } 1367 } 1368 } 1369 1370 return $folders; 1371 } 1372 1373 return false; 1374 } 1375 1376 /** 1144 1377 * Returns count of all messages in a folder 1145 1378 * … … 1330 1563 list($code, $response) = $this->execute($return_uid ? 'UID SORT' : 'SORT', 1331 1564 array("($field)", $encoding, 'ALL' . (!empty($add) ? ' '.$add : ''))); 1565 1566 if ($code != self::ERROR_OK) { 1567 $response = null; 1568 } 1569 1570 return new rcube_result_index($mailbox, $response); 1571 } 1572 1573 /** 1574 * Executes THREAD command 1575 * 1576 * @param string $mailbox Mailbox name 1577 * @param string $algorithm Threading algorithm (ORDEREDSUBJECT, REFERENCES, REFS) 1578 * @param string $criteria Searching criteria 1579 * @param bool $return_uid Enables UIDs in result instead of sequence numbers 1580 * @param string $encoding Character set 1581 * 1582 * @return rcube_result_thread Thread data 1583 */ 1584 function thread($mailbox, $algorithm='REFERENCES', $criteria='', $return_uid=false, $encoding='US-ASCII') 1585 { 1586 require_once dirname(__FILE__) . '/rcube_result_thread.php'; 1587 1588 $old_sel = $this->selected; 1589 1590 if (!$this->select($mailbox)) { 1591 return new rcube_result_thread($mailbox); 1592 } 1593 1594 // return empty result when folder is empty and we're just after SELECT 1595 if ($old_sel != $mailbox && !$this->data['EXISTS']) { 1596 return new rcube_result_thread($mailbox); 1597 } 1598 1599 $encoding = $encoding ? trim($encoding) : 'US-ASCII'; 1600 $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES'; 1601 $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL'; 1602 $data = ''; 1603 1604 list($code, $response) = $this->execute($return_uid ? 'UID THREAD' : 'THREAD', 1605 array($algorithm, $encoding, $criteria)); 1606 1607 if ($code != self::ERROR_OK) { 1608 $response = null; 1609 } 1610 1611 return new rcube_result_thread($mailbox, $response); 1612 } 1613 1614 /** 1615 * Executes SEARCH command 1616 * 1617 * @param string $mailbox Mailbox name 1618 * @param string $criteria Searching criteria 1619 * @param bool $return_uid Enable UID in result instead of sequence ID 1620 * @param array $items Return items (MIN, MAX, COUNT, ALL) 1621 * 1622 * @return rcube_result_index Result data 1623 */ 1624 function search($mailbox, $criteria, $return_uid=false, $items=array()) 1625 { 1626 require_once dirname(__FILE__) . '/rcube_result_index.php'; 1627 1628 $old_sel = $this->selected; 1629 1630 if (!$this->select($mailbox)) { 1631 return new rcube_result_index($mailbox); 1632 } 1633 1634 // return empty result when folder is empty and we're just after SELECT 1635 if ($old_sel != $mailbox && !$this->data['EXISTS']) { 1636 return new rcube_result_index($mailbox, '* SEARCH'); 1637 } 1638 1639 // If ESEARCH is supported always use ALL 1640 // but not when items are specified or using simple id2uid search 1641 if (empty($items) && ((int) $criteria != $criteria)) { 1642 $items = array('ALL'); 1643 } 1644 1645 $esearch = empty($items) ? false : $this->getCapability('ESEARCH'); 1646 $criteria = trim($criteria); 1647 $params = ''; 1648 1649 // RFC4731: ESEARCH 1650 if (!empty($items) && $esearch) { 1651 $params .= 'RETURN (' . implode(' ', $items) . ')'; 1652 } 1653 1654 if (!empty($criteria)) { 1655 $modseq = stripos($criteria, 'MODSEQ') !== false; 1656 $params .= ($params ? ' ' : '') . $criteria; 1657 } 1658 else { 1659 $params .= 'ALL'; 1660 } 1661 1662 list($code, $response) = $this->execute($return_uid ? 'UID SEARCH' : 'SEARCH', 1663 array($params)); 1332 1664 1333 1665 if ($code != self::ERROR_OK) { … … 1511 1843 } 1512 1844 1513 static function compressMessageSet($messages, $force=false)1514 {1515 // given a comma delimited list of independent mid's,1516 // compresses by grouping sequences together1517 1518 if (!is_array($messages)) {1519 // if less than 255 bytes long, let's not bother1520 if (!$force && strlen($messages)<255) {1521 return $messages;1522 }1523 1524 // see if it's already been compressed1525 if (strpos($messages, ':') !== false) {1526 return $messages;1527 }1528 1529 // separate, then sort1530 $messages = explode(',', $messages);1531 }1532 1533 sort($messages);1534 1535 $result = array();1536 $start = $prev = $messages[0];1537 1538 foreach ($messages as $id) {1539 $incr = $id - $prev;1540 if ($incr > 1) { // found a gap1541 if ($start == $prev) {1542 $result[] = $prev; // push single id1543 } else {1544 $result[] = $start . ':' . $prev; // push sequence as start_id:end_id1545 }1546 $start = $id; // start of new sequence1547 }1548 $prev = $id;1549 }1550 1551 // handle the last sequence/id1552 if ($start == $prev) {1553 $result[] = $prev;1554 } else {1555 $result[] = $start.':'.$prev;1556 }1557 1558 // return as comma separated string1559 return implode(',', $result);1560 }1561 1562 static function uncompressMessageSet($messages)1563 {1564 $result = array();1565 $messages = explode(',', $messages);1566 1567 foreach ($messages as $part) {1568 $items = explode(':', $part);1569 $max = max($items[0], $items[1]);1570 1571 for ($x=$items[0]; $x<=$max; $x++) {1572 $result[] = $x;1573 }1574 }1575 1576 return $result;1577 }1578 1579 1845 /** 1580 1846 * Returns message sequence identifier … … 1624 1890 1625 1891 return null; 1892 } 1893 1894 /** 1895 * Sets flag of the message(s) 1896 * 1897 * @param string $mailbox Mailbox name 1898 * @param string|array $messages Message UID(s) 1899 * @param string $flag Flag name 1900 * 1901 * @return bool True on success, False on failure 1902 */ 1903 function flag($mailbox, $messages, $flag) { 1904 return $this->modFlag($mailbox, $messages, $flag, '+'); 1905 } 1906 1907 /** 1908 * Unsets flag of the message(s) 1909 * 1910 * @param string $mailbox Mailbox name 1911 * @param string|array $messages Message UID(s) 1912 * @param string $flag Flag name 1913 * 1914 * @return bool True on success, False on failure 1915 */ 1916 function unflag($mailbox, $messages, $flag) { 1917 return $this->modFlag($mailbox, $messages, $flag, '-'); 1918 } 1919 1920 /** 1921 * Changes flag of the message(s) 1922 * 1923 * @param string $mailbox Mailbox name 1924 * @param string|array $messages Message UID(s) 1925 * @param string $flag Flag name 1926 * @param string $mod Modifier [+|-]. Default: "+". 1927 * 1928 * @return bool True on success, False on failure 1929 */ 1930 private function modFlag($mailbox, $messages, $flag, $mod = '+') 1931 { 1932 if ($mod != '+' && $mod != '-') { 1933 $mod = '+'; 1934 } 1935 1936 if (!$this->select($mailbox)) { 1937 return false; 1938 } 1939 1940 if (!$this->data['READ-WRITE']) { 1941 $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); 1942 return false; 1943 } 1944 1945 // Clear internal status cache 1946 if ($flag == 'SEEN') { 1947 unset($this->data['STATUS:'.$mailbox]['UNSEEN']); 1948 } 1949 1950 $flag = $this->flags[strtoupper($flag)]; 1951 $result = $this->execute('UID STORE', array( 1952 $this->compressMessageSet($messages), $mod . 'FLAGS.SILENT', "($flag)"), 1953 self::COMMAND_NORESPONSE); 1954 1955 return ($result == self::ERROR_OK); 1956 } 1957 1958 /** 1959 * Copies message(s) from one folder to another 1960 * 1961 * @param string|array $messages Message UID(s) 1962 * @param string $from Mailbox name 1963 * @param string $to Destination mailbox name 1964 * 1965 * @return bool True on success, False on failure 1966 */ 1967 function copy($messages, $from, $to) 1968 { 1969 if (!$this->select($from)) { 1970 return false; 1971 } 1972 1973 // Clear internal status cache 1974 unset($this->data['STATUS:'.$to]); 1975 1976 $result = $this->execute('UID COPY', array( 1977 $this->compressMessageSet($messages), $this->escape($to)), 1978 self::COMMAND_NORESPONSE); 1979 1980 return ($result == self::ERROR_OK); 1981 } 1982 1983 /** 1984 * Moves message(s) from one folder to another. 1985 * Original message(s) will be marked as deleted. 1986 * 1987 * @param string|array $messages Message UID(s) 1988 * @param string $from Mailbox name 1989 * @param string $to Destination mailbox name 1990 * 1991 * @return bool True on success, False on failure 1992 */ 1993 function move($messages, $from, $to) 1994 { 1995 if (!$this->select($from)) { 1996 return false; 1997 } 1998 1999 if (!$this->data['READ-WRITE']) { 2000 $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); 2001 return false; 2002 } 2003 2004 $r = $this->copy($messages, $from, $to); 2005 2006 if ($r) { 2007 // Clear internal status cache 2008 unset($this->data['STATUS:'.$from]); 2009 2010 return $this->flag($from, $messages, 'DELETED'); 2011 } 2012 return $r; 1626 2013 } 1627 2014 … … 1940 2327 } 1941 2328 1942 1943 function modFlag($mailbox, $messages, $flag, $mod)1944 {1945 if ($mod != '+' && $mod != '-') {1946 $mod = '+';1947 }1948 1949 if (!$this->select($mailbox)) {1950 return false;1951 }1952 1953 if (!$this->data['READ-WRITE']) {1954 $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE');1955 return false;1956 }1957 1958 // Clear internal status cache1959 if ($flag == 'SEEN') {1960 unset($this->data['STATUS:'.$mailbox]['UNSEEN']);1961 }1962 1963 $flag = $this->flags[strtoupper($flag)];1964 $result = $this->execute('UID STORE', array(1965 $this->compressMessageSet($messages), $mod . 'FLAGS.SILENT', "($flag)"),1966 self::COMMAND_NORESPONSE);1967 1968 return ($result == self::ERROR_OK);1969 }1970 1971 function flag($mailbox, $messages, $flag) {1972 return $this->modFlag($mailbox, $messages, $flag, '+');1973 }1974 1975 function unflag($mailbox, $messages, $flag) {1976 return $this->modFlag($mailbox, $messages, $flag, '-');1977 }1978 1979 function delete($mailbox, $messages) {1980 return $this->modFlag($mailbox, $messages, 'DELETED', '+');1981 }1982 1983 function copy($messages, $from, $to)1984 {1985 if (!$this->select($from)) {1986 return false;1987 }1988 1989 // Clear internal status cache1990 unset($this->data['STATUS:'.$to]);1991 1992 $result = $this->execute('UID COPY', array(1993 $this->compressMessageSet($messages), $this->escape($to)),1994 self::COMMAND_NORESPONSE);1995 1996 return ($result == self::ERROR_OK);1997 }1998 1999 function move($messages, $from, $to)2000 {2001 if (!$this->select($from)) {2002 return false;2003 }2004 2005 if (!$this->data['READ-WRITE']) {2006 $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE');2007 return false;2008 }2009 2010 $r = $this->copy($messages, $from, $to);2011 2012 if ($r) {2013 // Clear internal status cache2014 unset($this->data['STATUS:'.$from]);2015 2016 return $this->delete($from, $messages);2017 }2018 return $r;2019 }2020 2021 /**2022 * Executes THREAD command2023 *2024 * @param string $mailbox Mailbox name2025 * @param string $algorithm Threading algorithm (ORDEREDSUBJECT, REFERENCES, REFS)2026 * @param string $criteria Searching criteria2027 * @param bool $return_uid Enables UIDs in result instead of sequence numbers2028 * @param string $encoding Character set2029 *2030 * @return rcube_result_thread Thread data2031 */2032 function thread($mailbox, $algorithm='REFERENCES', $criteria='', $return_uid=false, $encoding='US-ASCII')2033 {2034 require_once dirname(__FILE__) . '/rcube_result_thread.php';2035 2036 $old_sel = $this->selected;2037 2038 if (!$this->select($mailbox)) {2039 return new rcube_result_thread($mailbox);2040 }2041 2042 // return empty result when folder is empty and we're just after SELECT2043 if ($old_sel != $mailbox && !$this->data['EXISTS']) {2044 return new rcube_result_thread($mailbox);2045 }2046 2047 $encoding = $encoding ? trim($encoding) : 'US-ASCII';2048 $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES';2049 $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL';2050 $data = '';2051 2052 list($code, $response) = $this->execute($return_uid ? 'UID THREAD' : 'THREAD',2053 array($algorithm, $encoding, $criteria));2054 2055 if ($code != self::ERROR_OK) {2056 $response = null;2057 }2058 2059 return new rcube_result_thread($mailbox, $response);2060 }2061 2062 /**2063 * Executes SEARCH command2064 *2065 * @param string $mailbox Mailbox name2066 * @param string $criteria Searching criteria2067 * @param bool $return_uid Enable UID in result instead of sequence ID2068 * @param array $items Return items (MIN, MAX, COUNT, ALL)2069 *2070 * @return rcube_result_index Result data2071 */2072 function search($mailbox, $criteria, $return_uid=false, $items=array())2073 {2074 require_once dirname(__FILE__) . '/rcube_result_index.php';2075 2076 $old_sel = $this->selected;2077 2078 if (!$this->select($mailbox)) {2079 return new rcube_result_index($mailbox);2080 }2081 2082 // return empty result when folder is empty and we're just after SELECT2083 if ($old_sel != $mailbox && !$this->data['EXISTS']) {2084 return new rcube_result_index($mailbox, '* SEARCH');2085 }2086 2087 // If ESEARCH is supported always use ALL2088 // but not when items are specified or using simple id2uid search2089 if (empty($items) && ((int) $criteria != $criteria)) {2090 $items = array('ALL');2091 }2092 2093 $esearch = empty($items) ? false : $this->getCapability('ESEARCH');2094 $criteria = trim($criteria);2095 $params = '';2096 2097 // RFC4731: ESEARCH2098 if (!empty($items) && $esearch) {2099 $params .= 'RETURN (' . implode(' ', $items) . ')';2100 }2101 2102 if (!empty($criteria)) {2103 $modseq = stripos($criteria, 'MODSEQ') !== false;2104 $params .= ($params ? ' ' : '') . $criteria;2105 }2106 else {2107 $params .= 'ALL';2108 }2109 2110 list($code, $response) = $this->execute($return_uid ? 'UID SEARCH' : 'SEARCH',2111 array($params));2112 2113 if ($code != self::ERROR_OK) {2114 $response = null;2115 }2116 2117 return new rcube_result_index($mailbox, $response);2118 }2119 2120 /**2121 * Returns list of mailboxes2122 *2123 * @param string $ref Reference name2124 * @param string $mailbox Mailbox name2125 * @param array $status_opts (see self::_listMailboxes)2126 * @param array $select_opts (see self::_listMailboxes)2127 *2128 * @return array List of mailboxes or hash of options if $status_opts argument2129 * is non-empty.2130 */2131 function listMailboxes($ref, $mailbox, $status_opts=array(), $select_opts=array())2132 {2133 return $this->_listMailboxes($ref, $mailbox, false, $status_opts, $select_opts);2134 }2135 2136 /**2137 * Returns list of subscribed mailboxes2138 *2139 * @param string $ref Reference name2140 * @param string $mailbox Mailbox name2141 * @param array $status_opts (see self::_listMailboxes)2142 *2143 * @return array List of mailboxes or hash of options if $status_opts argument2144 * is non-empty.2145 */2146 function listSubscribed($ref, $mailbox, $status_opts=array())2147 {2148 return $this->_listMailboxes($ref, $mailbox, true, $status_opts, NULL);2149 }2150 2151 /**2152 * IMAP LIST/LSUB command2153 *2154 * @param string $ref Reference name2155 * @param string $mailbox Mailbox name2156 * @param bool $subscribed Enables returning subscribed mailboxes only2157 * @param array $status_opts List of STATUS options (RFC5819: LIST-STATUS)2158 * Possible: MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, UNSEEN2159 * @param array $select_opts List of selection options (RFC5258: LIST-EXTENDED)2160 * Possible: SUBSCRIBED, RECURSIVEMATCH, REMOTE2161 *2162 * @return array List of mailboxes or hash of options if $status_ops argument2163 * is non-empty.2164 */2165 private function _listMailboxes($ref, $mailbox, $subscribed=false,2166 $status_opts=array(), $select_opts=array())2167 {2168 if (!strlen($mailbox)) {2169 $mailbox = '*';2170 }2171 2172 $args = array();2173 2174 if (!empty($select_opts) && $this->getCapability('LIST-EXTENDED')) {2175 $select_opts = (array) $select_opts;2176 2177 $args[] = '(' . implode(' ', $select_opts) . ')';2178 }2179 2180 $args[] = $this->escape($ref);2181 $args[] = $this->escape($mailbox);2182 2183 if (!empty($status_opts) && $this->getCapability('LIST-STATUS')) {2184 $status_opts = (array) $status_opts;2185 $lstatus = true;2186 2187 $args[] = 'RETURN (STATUS (' . implode(' ', $status_opts) . '))';2188 }2189 2190 list($code, $response) = $this->execute($subscribed ? 'LSUB' : 'LIST', $args);2191 2192 if ($code == self::ERROR_OK) {2193 $folders = array();2194 $last = 0;2195 $pos = 0;2196 $response .= "\r\n";2197 2198 while ($pos = strpos($response, "\r\n", $pos+1)) {2199 // literal string, not real end-of-command-line2200 if ($response[$pos-1] == '}') {2201 continue;2202 }2203 2204 $line = substr($response, $last, $pos - $last);2205 $last = $pos + 2;2206 2207 if (!preg_match('/^\* (LIST|LSUB|STATUS) /i', $line, $m)) {2208 continue;2209 }2210 $cmd = strtoupper($m[1]);2211 $line = substr($line, strlen($m[0]));2212 2213 // * LIST (<options>) <delimiter> <mailbox>2214 if ($cmd == 'LIST' || $cmd == 'LSUB') {2215 list($opts, $delim, $mailbox) = $this->tokenizeResponse($line, 3);2216 2217 // Add to result array2218 if (!$lstatus) {2219 $folders[] = $mailbox;2220 }2221 else {2222 $folders[$mailbox] = array();2223 }2224 2225 // Add to options array2226 if (empty($this->data['LIST'][$mailbox]))2227 $this->data['LIST'][$mailbox] = $opts;2228 else if (!empty($opts))2229 $this->data['LIST'][$mailbox] = array_unique(array_merge(2230 $this->data['LIST'][$mailbox], $opts));2231 }2232 // * STATUS <mailbox> (<result>)2233 else if ($cmd == 'STATUS') {2234 list($mailbox, $status) = $this->tokenizeResponse($line, 2);2235 2236 for ($i=0, $len=count($status); $i<$len; $i += 2) {2237 list($name, $value) = $this->tokenizeResponse($status, 2);2238 $folders[$mailbox][$name] = $value;2239 }2240 }2241 }2242 2243 return $folders;2244 }2245 2246 return false;2247 }2248 2249 2329 function fetchMIMEHeaders($mailbox, $uid, $parts, $mime=true) 2250 2330 { … … 2455 2535 } 2456 2536 2457 function createFolder($mailbox)2458 {2459 $result = $this->execute('CREATE', array($this->escape($mailbox)),2460 self::COMMAND_NORESPONSE);2461 2462 return ($result == self::ERROR_OK);2463 }2464 2465 function renameFolder($from, $to)2466 {2467 $result = $this->execute('RENAME', array($this->escape($from), $this->escape($to)),2468 self::COMMAND_NORESPONSE);2469 2470 return ($result == self::ERROR_OK);2471 }2472 2473 2537 /** 2474 2538 * Handler for IMAP APPEND command … … 2628 2692 } 2629 2693 2694 /** 2695 * Returns QUOTA information 2696 * 2697 * @return array Quota information 2698 */ 2630 2699 function getQuota() 2631 2700 { … … 3382 3451 } 3383 3452 3453 /** 3454 * Converts message identifiers array into sequence-set syntax 3455 * 3456 * @param array $messages Message identifiers 3457 * @param bool $force Forces compression of any size 3458 * 3459 * @return string Compressed sequence-set 3460 */ 3461 static function compressMessageSet($messages, $force=false) 3462 { 3463 // given a comma delimited list of independent mid's, 3464 // compresses by grouping sequences together 3465 3466 if (!is_array($messages)) { 3467 // if less than 255 bytes long, let's not bother 3468 if (!$force && strlen($messages)<255) { 3469 return $messages; 3470 } 3471 3472 // see if it's already been compressed 3473 if (strpos($messages, ':') !== false) { 3474 return $messages; 3475 } 3476 3477 // separate, then sort 3478 $messages = explode(',', $messages); 3479 } 3480 3481 sort($messages); 3482 3483 $result = array(); 3484 $start = $prev = $messages[0]; 3485 3486 foreach ($messages as $id) { 3487 $incr = $id - $prev; 3488 if ($incr > 1) { // found a gap 3489 if ($start == $prev) { 3490 $result[] = $prev; // push single id 3491 } else { 3492 $result[] = $start . ':' . $prev; // push sequence as start_id:end_id 3493 } 3494 $start = $id; // start of new sequence 3495 } 3496 $prev = $id; 3497 } 3498 3499 // handle the last sequence/id 3500 if ($start == $prev) { 3501 $result[] = $prev; 3502 } else { 3503 $result[] = $start.':'.$prev; 3504 } 3505 3506 // return as comma separated string 3507 return implode(',', $result); 3508 } 3509 3510 /** 3511 * Converts message sequence-set into array 3512 * 3513 * @param string $messages Message identifiers 3514 * 3515 * @return array List of message identifiers 3516 */ 3517 static function uncompressMessageSet($messages) 3518 { 3519 $result = array(); 3520 $messages = explode(',', $messages); 3521 3522 foreach ($messages as $idx => $part) { 3523 $items = explode(':', $part); 3524 $max = max($items[0], $items[1]); 3525 3526 for ($x=$items[0]; $x<=$max; $x++) { 3527 $result[] = $x; 3528 } 3529 unset($messages[$idx]); 3530 } 3531 3532 return $result; 3533 } 3534 3384 3535 private function _xor($string, $string2) 3385 3536 { … … 3420 3571 } 3421 3572 3573 /** 3574 * CAPABILITY response parser 3575 */ 3422 3576 private function parseCapability($str, $trusted=false) 3423 3577 { … … 3465 3619 } 3466 3620 3621 /** 3622 * Unescapes quoted-string 3623 * 3624 * @param string $string IMAP string 3625 * 3626 * @return string String 3627 */ 3467 3628 static function unEscape($string) 3468 3629 {
Note: See TracChangeset
for help on using the changeset viewer.
