Changeset 5575 in subversion


Ignore:
Timestamp:
Dec 8, 2011 4:51:39 AM (18 months ago)
Author:
alec
Message:
  • More documentation + cleanup
Location:
trunk/roundcubemail/program/include
Files:
2 edited

Legend:

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

    r5557 r5575  
    23732373            return false; 
    23742374 
    2375         $deleted = $this->conn->delete($mailbox, $uids); 
     2375        $deleted = $this->conn->flag($mailbox, $uids, 'DELETED'); 
    23762376 
    23772377        if ($deleted) { 
  • trunk/roundcubemail/program/include/rcube_imap_generic.php

    r5557 r5575  
    154154     * @param bool   $endln  True if CRLF need to be added at the end of command 
    155155     * 
    156      * @param int Number of bytes sent, False on error 
     156     * @return int|bool Number of bytes sent, False on error 
    157157     */ 
    158158    function putLineC($string, $endln=true) 
    159159    { 
    160         if (!$this->fp) 
    161             return false; 
    162  
    163         if ($endln) 
     160        if (!$this->fp) { 
     161            return false; 
     162        } 
     163 
     164        if ($endln) { 
    164165            $string .= "\r\n"; 
    165  
     166        } 
    166167 
    167168        $res = 0; 
     
    199200    } 
    200201 
     202    /** 
     203     * Reads line from the connection stream 
     204     * 
     205     * @param int  $size  Buffer size 
     206     * 
     207     * @return string Line of text response 
     208     */ 
    201209    function readLine($size=1024) 
    202210    { 
     
    227235    } 
    228236 
     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     */ 
    229246    function multLine($line, $escape = false) 
    230247    { 
     
    248265    } 
    249266 
     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     */ 
    250274    function readBytes($bytes) 
    251275    { 
     
    269293    } 
    270294 
     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     */ 
    271302    function readReply(&$untagged=null) 
    272303    { 
     
    284315    } 
    285316 
     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     */ 
    286325    function parseResult($string, $err_prefix='') 
    287326    { 
     
    327366    } 
    328367 
     368    /** 
     369     * Checks connection stream state. 
     370     * 
     371     * @return bool True if connection is closed 
     372     */ 
    329373    private function eof() 
    330374    { 
     
    337381        $start = microtime(true); 
    338382 
    339         if (feof($this->fp) ||  
     383        if (feof($this->fp) || 
    340384            ($this->prefs['timeout'] && (microtime(true) - $start > $this->prefs['timeout'])) 
    341385        ) { 
     
    347391    } 
    348392 
     393    /** 
     394     * Closes connection stream. 
     395     */ 
    349396    private function closeSocket() 
    350397    { 
     
    353400    } 
    354401 
     402    /** 
     403     * Error code/message setter. 
     404     */ 
    355405    function setError($code, $msg='') 
    356406    { 
     
    359409    } 
    360410 
    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     */ 
    362422    function startsWith($string, $match, $error=false, $nonempty=false) 
    363423    { 
    364         $len = strlen($match); 
    365         if ($len == 0) { 
    366             return false; 
    367         } 
    368424        if (!$this->fp) { 
    369425            return true; 
    370426        } 
    371         if (strncmp($string, $match, $len) == 0) { 
     427        if (strncmp($string, $match, strlen($match)) == 0) { 
    372428            return true; 
    373429        } 
     
    609665 
    610666    /** 
    611      * Gets the delimiter 
     667     * Detects hierarchy delimiter 
    612668     * 
    613669     * @return string The delimiter 
     
    669725    } 
    670726 
     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     */ 
    671737    function connect($host, $user, $password, $options=null) 
    672738    { 
     
    856922    } 
    857923 
     924    /** 
     925     * Checks connection status 
     926     * 
     927     * @return bool True if connection is active and user is logged in, False otherwise. 
     928     */ 
    858929    function connected() 
    859930    { 
     
    861932    } 
    862933 
     934    /** 
     935     * Closes connection with logout. 
     936     */ 
    863937    function closeConnection() 
    864938    { 
     
    10731147 
    10741148    /** 
    1075      * Executes SUBSCRIBE command 
     1149     * Folder subscription (SUBSCRIBE) 
    10761150     * 
    10771151     * @param string $mailbox Mailbox name 
     
    10881162 
    10891163    /** 
    1090      * Executes UNSUBSCRIBE command 
     1164     * Folder unsubscription (UNSUBSCRIBE) 
    10911165     * 
    10921166     * @param string $mailbox Mailbox name 
     
    10971171    { 
    10981172        $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)), 
    10991203            self::COMMAND_NORESPONSE); 
    11001204 
     
    11421246 
    11431247    /** 
     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    /** 
    11441377     * Returns count of all messages in a folder 
    11451378     * 
     
    13301563        list($code, $response) = $this->execute($return_uid ? 'UID SORT' : 'SORT', 
    13311564            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)); 
    13321664 
    13331665        if ($code != self::ERROR_OK) { 
     
    15111843    } 
    15121844 
    1513     static function compressMessageSet($messages, $force=false) 
    1514     { 
    1515         // given a comma delimited list of independent mid's, 
    1516         // compresses by grouping sequences together 
    1517  
    1518         if (!is_array($messages)) { 
    1519             // if less than 255 bytes long, let's not bother 
    1520             if (!$force && strlen($messages)<255) { 
    1521                 return $messages; 
    1522            } 
    1523  
    1524             // see if it's already been compressed 
    1525             if (strpos($messages, ':') !== false) { 
    1526                 return $messages; 
    1527             } 
    1528  
    1529             // separate, then sort 
    1530             $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 gap 
    1541                 if ($start == $prev) { 
    1542                     $result[] = $prev; // push single id 
    1543                 } else { 
    1544                     $result[] = $start . ':' . $prev; // push sequence as start_id:end_id 
    1545                 } 
    1546                 $start = $id; // start of new sequence 
    1547             } 
    1548             $prev = $id; 
    1549         } 
    1550  
    1551         // handle the last sequence/id 
    1552         if ($start == $prev) { 
    1553             $result[] = $prev; 
    1554         } else { 
    1555             $result[] = $start.':'.$prev; 
    1556         } 
    1557  
    1558         // return as comma separated string 
    1559         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  
    15791845    /** 
    15801846     * Returns message sequence identifier 
     
    16241890 
    16251891        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; 
    16262013    } 
    16272014 
     
    19402327    } 
    19412328 
    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 cache 
    1959         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 cache 
    1990         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 cache 
    2014             unset($this->data['STATUS:'.$from]); 
    2015  
    2016             return $this->delete($from, $messages); 
    2017         } 
    2018         return $r; 
    2019     } 
    2020  
    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  
    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 SELECT 
    2043         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 command 
    2064      * 
    2065      * @param string $mailbox    Mailbox name 
    2066      * @param string $criteria   Searching criteria 
    2067      * @param bool   $return_uid Enable UID in result instead of sequence ID 
    2068      * @param array  $items      Return items (MIN, MAX, COUNT, ALL) 
    2069      * 
    2070      * @return rcube_result_index Result data 
    2071      */ 
    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 SELECT 
    2083         if ($old_sel != $mailbox && !$this->data['EXISTS']) { 
    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'); 
    2091         } 
    2092  
    2093         $esearch  = empty($items) ? false : $this->getCapability('ESEARCH'); 
    2094         $criteria = trim($criteria); 
    2095         $params   = ''; 
    2096  
    2097         // RFC4731: ESEARCH 
    2098         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 mailboxes 
    2122      * 
    2123      * @param string $ref         Reference name 
    2124      * @param string $mailbox     Mailbox name 
    2125      * @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 argument 
    2129      *               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 mailboxes 
    2138      * 
    2139      * @param string $ref         Reference name 
    2140      * @param string $mailbox     Mailbox name 
    2141      * @param array  $status_opts (see self::_listMailboxes) 
    2142      * 
    2143      * @return array List of mailboxes or hash of options if $status_opts argument 
    2144      *               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 command 
    2153      * 
    2154      * @param string $ref         Reference name 
    2155      * @param string $mailbox     Mailbox name 
    2156      * @param bool   $subscribed  Enables returning subscribed mailboxes only 
    2157      * @param array  $status_opts List of STATUS options (RFC5819: LIST-STATUS) 
    2158      *                            Possible: MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, UNSEEN 
    2159      * @param array  $select_opts List of selection options (RFC5258: LIST-EXTENDED) 
    2160      *                            Possible: SUBSCRIBED, RECURSIVEMATCH, REMOTE 
    2161      * 
    2162      * @return array List of mailboxes or hash of options if $status_ops argument 
    2163      *               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-line 
    2200                 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 array 
    2218                     if (!$lstatus) { 
    2219                         $folders[] = $mailbox; 
    2220                     } 
    2221                     else { 
    2222                         $folders[$mailbox] = array(); 
    2223                     } 
    2224  
    2225                     // Add to options array 
    2226                     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  
    22492329    function fetchMIMEHeaders($mailbox, $uid, $parts, $mime=true) 
    22502330    { 
     
    24552535    } 
    24562536 
    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  
    24732537    /** 
    24742538     * Handler for IMAP APPEND command 
     
    26282692    } 
    26292693 
     2694    /** 
     2695     * Returns QUOTA information 
     2696     * 
     2697     * @return array Quota information 
     2698     */ 
    26302699    function getQuota() 
    26312700    { 
     
    33823451    } 
    33833452 
     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 
    33843535    private function _xor($string, $string2) 
    33853536    { 
     
    34203571    } 
    34213572 
     3573    /** 
     3574     * CAPABILITY response parser 
     3575     */ 
    34223576    private function parseCapability($str, $trusted=false) 
    34233577    { 
     
    34653619    } 
    34663620 
     3621    /** 
     3622     * Unescapes quoted-string 
     3623     * 
     3624     * @param string  $string       IMAP string 
     3625     * 
     3626     * @return string String 
     3627     */ 
    34673628    static function unEscape($string) 
    34683629    { 
Note: See TracChangeset for help on using the changeset viewer.