Ignore:
Timestamp:
Dec 7, 2011 3:44:48 AM (18 months ago)
Author:
alec
Message:
  • Fixed issues with big memory allocation of IMAP results, improved a lot of rcube_imap class
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail/program/include/rcube_imap_generic.php

    r5400 r5557  
    2626 
    2727*/ 
    28  
    2928 
    3029/** 
     
    12191218        // Invoke SEARCH as a fallback 
    12201219        $index = $this->search($mailbox, 'ALL UNSEEN', false, array('COUNT')); 
    1221         if (is_array($index)) { 
    1222             return (int) $index['COUNT']; 
     1220        if (!$index->isError()) { 
     1221            return $index->count(); 
    12231222        } 
    12241223 
     
    12941293    } 
    12951294 
    1296     function sort($mailbox, $field, $add='', $is_uid=FALSE, $encoding = 'US-ASCII') 
    1297     { 
     1295    /** 
     1296     * Executes SORT command 
     1297     * 
     1298     * @param string $mailbox    Mailbox name 
     1299     * @param string $field      Field to sort by (ARRIVAL, CC, DATE, FROM, SIZE, SUBJECT, TO) 
     1300     * @param string $add        Searching criteria 
     1301     * @param bool   $return_uid Enables UID SORT usage 
     1302     * @param string $encoding   Character set 
     1303     * 
     1304     * @return rcube_result_index Response data 
     1305     */ 
     1306    function sort($mailbox, $field, $add='', $return_uid=false, $encoding = 'US-ASCII') 
     1307    { 
     1308        require_once dirname(__FILE__) . '/rcube_result_index.php'; 
     1309 
    12981310        $field = strtoupper($field); 
    12991311        if ($field == 'INTERNALDATE') { 
     
    13051317 
    13061318        if (!$fields[$field]) { 
    1307             return false; 
     1319            return new rcube_result_index($mailbox); 
    13081320        } 
    13091321 
    13101322        if (!$this->select($mailbox)) { 
    1311             return false; 
     1323            return new rcube_result_index($mailbox); 
    13121324        } 
    13131325 
     
    13161328            $add = $this->compressMessageSet($add); 
    13171329 
    1318         list($code, $response) = $this->execute($is_uid ? 'UID SORT' : 'SORT', 
     1330        list($code, $response) = $this->execute($return_uid ? 'UID SORT' : 'SORT', 
    13191331            array("($field)", $encoding, 'ALL' . (!empty($add) ? ' '.$add : ''))); 
    13201332 
    1321         if ($code == self::ERROR_OK) { 
    1322             // remove prefix and unilateral untagged server responses 
    1323             $response = substr($response, stripos($response, '* SORT') + 7); 
    1324             if ($pos = strpos($response, '*')) { 
    1325                 $response = substr($response, 0, $pos); 
    1326             } 
    1327             return preg_split('/[\s\r\n]+/', $response, -1, PREG_SPLIT_NO_EMPTY); 
    1328         } 
    1329  
    1330         return false; 
    1331     } 
    1332  
    1333     function fetchHeaderIndex($mailbox, $message_set, $index_field='', $skip_deleted=true, $uidfetch=false) 
     1333        if ($code != self::ERROR_OK) { 
     1334            $response = null; 
     1335        } 
     1336 
     1337        return new rcube_result_index($mailbox, $response); 
     1338    } 
     1339 
     1340    /** 
     1341     * Simulates SORT command by using FETCH and sorting. 
     1342     * 
     1343     * @param string       $mailbox      Mailbox name 
     1344     * @param string|array $message_set  Searching criteria (list of messages to return) 
     1345     * @param string       $index_field  Field to sort by (ARRIVAL, CC, DATE, FROM, SIZE, SUBJECT, TO) 
     1346     * @param bool         $skip_deleted Makes that DELETED messages will be skipped 
     1347     * @param bool         $uidfetch     Enables UID FETCH usage 
     1348     * @param bool         $return_uid   Enables returning UIDs instead of IDs 
     1349     * 
     1350     * @return rcube_result_index Response data 
     1351     */ 
     1352    function index($mailbox, $message_set, $index_field='', $skip_deleted=true, 
     1353        $uidfetch=false, $return_uid=false) 
     1354    { 
     1355        require_once dirname(__FILE__) . '/rcube_result_index.php'; 
     1356 
     1357        $msg_index = $this->fetchHeaderIndex($mailbox, $message_set, 
     1358            $index_field, $skip_deleted, $uidfetch, $return_uid); 
     1359 
     1360        if (!empty($msg_index)) { 
     1361            asort($msg_index); // ASC 
     1362            $msg_index = array_keys($msg_index); 
     1363            $msg_index = '* SEARCH ' . implode(' ', $msg_index); 
     1364        } 
     1365        else { 
     1366            $msg_index = is_array($msg_index) ? '* SEARCH' : null; 
     1367        } 
     1368 
     1369        return new rcube_result_index($mailbox, $msg_index); 
     1370    } 
     1371 
     1372    function fetchHeaderIndex($mailbox, $message_set, $index_field='', $skip_deleted=true, 
     1373        $uidfetch=false, $return_uid=false) 
    13341374    { 
    13351375        if (is_array($message_set)) { 
     
    13711411 
    13721412        // build FETCH command string 
    1373         $key     = $this->nextTag(); 
    1374         $cmd     = $uidfetch ? 'UID FETCH' : 'FETCH'; 
    1375         $deleted = $skip_deleted ? ' FLAGS' : ''; 
    1376  
    1377         if ($mode == 1 && $index_field == 'DATE') 
    1378             $request = " $cmd $message_set (INTERNALDATE BODY.PEEK[HEADER.FIELDS (DATE)]$deleted)"; 
    1379         else if ($mode == 1) 
    1380             $request = " $cmd $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)]$deleted)"; 
     1413        $key    = $this->nextTag(); 
     1414        $cmd    = $uidfetch ? 'UID FETCH' : 'FETCH'; 
     1415        $fields = array(); 
     1416 
     1417        if ($return_uid) 
     1418            $fields[] = 'UID'; 
     1419        if ($skip_deleted) 
     1420            $fields[] = 'FLAGS'; 
     1421 
     1422        if ($mode == 1) { 
     1423            if ($index_field == 'DATE') 
     1424                $fields[] = 'INTERNALDATE'; 
     1425            $fields[] = "BODY.PEEK[HEADER.FIELDS ($index_field)]"; 
     1426        } 
    13811427        else if ($mode == 2) { 
    13821428            if ($index_field == 'SIZE') 
    1383                 $request = " $cmd $message_set (RFC822.SIZE$deleted)"; 
    1384             else 
    1385                 $request = " $cmd $message_set ($index_field$deleted)"; 
    1386         } else if ($mode == 3) 
    1387             $request = " $cmd $message_set (FLAGS)"; 
    1388         else // 4 
    1389             $request = " $cmd $message_set (INTERNALDATE$deleted)"; 
    1390  
    1391         $request = $key . $request; 
     1429                $fields[] = 'RFC822.SIZE'; 
     1430            else if (!$return_uid || $index_field != 'UID') 
     1431                $fields[] = $index_field; 
     1432        } 
     1433        else if ($mode == 3 && !$skip_deleted) 
     1434            $fields[] = 'FLAGS'; 
     1435        else if ($mode == 4) 
     1436            $fields[] = 'INTERNALDATE'; 
     1437 
     1438        $request = "$key $cmd $message_set (" . implode(' ', $fields) . ")"; 
    13921439 
    13931440        if (!$this->putLine($request)) { 
     
    14061453                $flags  = NULL; 
    14071454 
     1455                if ($return_uid) { 
     1456                    if (preg_match('/UID ([0-9]+)/', $line, $matches)) 
     1457                        $id = (int) $matches[1]; 
     1458                    else 
     1459                        continue; 
     1460                } 
    14081461                if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) { 
    14091462                    $flags = explode(' ', strtoupper($matches[1])); 
     
    15351588    { 
    15361589        if ($uid > 0) { 
    1537             $id_a = $this->search($mailbox, "UID $uid"); 
    1538             if (is_array($id_a) && count($id_a) == 1) { 
    1539                 return (int) $id_a[0]; 
     1590            $index = $this->search($mailbox, "UID $uid"); 
     1591 
     1592            if ($index->count() == 1) { 
     1593                $arr = $index->get(); 
     1594                return (int) $arr[0]; 
    15401595            } 
    15411596        } 
     
    15611616        } 
    15621617 
    1563         list($code, $response) = $this->execute('FETCH', array($id, '(UID)')); 
    1564  
    1565         if ($code == self::ERROR_OK && preg_match("/^\* $id FETCH \(UID (.*)\)/i", $response, $m)) { 
    1566             return (int) $m[1]; 
     1618        $index = $this->search($mailbox, $id, true); 
     1619 
     1620        if ($index->count() == 1) { 
     1621            $arr = $index->get(); 
     1622            return (int) $arr[0]; 
    15671623        } 
    15681624 
    15691625        return null; 
    1570     } 
    1571  
    1572     function fetchUIDs($mailbox, $message_set=null) 
    1573     { 
    1574         if (empty($message_set)) 
    1575             $message_set = '1:*'; 
    1576  
    1577         return $this->fetchHeaderIndex($mailbox, $message_set, 'UID', false); 
    15781626    } 
    15791627 
     
    19712019    } 
    19722020 
    1973     // Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about 
    1974     // 7 times instead :-) See comments on http://uk2.php.net/references and this article: 
    1975     // http://derickrethans.nl/files/phparch-php-variables-article.pdf 
    1976     private function parseThread($str, $begin, $end, $root, $parent, $depth, &$depthmap, &$haschildren) 
    1977     { 
    1978         $node = array(); 
    1979         if ($str[$begin] != '(') { 
    1980             $stop = $begin + strspn($str, '1234567890', $begin, $end - $begin); 
    1981             $msg = substr($str, $begin, $stop - $begin); 
    1982             if ($msg == 0) 
    1983                 return $node; 
    1984             if (is_null($root)) 
    1985                 $root = $msg; 
    1986             $depthmap[$msg] = $depth; 
    1987             $haschildren[$msg] = false; 
    1988             if (!is_null($parent)) 
    1989                 $haschildren[$parent] = true; 
    1990             if ($stop + 1 < $end) 
    1991                 $node[$msg] = $this->parseThread($str, $stop + 1, $end, $root, $msg, $depth + 1, $depthmap, $haschildren); 
    1992             else 
    1993                 $node[$msg] = array(); 
    1994         } else { 
    1995             $off = $begin; 
    1996             while ($off < $end) { 
    1997                 $start = $off; 
    1998                 $off++; 
    1999                 $n = 1; 
    2000                 while ($n > 0) { 
    2001                     $p = strpos($str, ')', $off); 
    2002                     if ($p === false) { 
    2003                         error_log("Mismatched brackets parsing IMAP THREAD response:"); 
    2004                         error_log(substr($str, ($begin < 10) ? 0 : ($begin - 10), $end - $begin + 20)); 
    2005                         error_log(str_repeat(' ', $off - (($begin < 10) ? 0 : ($begin - 10)))); 
    2006                         return $node; 
    2007                     } 
    2008                     $p1 = strpos($str, '(', $off); 
    2009                     if ($p1 !== false && $p1 < $p) { 
    2010                         $off = $p1 + 1; 
    2011                         $n++; 
    2012                     } else { 
    2013                         $off = $p + 1; 
    2014                         $n--; 
    2015                     } 
    2016                 } 
    2017                 $node += $this->parseThread($str, $start + 1, $off - 1, $root, $parent, $depth, $depthmap, $haschildren); 
    2018             } 
    2019         } 
    2020  
    2021         return $node; 
    2022     } 
    2023  
    2024     function thread($mailbox, $algorithm='REFERENCES', $criteria='', $encoding='US-ASCII') 
    2025     { 
     2021    /** 
     2022     * Executes THREAD command 
     2023     * 
     2024     * @param string $mailbox    Mailbox name 
     2025     * @param string $algorithm  Threading algorithm (ORDEREDSUBJECT, REFERENCES, REFS) 
     2026     * @param string $criteria   Searching criteria 
     2027     * @param bool   $return_uid Enables UIDs in result instead of sequence numbers 
     2028     * @param string $encoding   Character set 
     2029     * 
     2030     * @return rcube_result_thread Thread data 
     2031     */ 
     2032    function thread($mailbox, $algorithm='REFERENCES', $criteria='', $return_uid=false, $encoding='US-ASCII') 
     2033    { 
     2034        require_once dirname(__FILE__) . '/rcube_result_thread.php'; 
     2035 
    20262036        $old_sel = $this->selected; 
    20272037 
    20282038        if (!$this->select($mailbox)) { 
    2029             return false; 
     2039            return new rcube_result_thread($mailbox); 
    20302040        } 
    20312041 
    20322042        // return empty result when folder is empty and we're just after SELECT 
    20332043        if ($old_sel != $mailbox && !$this->data['EXISTS']) { 
    2034             return array(array(), array(), array()); 
     2044            return new rcube_result_thread($mailbox); 
    20352045        } 
    20362046 
     
    20402050        $data      = ''; 
    20412051 
    2042         list($code, $response) = $this->execute('THREAD', array( 
    2043             $algorithm, $encoding, $criteria)); 
    2044  
    2045         if ($code == self::ERROR_OK) { 
    2046             // remove prefix... 
    2047             $response = substr($response, stripos($response, '* THREAD') + 9); 
    2048             // ...unilateral untagged server responses 
    2049             if ($pos = strpos($response, '*')) { 
    2050                 $response = substr($response, 0, $pos); 
    2051             } 
    2052  
    2053             $response    = str_replace("\r\n", '', $response); 
    2054             $depthmap    = array(); 
    2055             $haschildren = array(); 
    2056  
    2057             $tree = $this->parseThread($response, 0, strlen($response), 
    2058                 null, null, 0, $depthmap, $haschildren); 
    2059  
    2060             return array($tree, $depthmap, $haschildren); 
    2061         } 
    2062  
    2063         return false; 
     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); 
    20642060    } 
    20652061 
     
    20722068     * @param array  $items      Return items (MIN, MAX, COUNT, ALL) 
    20732069     * 
    2074      * @return array Message identifiers or item-value hash  
     2070     * @return rcube_result_index Result data 
    20752071     */ 
    20762072    function search($mailbox, $criteria, $return_uid=false, $items=array()) 
    20772073    { 
     2074        require_once dirname(__FILE__) . '/rcube_result_index.php'; 
     2075 
    20782076        $old_sel = $this->selected; 
    20792077 
    20802078        if (!$this->select($mailbox)) { 
    2081             return false; 
     2079            return new rcube_result_index($mailbox); 
    20822080        } 
    20832081 
    20842082        // return empty result when folder is empty and we're just after SELECT 
    20852083        if ($old_sel != $mailbox && !$this->data['EXISTS']) { 
    2086             if (!empty($items)) 
    2087                 return array_combine($items, array_fill(0, count($items), 0)); 
    2088             else 
    2089                 return array(); 
     2084            return new rcube_result_index($mailbox, '* SEARCH'); 
     2085        } 
     2086 
     2087        // If ESEARCH is supported always use ALL 
     2088        // but not when items are specified or using simple id2uid search 
     2089        if (empty($items) && ((int) $criteria != $criteria)) { 
     2090            $items = array('ALL'); 
    20902091        } 
    20912092 
     
    20982099            $params .= 'RETURN (' . implode(' ', $items) . ')'; 
    20992100        } 
     2101 
    21002102        if (!empty($criteria)) { 
    21012103            $modseq = stripos($criteria, 'MODSEQ') !== false; 
     
    21092111            array($params)); 
    21102112 
    2111         if ($code == self::ERROR_OK) { 
    2112             // remove prefix... 
    2113             $response = substr($response, stripos($response, 
    2114                 $esearch ? '* ESEARCH' : '* SEARCH') + ($esearch ? 10 : 9)); 
    2115             // ...and unilateral untagged server responses 
    2116             if ($pos = strpos($response, '*')) { 
    2117                 $response = rtrim(substr($response, 0, $pos)); 
    2118             } 
    2119  
    2120             // remove MODSEQ response 
    2121             if ($modseq) { 
    2122                 if (preg_match('/\(MODSEQ ([0-9]+)\)$/', $response, $m)) { 
    2123                     $response = substr($response, 0, -strlen($m[0])); 
    2124                 } 
    2125             } 
    2126  
    2127             if ($esearch) { 
    2128                 // Skip prefix: ... (TAG "A285") UID ... 
    2129                 $this->tokenizeResponse($response, $return_uid ? 2 : 1); 
    2130  
    2131                 $result = array(); 
    2132                 for ($i=0; $i<count($items); $i++) { 
    2133                     // If the SEARCH returns no matches, the server MUST NOT 
    2134                     // include the item result option in the ESEARCH response 
    2135                     if ($ret = $this->tokenizeResponse($response, 2)) { 
    2136                         list ($name, $value) = $ret; 
    2137                         $result[$name] = $value; 
    2138                     } 
    2139                 } 
    2140  
    2141                 return $result; 
    2142             } 
    2143             else { 
    2144                 $response = preg_split('/[\s\r\n]+/', $response, -1, PREG_SPLIT_NO_EMPTY); 
    2145  
    2146                 if (!empty($items)) { 
    2147                     $result = array(); 
    2148                     if (in_array('COUNT', $items)) { 
    2149                         $result['COUNT'] = count($response); 
    2150                     } 
    2151                     if (in_array('MIN', $items)) { 
    2152                         $result['MIN'] = !empty($response) ? min($response) : 0; 
    2153                     } 
    2154                     if (in_array('MAX', $items)) { 
    2155                         $result['MAX'] = !empty($response) ? max($response) : 0; 
    2156                     } 
    2157                     if (in_array('ALL', $items)) { 
    2158                         $result['ALL'] = $this->compressMessageSet($response, true); 
    2159                     } 
    2160  
    2161                     return $result; 
    2162                 } 
    2163                 else { 
    2164                     return $response; 
    2165                 } 
    2166             } 
    2167         } 
    2168  
    2169         return false; 
     2113        if ($code != self::ERROR_OK) { 
     2114            $response = null; 
     2115        } 
     2116 
     2117        return new rcube_result_index($mailbox, $response); 
    21702118    } 
    21712119 
Note: See TracChangeset for help on using the changeset viewer.