From 31a346f31689c76419fa0ca1244de72425a37c08 Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Tue, 26 Jan 2010 12:45:00 -0700 Subject: [PATCH] Add support for LIST-STATUS (draft-ietf-morg-status-in-list-01) --- .../Imap_Client/lib/Horde/Imap/Client/Base.php | 52 +++++++++++++++++- .../Imap_Client/lib/Horde/Imap/Client/Socket.php | 63 ++++++++++++++++++---- framework/Imap_Client/package.xml | 2 +- imp/docs/CHANGES | 1 + imp/docs/RFCS | 6 +-- imp/lib/Ajax/Application.php | 6 +-- 6 files changed, 110 insertions(+), 20 deletions(-) diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Base.php b/framework/Imap_Client/lib/Horde/Imap/Client/Base.php index 6a3261d74..97b35986a 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Base.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Base.php @@ -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 diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php b/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php index fc6af55c8..65d826c9b 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php @@ -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': diff --git a/framework/Imap_Client/package.xml b/framework/Imap_Client/package.xml index 633f08ee5..c8a4e7a75 100644 --- a/framework/Imap_Client/package.xml +++ b/framework/Imap_Client/package.xml @@ -31,7 +31,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> alpha LGPL - * Add run-time configurable caching for headers fetch requests. + * 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(). diff --git a/imp/docs/CHANGES b/imp/docs/CHANGES index b6e36c779..aa5e38450 100644 --- a/imp/docs/CHANGES +++ b/imp/docs/CHANGES @@ -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 diff --git a/imp/docs/RFCS b/imp/docs/RFCS index b6f29be6f..1ffd88e36 100644 --- a/imp/docs/RFCS +++ b/imp/docs/RFCS @@ -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 diff --git a/imp/lib/Ajax/Application.php b/imp/lib/Ajax/Application.php index ab2e9ea17..187f05cf5 100644 --- a/imp/lib/Ajax/Application.php +++ b/imp/lib/Ajax/Application.php @@ -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 && -- 2.11.0