Add support for LIST-STATUS (draft-ietf-morg-status-in-list-01)
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 26 Jan 2010 19:45:00 +0000 (12:45 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 26 Jan 2010 20:42:49 +0000 (13:42 -0700)
framework/Imap_Client/lib/Horde/Imap/Client/Base.php
framework/Imap_Client/lib/Horde/Imap/Client/Socket.php
framework/Imap_Client/package.xml
imp/docs/CHANGES
imp/docs/RFCS
imp/lib/Ajax/Application.php

index 6a3261d..97b3598 100644 (file)
@@ -849,6 +849,16 @@ abstract class Horde_Imap_Client_Base
      * 'remote' - (boolean) Tell server to return mailboxes that reside on
      *            another server. Requires the LIST-EXTENDED extension.
      *            DEFAULT: false
+     * 'status' - (integer) Tell server to return status information. The
+     *            value is a bitmask that may contain the following:
+     *            Horde_Imap_Client::STATUS_MESSAGES,
+     *            Horde_Imap_Client::STATUS_RECENT,
+     *            Horde_Imap_Client::STATUS_UIDNEXT,
+     *            Horde_Imap_Client::STATUS_UIDVALIDITY,
+     *            Horde_Imap_Client::STATUS_UNSEEN, and
+     *            Horde_Imap_Client::STATUS_HIGHESTMODSEQ.
+     *            Requires the LIST-STATUS extension.
+     *            DEFAULT: 0
      * 'sort' - (boolean) If true, return a sorted list of mailboxes?
      *          DEFAULT: Do not sort the list.
      * 'sort_delimiter' - (string) If 'sort' is true, this is the delimiter
@@ -862,8 +872,10 @@ abstract class Horde_Imap_Client_Base
      *                of mailboxes.  Otherwise, the array values are arrays
      *                with the following keys: 'mailbox', 'attributes' (only
      *                if 'attributes' option is true), 'delimiter' (only
-     *                if 'delimiter' option is true), and 'extended' (only
+     *                if 'delimiter' option is true), 'extended' (only
      *                if 'recursivematch' option is true and LIST-EXTENDED
+     *                extension is supported on the server), and 'status'
+     *                (only if 'status' option is true and LIST-STATUS
      *                extension is supported on the server).
      * @throws Horde_Imap_Client_Exception
      */
@@ -1049,6 +1061,44 @@ abstract class Horde_Imap_Client_Base
     abstract protected function _status($mailbox, $flags);
 
     /**
+     * Perform a STATUS call on multiple mailboxes at the same time.
+     *
+     * This method leverages the LIST-EXTENDED and LIST-STATUS extensions on
+     * the IMAP server to improve the efficiency of this operation.
+     *
+     * @param array $mailboxes  The mailboxes to query. Either in UTF7-IMAP
+     *                          or UTF-8.
+     * @param integer $flags    See self::status().
+     *
+     * @return array  An array with the keys as the mailbox names and the
+     *                values as arrays with the requested keys (from the
+     *                mask given in $flags).
+     */
+    public function statusMultiple($mailboxes,
+                                   $flags = Horde_Imap_Client::STATUS_ALL)
+    {
+        $ret = array();
+
+        if ($this->queryCapability('LIST-STATUS')) {
+            try {
+                foreach ($this->listMailboxes($mailboxes, Horde_Imap_Client::MBOX_ALL, array('status' => $flags)) as $val) {
+                    if (isset($val['status'])) {
+                        $ret[$val['mailbox']] = $val['status'];
+                    }
+                }
+            } catch (Horde_Imap_Client_Exception $e) {}
+        } else {
+            foreach ($mailboxes as $val) {
+                try {
+                    $ret[$val] = $this->status($val, $flags);
+                } catch (Horde_Imap_Client_Exception $e) {}
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
      * Append message(s) to a mailbox.
      *
      * @param string $mailbox  The mailbox to append the message(s) to. Either
index fc6af55..65d826c 100644 (file)
@@ -36,8 +36,9 @@
  *   RFC 5464 - METADATA
  *   RFC 5530 - IMAP Response Codes
  *
- *   draft-ietf-morg-sortdisplay-02 - SORT=DISPLAY
- *   draft-ietf-morg-inthread-00 - THREAD=REFS
+ *   draft-ietf-morg-status-in-list-01  LIST-STATUS
+ *   draft-ietf-morg-sortdisplay-02     SORT=DISPLAY
+ *   draft-ietf-morg-inthread-00        THREAD=REFS
  *
  *   [NO RFC] - XIMAPPROXY
  *       + Requires imapproxy v1.2.7-rc1 or later
@@ -1035,6 +1036,29 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base
                 $return_opts[] = 'CHILDREN';
             }
 
+            if (!empty($options['status']) &&
+                $this->queryCapability('LIST-STATUS')) {
+                $status_mask = array(
+                    Horde_Imap_Client::STATUS_MESSAGES => 'MESSAGES',
+                    Horde_Imap_Client::STATUS_RECENT => 'RECENT',
+                    Horde_Imap_Client::STATUS_UIDNEXT => 'UIDNEXT',
+                    Horde_Imap_Client::STATUS_UIDVALIDITY => 'UIDVALIDITY',
+                    Horde_Imap_Client::STATUS_UNSEEN => 'UNSEEN',
+                    Horde_Imap_Client::STATUS_HIGHESTMODSEQ => 'HIGHESTMODSEQ'
+                );
+
+                $status_opts = array();
+                foreach ($status_mask as $key => $val) {
+                    if ($options['status'] & $key) {
+                        $status_opts[] = $val;
+                    }
+                }
+
+                if (!empty($status_opts)) {
+                    $return_opts[] = 'STATUS (' . implode(' ', $status_opts) . ')';
+                }
+            }
+
             if (!empty($return_opts)) {
                 $cmd .= ' RETURN (' . implode(' ', $return_opts) . ')';
             }
@@ -1052,9 +1076,24 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base
 
         $this->_sendLine($cmd);
 
-        return empty($options['flat'])
-            ? $t['listresponse']
-            : array_values($t['listresponse']);
+        if (!empty($options['flat'])) {
+            return array_values($t['listresponse']);
+        }
+
+        /* Add in STATUS return, if needed. */
+        if (!empty($options['status'])) {
+            if (!is_array($pattern)) {
+                $pattern = array($pattern);
+            }
+
+            foreach ($pattern as $val) {
+                if (!empty($t['status'][$val])) {
+                    $t['listresponse'][$val]['status'] = $t['status'][$val];
+                }
+            }
+        }
+
+        return $t['listresponse'];
     }
 
     /**
@@ -1201,22 +1240,24 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base
             return $data;
         }
 
-        $this->_temp['status'] = array();
         $this->_sendLine('STATUS ' . $this->utils->escape($mailbox) . ' (' . implode(' ', array_map('strtoupper', $query)) . ')');
 
-        return $this->_temp['status'];
+        return $this->_temp['status'][$mailbox];
     }
 
     /**
      * Parse a STATUS response (RFC 3501 [7.2.4], RFC 4551 [3.6])
      *
-     * @param array $data  The server response.
+     * @param string $mailbox  The mailbox name (UTF7-IMAP).
+     * @param array $data      The server response.
      */
-    protected function _parseStatus($data)
+    protected function _parseStatus($mailbox, $data)
     {
+        $this->_temp['status'][$mailbox] = array();
+
         for ($i = 0, $len = count($data); $i < $len; $i += 2) {
             $item = strtolower($data[$i]);
-            $this->_temp['status'][$item] = $data[$i + 1];
+            $this->_temp['status'][$mailbox][$item] = $data[$i + 1];
         }
     }
 
@@ -3744,7 +3785,7 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base
 
             case 'STATUS':
                 // Parse a STATUS response (RFC 3501 [7.2.4]).
-                $this->_parseStatus($ob['token'][2]);
+                $this->_parseStatus($ob['token'][1], $ob['token'][2]);
                 break;
 
             case 'SEARCH':
index 633f08e..c8a4e7a 100644 (file)
@@ -31,7 +31,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
   <api>alpha</api>
  </stability>
  <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>* Add run-time configurable caching for headers fetch requests.
+ <notes>* Add support for LIST-STATUS (draft-ietf-morg-status-in-list-01).
  * Add support for THREAD=REFS (draft-ietf-morg-inthread-00).
  * Add support for RFC 5258 (LIST-EXTENDED).
  * Add Horde_Imap_Client_Utils::createUrl().
index b6e36c7..aa5e384 100644 (file)
@@ -2,6 +2,7 @@
 v5.0-git
 --------
 
+[mms] Add support for LIST-STATUS IMAP extension.
 [mms] Add hook to allow determination of compose attachments MIME type.
 [mms] Move AJAX processing framework to Horde (Request #4561).
 [mms] If selected message(s) disappear from mailbox, gracefully handle in the
index b6f29be..1ffd88e 100644 (file)
@@ -48,9 +48,9 @@ RFC 5464        METADATA
 RFC 5530        IMAP Response Codes
 RFC 5550        Lemonade Profile (specifically [2.8] - $Forwarded flag)
 
-draft-ietf-morg-sortdisplay-02    SORT=DISPLAY
-draft-ietf-morg-inthread-00       THREAD=REFS
-
+draft-ietf-morg-status-in-list-01  LIST-STATUS
+draft-ietf-morg-sortdisplay-02     SORT=DISPLAY
+draft-ietf-morg-inthread-00        THREAD=REFS
 
 
 POP3
index ab2e9ea..187f05c 100644 (file)
@@ -350,10 +350,8 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base
         $result = new stdClass;
         $result->poll = array();
 
-        foreach ($imptree->getPollList() as $val) {
-            if ($info = $imptree->getElementInfo($val)) {
-                $result->poll[$val] = intval($info['unseen']);
-            }
+        foreach ($GLOBALS['imp_imap']->ob()->statusMultiple($imptree->getPollList(), Horde_Imap_Client::STATUS_UNSEEN) as $key => $val) {
+            $result->poll[$key] = intval($val['unseen']);
         }
 
         if ($vars->view &&