summaryrefslogtreecommitdiff
path: root/program/include/rcube_imap_generic.php
diff options
context:
space:
mode:
authoralecpl <alec@alec.pl>2011-12-07 08:44:48 +0000
committeralecpl <alec@alec.pl>2011-12-07 08:44:48 +0000
commit40c45e9de99186eda203a925c09424a3a8ec103c (patch)
tree66d59cf5b1aeb38a701976aed8aa97cbb6f00576 /program/include/rcube_imap_generic.php
parent86130d6366a18cf3d558d64f67bcf410ff5f0fc2 (diff)
- Fixed issues with big memory allocation of IMAP results, improved a lot of rcube_imap class
Diffstat (limited to 'program/include/rcube_imap_generic.php')
-rw-r--r--program/include/rcube_imap_generic.php312
1 files changed, 130 insertions, 182 deletions
diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index a4e921fe6..ad147e5fd 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -26,7 +26,6 @@
*/
-
/**
* Struct representing an e-mail message header
*
@@ -1218,8 +1217,8 @@ class rcube_imap_generic
// Invoke SEARCH as a fallback
$index = $this->search($mailbox, 'ALL UNSEEN', false, array('COUNT'));
- if (is_array($index)) {
- return (int) $index['COUNT'];
+ if (!$index->isError()) {
+ return $index->count();
}
return false;
@@ -1293,8 +1292,21 @@ class rcube_imap_generic
return false;
}
- function sort($mailbox, $field, $add='', $is_uid=FALSE, $encoding = 'US-ASCII')
+ /**
+ * Executes SORT command
+ *
+ * @param string $mailbox Mailbox name
+ * @param string $field Field to sort by (ARRIVAL, CC, DATE, FROM, SIZE, SUBJECT, TO)
+ * @param string $add Searching criteria
+ * @param bool $return_uid Enables UID SORT usage
+ * @param string $encoding Character set
+ *
+ * @return rcube_result_index Response data
+ */
+ function sort($mailbox, $field, $add='', $return_uid=false, $encoding = 'US-ASCII')
{
+ require_once dirname(__FILE__) . '/rcube_result_index.php';
+
$field = strtoupper($field);
if ($field == 'INTERNALDATE') {
$field = 'ARRIVAL';
@@ -1304,33 +1316,61 @@ class rcube_imap_generic
'FROM' => 1, 'SIZE' => 1, 'SUBJECT' => 1, 'TO' => 1);
if (!$fields[$field]) {
- return false;
+ return new rcube_result_index($mailbox);
}
if (!$this->select($mailbox)) {
- return false;
+ return new rcube_result_index($mailbox);
}
// message IDs
if (!empty($add))
$add = $this->compressMessageSet($add);
- list($code, $response) = $this->execute($is_uid ? 'UID SORT' : 'SORT',
+ list($code, $response) = $this->execute($return_uid ? 'UID SORT' : 'SORT',
array("($field)", $encoding, 'ALL' . (!empty($add) ? ' '.$add : '')));
- if ($code == self::ERROR_OK) {
- // remove prefix and unilateral untagged server responses
- $response = substr($response, stripos($response, '* SORT') + 7);
- if ($pos = strpos($response, '*')) {
- $response = substr($response, 0, $pos);
- }
- return preg_split('/[\s\r\n]+/', $response, -1, PREG_SPLIT_NO_EMPTY);
+ if ($code != self::ERROR_OK) {
+ $response = null;
}
- return false;
+ return new rcube_result_index($mailbox, $response);
}
- function fetchHeaderIndex($mailbox, $message_set, $index_field='', $skip_deleted=true, $uidfetch=false)
+ /**
+ * Simulates SORT command by using FETCH and sorting.
+ *
+ * @param string $mailbox Mailbox name
+ * @param string|array $message_set Searching criteria (list of messages to return)
+ * @param string $index_field Field to sort by (ARRIVAL, CC, DATE, FROM, SIZE, SUBJECT, TO)
+ * @param bool $skip_deleted Makes that DELETED messages will be skipped
+ * @param bool $uidfetch Enables UID FETCH usage
+ * @param bool $return_uid Enables returning UIDs instead of IDs
+ *
+ * @return rcube_result_index Response data
+ */
+ function index($mailbox, $message_set, $index_field='', $skip_deleted=true,
+ $uidfetch=false, $return_uid=false)
+ {
+ require_once dirname(__FILE__) . '/rcube_result_index.php';
+
+ $msg_index = $this->fetchHeaderIndex($mailbox, $message_set,
+ $index_field, $skip_deleted, $uidfetch, $return_uid);
+
+ if (!empty($msg_index)) {
+ asort($msg_index); // ASC
+ $msg_index = array_keys($msg_index);
+ $msg_index = '* SEARCH ' . implode(' ', $msg_index);
+ }
+ else {
+ $msg_index = is_array($msg_index) ? '* SEARCH' : null;
+ }
+
+ return new rcube_result_index($mailbox, $msg_index);
+ }
+
+ function fetchHeaderIndex($mailbox, $message_set, $index_field='', $skip_deleted=true,
+ $uidfetch=false, $return_uid=false)
{
if (is_array($message_set)) {
if (!($message_set = $this->compressMessageSet($message_set)))
@@ -1370,25 +1410,32 @@ class rcube_imap_generic
}
// build FETCH command string
- $key = $this->nextTag();
- $cmd = $uidfetch ? 'UID FETCH' : 'FETCH';
- $deleted = $skip_deleted ? ' FLAGS' : '';
-
- if ($mode == 1 && $index_field == 'DATE')
- $request = " $cmd $message_set (INTERNALDATE BODY.PEEK[HEADER.FIELDS (DATE)]$deleted)";
- else if ($mode == 1)
- $request = " $cmd $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)]$deleted)";
+ $key = $this->nextTag();
+ $cmd = $uidfetch ? 'UID FETCH' : 'FETCH';
+ $fields = array();
+
+ if ($return_uid)
+ $fields[] = 'UID';
+ if ($skip_deleted)
+ $fields[] = 'FLAGS';
+
+ if ($mode == 1) {
+ if ($index_field == 'DATE')
+ $fields[] = 'INTERNALDATE';
+ $fields[] = "BODY.PEEK[HEADER.FIELDS ($index_field)]";
+ }
else if ($mode == 2) {
if ($index_field == 'SIZE')
- $request = " $cmd $message_set (RFC822.SIZE$deleted)";
- else
- $request = " $cmd $message_set ($index_field$deleted)";
- } else if ($mode == 3)
- $request = " $cmd $message_set (FLAGS)";
- else // 4
- $request = " $cmd $message_set (INTERNALDATE$deleted)";
+ $fields[] = 'RFC822.SIZE';
+ else if (!$return_uid || $index_field != 'UID')
+ $fields[] = $index_field;
+ }
+ else if ($mode == 3 && !$skip_deleted)
+ $fields[] = 'FLAGS';
+ else if ($mode == 4)
+ $fields[] = 'INTERNALDATE';
- $request = $key . $request;
+ $request = "$key $cmd $message_set (" . implode(' ', $fields) . ")";
if (!$this->putLine($request)) {
$this->setError(self::ERROR_COMMAND, "Unable to send command: $request");
@@ -1405,6 +1452,12 @@ class rcube_imap_generic
$id = $m[1];
$flags = NULL;
+ if ($return_uid) {
+ if (preg_match('/UID ([0-9]+)/', $line, $matches))
+ $id = (int) $matches[1];
+ else
+ continue;
+ }
if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) {
$flags = explode(' ', strtoupper($matches[1]));
if (in_array('\\DELETED', $flags)) {
@@ -1534,9 +1587,11 @@ class rcube_imap_generic
function UID2ID($mailbox, $uid)
{
if ($uid > 0) {
- $id_a = $this->search($mailbox, "UID $uid");
- if (is_array($id_a) && count($id_a) == 1) {
- return (int) $id_a[0];
+ $index = $this->search($mailbox, "UID $uid");
+
+ if ($index->count() == 1) {
+ $arr = $index->get();
+ return (int) $arr[0];
}
}
return null;
@@ -1560,23 +1615,16 @@ class rcube_imap_generic
return null;
}
- list($code, $response) = $this->execute('FETCH', array($id, '(UID)'));
+ $index = $this->search($mailbox, $id, true);
- if ($code == self::ERROR_OK && preg_match("/^\* $id FETCH \(UID (.*)\)/i", $response, $m)) {
- return (int) $m[1];
+ if ($index->count() == 1) {
+ $arr = $index->get();
+ return (int) $arr[0];
}
return null;
}
- function fetchUIDs($mailbox, $message_set=null)
- {
- if (empty($message_set))
- $message_set = '1:*';
-
- return $this->fetchHeaderIndex($mailbox, $message_set, 'UID', false);
- }
-
/**
* FETCH command (RFC3501)
*
@@ -1970,68 +2018,30 @@ class rcube_imap_generic
return $r;
}
- // Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about
- // 7 times instead :-) See comments on http://uk2.php.net/references and this article:
- // http://derickrethans.nl/files/phparch-php-variables-article.pdf
- private function parseThread($str, $begin, $end, $root, $parent, $depth, &$depthmap, &$haschildren)
- {
- $node = array();
- if ($str[$begin] != '(') {
- $stop = $begin + strspn($str, '1234567890', $begin, $end - $begin);
- $msg = substr($str, $begin, $stop - $begin);
- if ($msg == 0)
- return $node;
- if (is_null($root))
- $root = $msg;
- $depthmap[$msg] = $depth;
- $haschildren[$msg] = false;
- if (!is_null($parent))
- $haschildren[$parent] = true;
- if ($stop + 1 < $end)
- $node[$msg] = $this->parseThread($str, $stop + 1, $end, $root, $msg, $depth + 1, $depthmap, $haschildren);
- else
- $node[$msg] = array();
- } else {
- $off = $begin;
- while ($off < $end) {
- $start = $off;
- $off++;
- $n = 1;
- while ($n > 0) {
- $p = strpos($str, ')', $off);
- if ($p === false) {
- error_log("Mismatched brackets parsing IMAP THREAD response:");
- error_log(substr($str, ($begin < 10) ? 0 : ($begin - 10), $end - $begin + 20));
- error_log(str_repeat(' ', $off - (($begin < 10) ? 0 : ($begin - 10))));
- return $node;
- }
- $p1 = strpos($str, '(', $off);
- if ($p1 !== false && $p1 < $p) {
- $off = $p1 + 1;
- $n++;
- } else {
- $off = $p + 1;
- $n--;
- }
- }
- $node += $this->parseThread($str, $start + 1, $off - 1, $root, $parent, $depth, $depthmap, $haschildren);
- }
- }
-
- return $node;
- }
-
- function thread($mailbox, $algorithm='REFERENCES', $criteria='', $encoding='US-ASCII')
+ /**
+ * Executes THREAD command
+ *
+ * @param string $mailbox Mailbox name
+ * @param string $algorithm Threading algorithm (ORDEREDSUBJECT, REFERENCES, REFS)
+ * @param string $criteria Searching criteria
+ * @param bool $return_uid Enables UIDs in result instead of sequence numbers
+ * @param string $encoding Character set
+ *
+ * @return rcube_result_thread Thread data
+ */
+ function thread($mailbox, $algorithm='REFERENCES', $criteria='', $return_uid=false, $encoding='US-ASCII')
{
+ require_once dirname(__FILE__) . '/rcube_result_thread.php';
+
$old_sel = $this->selected;
if (!$this->select($mailbox)) {
- return false;
+ return new rcube_result_thread($mailbox);
}
// return empty result when folder is empty and we're just after SELECT
if ($old_sel != $mailbox && !$this->data['EXISTS']) {
- return array(array(), array(), array());
+ return new rcube_result_thread($mailbox);
}
$encoding = $encoding ? trim($encoding) : 'US-ASCII';
@@ -2039,28 +2049,14 @@ class rcube_imap_generic
$criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL';
$data = '';
- list($code, $response) = $this->execute('THREAD', array(
- $algorithm, $encoding, $criteria));
+ list($code, $response) = $this->execute($return_uid ? 'UID THREAD' : 'THREAD',
+ array($algorithm, $encoding, $criteria));
- if ($code == self::ERROR_OK) {
- // remove prefix...
- $response = substr($response, stripos($response, '* THREAD') + 9);
- // ...unilateral untagged server responses
- if ($pos = strpos($response, '*')) {
- $response = substr($response, 0, $pos);
- }
-
- $response = str_replace("\r\n", '', $response);
- $depthmap = array();
- $haschildren = array();
-
- $tree = $this->parseThread($response, 0, strlen($response),
- null, null, 0, $depthmap, $haschildren);
-
- return array($tree, $depthmap, $haschildren);
+ if ($code != self::ERROR_OK) {
+ $response = null;
}
- return false;
+ return new rcube_result_thread($mailbox, $response);
}
/**
@@ -2071,22 +2067,27 @@ class rcube_imap_generic
* @param bool $return_uid Enable UID in result instead of sequence ID
* @param array $items Return items (MIN, MAX, COUNT, ALL)
*
- * @return array Message identifiers or item-value hash
+ * @return rcube_result_index Result data
*/
function search($mailbox, $criteria, $return_uid=false, $items=array())
{
+ require_once dirname(__FILE__) . '/rcube_result_index.php';
+
$old_sel = $this->selected;
if (!$this->select($mailbox)) {
- return false;
+ return new rcube_result_index($mailbox);
}
// return empty result when folder is empty and we're just after SELECT
if ($old_sel != $mailbox && !$this->data['EXISTS']) {
- if (!empty($items))
- return array_combine($items, array_fill(0, count($items), 0));
- else
- return array();
+ return new rcube_result_index($mailbox, '* SEARCH');
+ }
+
+ // If ESEARCH is supported always use ALL
+ // but not when items are specified or using simple id2uid search
+ if (empty($items) && ((int) $criteria != $criteria)) {
+ $items = array('ALL');
}
$esearch = empty($items) ? false : $this->getCapability('ESEARCH');
@@ -2097,6 +2098,7 @@ class rcube_imap_generic
if (!empty($items) && $esearch) {
$params .= 'RETURN (' . implode(' ', $items) . ')';
}
+
if (!empty($criteria)) {
$modseq = stripos($criteria, 'MODSEQ') !== false;
$params .= ($params ? ' ' : '') . $criteria;
@@ -2108,65 +2110,11 @@ class rcube_imap_generic
list($code, $response) = $this->execute($return_uid ? 'UID SEARCH' : 'SEARCH',
array($params));
- if ($code == self::ERROR_OK) {
- // remove prefix...
- $response = substr($response, stripos($response,
- $esearch ? '* ESEARCH' : '* SEARCH') + ($esearch ? 10 : 9));
- // ...and unilateral untagged server responses
- if ($pos = strpos($response, '*')) {
- $response = rtrim(substr($response, 0, $pos));
- }
-
- // remove MODSEQ response
- if ($modseq) {
- if (preg_match('/\(MODSEQ ([0-9]+)\)$/', $response, $m)) {
- $response = substr($response, 0, -strlen($m[0]));
- }
- }
-
- if ($esearch) {
- // Skip prefix: ... (TAG "A285") UID ...
- $this->tokenizeResponse($response, $return_uid ? 2 : 1);
-
- $result = array();
- for ($i=0; $i<count($items); $i++) {
- // If the SEARCH returns no matches, the server MUST NOT
- // include the item result option in the ESEARCH response
- if ($ret = $this->tokenizeResponse($response, 2)) {
- list ($name, $value) = $ret;
- $result[$name] = $value;
- }
- }
-
- return $result;
- }
- else {
- $response = preg_split('/[\s\r\n]+/', $response, -1, PREG_SPLIT_NO_EMPTY);
-
- if (!empty($items)) {
- $result = array();
- if (in_array('COUNT', $items)) {
- $result['COUNT'] = count($response);
- }
- if (in_array('MIN', $items)) {
- $result['MIN'] = !empty($response) ? min($response) : 0;
- }
- if (in_array('MAX', $items)) {
- $result['MAX'] = !empty($response) ? max($response) : 0;
- }
- if (in_array('ALL', $items)) {
- $result['ALL'] = $this->compressMessageSet($response, true);
- }
-
- return $result;
- }
- else {
- return $response;
- }
- }
+ if ($code != self::ERROR_OK) {
+ $response = null;
}
- return false;
+ return new rcube_result_index($mailbox, $response);
}
/**