From: Michael M Slusarz Date: Wed, 25 Mar 2009 18:45:16 +0000 (-0600) Subject: More thorough job of identifying mailboxes we shouldn't be caching. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=17377665f8b316d3979d0d1c42635eb498807af3;p=horde.git More thorough job of identifying mailboxes we shouldn't be caching. Based primarily on NOMODSEQ (RFC 4551) and UIDNOTSTICKY (RFC 4315) responses. --- diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Base.php b/framework/Imap_Client/lib/Horde/Imap/Client/Base.php index a843865ba..06ab2fc55 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Base.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Base.php @@ -196,10 +196,15 @@ abstract class Horde_Imap_Client_Base /** * Initialize the Horde_Imap_Client_Cache object, if necessary. * + * @param boolean $current If true, we are going to update the currently + * selected mailbox. Add an additional check to + * see if caching is available in current + * mailbox. + * * @return boolean Returns true if caching is enabled. * @throws Horde_Imap_Client_Exception */ - protected function _initCache() + protected function _initCache($current = false) { if (empty($this->_params['cache']['fields'])) { return false; @@ -214,7 +219,14 @@ abstract class Horde_Imap_Client_Base ))); } - return true; + if (!$current) { + return true; + } + + /* If UIDs are labeled as not sticky, don't cache since UIDs will + * change on every access. */ + $status = $this->status($this->_selected, Horde_Imap_Client::STATUS_UIDNOTSTICKY); + return !$status['uidnotsticky']; } /** @@ -260,7 +272,9 @@ abstract class Horde_Imap_Client_Base } } $capability = strtoupper($capability); - return isset($this->_init['capability'][$capability]) ? $this->_init['capability'][$capability] : false; + return isset($this->_init['capability'][$capability]) + ? $this->_init['capability'][$capability] + : false; } /** @@ -909,9 +923,9 @@ abstract class Horde_Imap_Client_Base * Return key: 'highestmodseq' * Return format: (integer) If the server supports the CONDSTORE IMAP * extension, this will be the highest mod-sequence value - * of all messages in the mailbox or 0 if the mailbox - * does not support mod-sequences. Else, this value will - * be undefined. + * of all messages in the mailbox. Else 0 if CONDSTORE + * not available or the mailbox does not support + * mod-sequences. * * Flag: Horde_Imap_Client::STATUS_UIDNOTSTICKY * Return key: 'uidnotsticky' @@ -962,6 +976,19 @@ abstract class Horde_Imap_Client_Base } } + /* Catch flags that are not supported. */ + if (($flags & Horde_Imap_Client::STATUS_HIGHESTMODSEQ) && + isset($this->_init['enabled']['CONDSTORE'])) { + $ret['highestmodseq'] = 0; + $flags &= ~$val; + } + + if (($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY) && + !$this->queryCapability('UIDPLUS')) { + $ret['uidnotsticky'] = false; + $flags &= ~$val; + } + if (!$flags) { return $ret; } @@ -1123,7 +1150,8 @@ abstract class Horde_Imap_Client_Base } /* If we are caching, search for deleted messages. */ - if (!empty($options['expunge']) && $this->_initCache()) { + if (!empty($options['expunge']) && + $this->_initCache(true)) { $search_query = new Horde_Imap_Client_Search_Query(); $search_query->flag('\\deleted', true); $search_res = $this->search($this->_selected, $search_query); @@ -1301,6 +1329,8 @@ abstract class Horde_Imap_Client_Base $type = empty($options['sort']) ? 'match' : 'sort'; + $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO); + /* Take advantage of search result caching. If CONDSTORE available, * we can cache all queries and invalidate the cache when the MODSEQ * changes. If CONDSTORE not available, we can only store queries @@ -1308,7 +1338,7 @@ abstract class Horde_Imap_Client_Base * array - the generated query is already added to '_query' key * above. */ $cache = null; - if ($this->_initCache() && + if ($this->_initCache(true) && (isset($this->_init['enabled']['CONDSTORE']) || !$query->flagSearch())) { $cache = $this->_getSearchCache('search', $mailbox, $options); @@ -1320,8 +1350,6 @@ abstract class Horde_Imap_Client_Base } } - $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO); - $ret = $this->_search($query, $options); if (!empty($options['reverse'])) { @@ -1441,12 +1469,14 @@ abstract class Horde_Imap_Client_Base */ public function thread($mailbox, $options = array()) { + $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO); + /* Take advantage of search result caching. If CONDSTORE available, * we can cache all queries and invalidate the cache when the MODSEQ * changes. If CONDSTORE not available, we can only store queries * that don't involve flags. See search() for similar caching. */ $cache = null; - if ($this->_initCache() && + if ($this->_initCache(true) && (isset($this->_init['enabled']['CONDSTORE']) || empty($options['search']) || !$options['search']->flagSearch())) { @@ -1456,8 +1486,6 @@ abstract class Horde_Imap_Client_Base } } - $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO); - $ob = new Horde_Imap_Client_Thread($this->_thread($options), empty($options['sequence']) ? 'uid' : 'sequence'); if ($cache) { @@ -1835,7 +1863,6 @@ abstract class Horde_Imap_Client_Base public function fetch($mailbox, $criteria, $options = array()) { $cache_array = $get_fields = $new_criteria = $ret = array(); - $cf = $this->_initCache() ? $this->_params['cache']['fields'] : array(); $qresync = isset($this->_init['enabled']['QRESYNC']); $seq = !empty($options['sequence']); @@ -1853,13 +1880,16 @@ abstract class Horde_Imap_Client_Base throw new Horde_Imap_Client_Exception('The vanished FETCH modifier is missing a pre-requisite.'); } + $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO); + + $cf = $this->_initCache(true) + ? $this->_params['cache']['fields'] + : array(); + /* The 'changedsince' modifier implicitly adds the MODSEQ FETCH item. * (RFC 4551 [3.3.1]). A UID SEARCH will always return UID * information (RFC 3501 [6.4.8]). Don't add to criteria because it * simply creates a longer FETCH command. */ - - $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO); - if (!empty($cf)) { /* We need the UIDVALIDITY for the current mailbox. */ $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_HIGHESTMODSEQ | Horde_Imap_Client::STATUS_UIDVALIDITY); @@ -2443,7 +2473,11 @@ abstract class Horde_Imap_Client_Base */ protected function _updateCache($data, $options = array()) { - if (!$this->_initCache()) { + $mailbox = empty($options['mailbox']) + ? $this->_selected + : $options['mailbox']; + + if (!$this->_initCache(empty($options['mailbox']))) { return; } @@ -2453,7 +2487,6 @@ abstract class Horde_Imap_Client_Base $cf = $this->_params['cache']['fields']; $tocache = array(); - $mailbox = empty($options['mailbox']) ? $this->_selected : $options['mailbox']; $status_flags = 0; if (isset($this->_init['enabled']['CONDSTORE'])) { diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Cclient.php b/framework/Imap_Client/lib/Horde/Imap/Client/Cclient.php index eb1bdd7d0..dac509936 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Cclient.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Cclient.php @@ -732,7 +732,7 @@ class Horde_Imap_Client_Cclient extends Horde_Imap_Client_Base /* If we are using a cache, we need to get the list of * messages that will be expunged. */ - if ($this->_initCache()) { + if ($this->_initCache($this->_selected)) { if ($use_seq) { $res = $this->search($this->_selected, $search_query); $expunged = $res['match']; diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php b/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php index c8e6ce59b..15055e9e5 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php @@ -822,18 +822,11 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base $this->_init['enabled']['CONDSTORE'] = true; } - if (isset($this->_init['enabled']['CONDSTORE'])) { - /* MODSEQ should be set if CONDSTORE is active. Some servers won't - * advertise in SELECT/EXAMINE info though. */ - if (!isset($this->_temp['mailbox']['highestmodseq'])) { - $this->_temp['mailbox']['highestmodseq'] = 1; - } elseif ($qresync && - empty($this->_temp['mailbox']['highestmodseq'])) { - /* Check that MODSEQ is enabled for the mailbox and, if not, - * delete the cache. Note: Invalidating the cache based on - * UIDVALIDITY is done within Horde_Imap_Client_Cache::. */ - $this->_cache->deleteMailbox($mailbox); - } + /* MODSEQ should be set if CONDSTORE is active. Some servers won't + * advertise in SELECT/EXAMINE info though. */ + if (isset($this->_init['enabled']['CONDSTORE']) && + !isset($this->_temp['mailbox']['highestmodseq'])) { + $this->_temp['mailbox']['highestmodseq'] = 1; } } @@ -1274,7 +1267,7 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base } else { // If caching, we need to know the UIDs being deleted, so call // expunge() before calling close(). - if ($this->_initCache()) { + if ($this->_initCache(true)) { $this->expunge($this->_selected); } @@ -1305,7 +1298,7 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base $seq = !empty($options['sequence']); $s_res = null; $uidplus = $this->queryCapability('UIDPLUS'); - $use_cache = $this->_initCache(); + $use_cache = $this->_initCache(true); if (empty($options['ids'])) { $uid_string = '1:*'; @@ -1398,15 +1391,20 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base if (!empty($expunged)) { $this->_cache->deleteMsgs($mailbox, $expunged); $tmp['mailbox']['messages'] -= $i; - if (isset($this->_init['enabled']['QRESYNC'])) { - $this->_updateMetaData($mailbox, array('HICmodseq' => $this->_temp['mailbox']['highestmodseq'])); - } elseif (isset($this->_init['enabled']['CONDSTORE'])) { - /* Unfortunately, RFC 4551 does not provide any method to - * obtain the HIGHESTMODSEQ after an EXPUNGE is completed. - * Instead, unselect the mailbox - if we need to reselect - * the mailbox, the HIGHESTMODSEQ info will appear in the - * EXAMINE/SELECT response. */ - $this->close(); + + /* Update MODSEQ if active for mailbox. */ + if (!empty($this->_temp['mailbox']['highestmodseq'])) { + if (isset($this->_init['enabled']['QRESYNC'])) { + $this->_updateMetaData($mailbox, array('HICmodseq' => $this->_temp['mailbox']['highestmodseq'])); + } else { + /* Unfortunately, RFC 4551 does not provide any method + * to obtain the HIGHESTMODSEQ after an EXPUNGE is + * completed. Instead, unselect the mailbox - if we + * need to reselect the mailbox, the HIGHESTMODSEQ + * info will appear in the EXAMINE/SELECT + * HIGHESTMODSEQ response. */ + $this->close(); + } } } @@ -2637,7 +2635,8 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base $cmd_prefix = (empty($options['sequence']) ? 'UID ' : '') . 'STORE ' . $seq . ' '; - $ucsince = null; + $condstore = $ucsince = null; + if (empty($this->_temp['mailbox']['highestmodseq'])) { if (!empty($options['unchangedsince'])) { /* RFC 4551 [3.1] - trying to do a UNCHANGEDSINCE STORE on a @@ -2645,12 +2644,21 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base * here and throw an exception. */ throw new Horde_Imap_Client_Exception('Mailbox does not support mod-sequences.', Horde_Imap_Client_Exception::MBOXNOMODSEQ); } - } elseif (!empty($options['unchangedsince'])) { - $ucsince = intval($options['unchangedsince']); } else { - /* If CONDSTORE is enabled, we need to add UNCHANGEDSINCE output - * to ensure we get MODSEQ updated information. */ - $ucsince = $this->_temp['mailbox']['highestmodseq']; + if (!empty($options['unchangedsince'])) { + $ucsince = intval($options['unchangedsince']); + } + + if (isset($this->_init['enabled']['CONDSTORE'])) { + /* If we reach here, MODSEQ is active for mailbox. */ + $condstore = true; + + /* If CONDSTORE is enabled, we need to verify UNCHANGEDSINCE + * added to ensure we get MODSEQ updated information. */ + if (is_null($ucsince)) { + $ucsince = $this->_temp['mailbox']['highestmodseq']; + } + } } if ($ucsince) { @@ -2671,8 +2679,7 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base /* Update the flags in the cache. Only update if store was successful * and flag information was not returned. */ - if (!empty($this->_temp['fetchresp']) && - isset($this->_init['enabled']['CONDSTORE'])) { + if ($condstore && !empty($this->_temp['fetchresp'])) { $fr = $this->_temp['fetchresp']; $out = $uids = array(); @@ -3576,9 +3583,18 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base break; case 'HIGHESTMODSEQ': + // Defined by RFC 4551 [3.1.1] + $this->_temp['mailbox']['highestmodseq'] = $data; + break; + case 'NOMODSEQ': - // Defined by RFC 4551 [3.1.1 & 3.1.2] - $this->_temp['mailbox']['highestmodseq'] = ($code == 'HIGHESTMODSEQ') ? $data : 0; + // Defined by RFC 4551 [3.1.2] + $this->_temp['mailbox']['highestmodseq'] = 0; + + // Delete cache for mailbox, if it exists. + if ($this->_initCache()) { + $this->_cache->deleteMailbox($this->_selected); + } break; case 'MODIFIED':