Search for messages modified since a specific moment. The IMAP server
must support the CONDSTORE extension (RFC 7162) for this query to be
used.
public modseq ( integer $value, string $name = null, string $type = null, boolean $not = false, array $opts = [] ) | ||
$value | integer | The mod-sequence value. |
$name | string | The entry-name string. |
$type | string | Either 'shared', 'priv', or 'all'. Defaults to 'all' |
$not | boolean | If true, do a 'NOT' search. |
$opts | array | Additional options: - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server MUST support RFC 6203. |
/** * @dataProvider modseqSearchQueryProvider */ public function testModseq($name, $type, $not, $fuzzy, $expected) { $ob = new Horde_Imap_Client_Search_Query(); $ob->modseq(123, $name, $type, $not, array('fuzzy' => $fuzzy)); $this->assertEquals($expected, $fuzzy ? $this->_fuzzy($ob, array('CONDSTORE')) : strval($ob)); }
/** * Return message changes from the specified mailbox. * * @param Horde_ActiveSync_Folder_Imap $folder The folder object. * @param array $options Additional options: * - sincedate: (integer) Timestamp of earliest message to retrieve. * DEFAULT: 0 (Don't filter). * - protocolversion: (float) EAS protocol version to support. * DEFAULT: none REQUIRED * - softdelete: (boolean) If true, calculate SOFTDELETE data. * @since 2.8.0 * - refreshfilter: (boolean) If true, force refresh the query to reflect * changes in FILTERTYPE (using the sincedate) * @since 2.28.0 * * @return Horde_ActiveSync_Folder_Imap The folder object, containing any * change instructions for the device. * * @throws Horde_ActiveSync_Exception_FolderGone, * Horde_ActiveSync_Exception, Horde_ActiveSync_Exception_StaleState */ public function getMessageChanges(Horde_ActiveSync_Folder_Imap $folder, array $options = array()) { $imap = $this->_getImapOb(); $mbox = new Horde_Imap_Client_Mailbox($folder->serverid()); $flags = array(); $search_uids = array(); // Note: non-CONDSTORE servers will return a highestmodseq of 0 $status_flags = Horde_Imap_Client::STATUS_HIGHESTMODSEQ | Horde_Imap_Client::STATUS_UIDVALIDITY | Horde_Imap_Client::STATUS_UIDNEXT_FORCE | Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_FORCE_REFRESH; try { $status = $imap->status($mbox, $status_flags); } catch (Horde_Imap_Client_Exception $e) { // If we can't status the mailbox, assume it's gone. throw new Horde_ActiveSync_Exception_FolderGone($e); } $this->_logger->info(sprintf('[%s] IMAP status: %s', $this->_procid, serialize($status))); $current_modseq = $status[Horde_ActiveSync_Folder_Imap::HIGHESTMODSEQ]; if ($current_modseq && $folder->modseq() > 0 && ($folder->modseq() < $current_modseq || !empty($options['softdelete']) || !empty($options['refreshfilter']))) { $this->_logger->info(sprintf('[%s] CONDSTORE and CHANGES', $this->_procid)); $folder->checkValidity($status); // Catch all *changes* since the provided MODSEQ value. $query = new Horde_Imap_Client_Search_Query(); // $imap->search uses a >= comparison for MODSEQ, so we must // increment by one so we don't continuously receive the same change // set. $query->modseq($folder->modseq() + 1); if (!empty($options['sincedate'])) { $query->dateSearch(new Horde_Date($options['sincedate']), Horde_Imap_Client_Search_Query::DATE_SINCE); } $search_ret = $imap->search($mbox, $query, array('results' => array(Horde_Imap_Client::SEARCH_RESULTS_MATCH))); $search_uids = $search_ret['count'] ? $search_ret['match']->ids : array(); // Catch changes to FILTERTYPE. if (!empty($options['refreshfilter'])) { $this->_logger->info(sprintf('[%s] Checking for additional messages within the new FilterType parameters.', $this->_procid)); $search_ret = $this->_buildSearchQuery($folder, $options, $mbox, false); if ($search_ret['count']) { $this->_logger->info(sprintf('[%s] Found %d messages that are now outside FilterType.', $this->_procid, $search_ret['count'])); $search_uids = array_merge($search_uids, $search_ret['match']->ids); } else { $this->_logger->info(sprintf('[%s] Found NO additional messages.', $this->_procid)); } } // Protect against very large change sets like might occur if // the FILTERTYPE is changed from some short interval like one week // to no filter at all. $cnt = count($search_uids) / self::MAX_FETCH + 1; $query = new Horde_Imap_Client_Fetch_Query(); $query->modseq(); $query->flags(); $changes = array(); $categories = array(); for ($i = 0; $i <= $cnt; $i++) { $ids = new Horde_Imap_Client_Ids(array_slice($search_uids, $i * self::MAX_FETCH, self::MAX_FETCH)); try { $fetch_ret = $imap->fetch($mbox, $query, array('ids' => $ids)); } catch (Horde_Imap_Client_Exception $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } $this->_buildModSeqChanges($changes, $flags, $categories, $fetch_ret, $options, $current_modseq); } // Set the changes in the folder object. $folder->setChanges($changes, $flags, $categories, !empty($options['softdelete']) || !empty($options['refreshfilter'])); // Check for deleted messages. try { $deleted = $imap->vanished($mbox, $folder->modseq(), array('ids' => new Horde_Imap_Client_Ids($folder->messages()))); } catch (Horde_Imap_Client_Excetion $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } $folder->setRemoved($deleted->ids); $this->_logger->info(sprintf('[%s] Found %d deleted messages.', $this->_procid, $deleted->count())); // Check for SOFTDELETE messages. if ((!empty($options['softdelete']) || !empty($options['refreshfilter'])) && !empty($options['sincedate'])) { $this->_logger->info(sprintf('[%s] Polling for SOFTDELETE in %s before %d', $this->_procid, $folder->serverid(), $options['sincedate'])); $search_ret = $this->_buildSearchQuery($folder, $options, $mbox, true); if ($search_ret['count']) { $this->_logger->info(sprintf('[%s] Found %d messages to SOFTDELETE.', $this->_procid, count($search_ret['match']->ids))); $folder->setSoftDeleted($search_ret['match']->ids); } else { $this->_logger->info(sprintf('[%s] Found NO messages to SOFTDELETE.', $this->_procid)); } $folder->setSoftDeleteTimes($options['sincedate'], time()); } } elseif ($folder->uidnext() == 0) { $this->_logger->info(sprintf('[%s] INITIAL SYNC', $this->_procid)); $query = new Horde_Imap_Client_Search_Query(); if (!empty($options['sincedate'])) { $query->dateSearch(new Horde_Date($options['sincedate']), Horde_Imap_Client_Search_Query::DATE_SINCE); } $search_ret = $imap->search($mbox, $query, array('results' => array(Horde_Imap_Client::SEARCH_RESULTS_MATCH))); if ($current_modseq && !$folder->haveInitialSync) { $this->_logger->info(sprintf('[%s] Priming IMAP folder object.', $this->_procid)); $folder->primeFolder($search_ret['match']->ids); } elseif (count($search_ret['match']->ids)) { // No modseq. $query = new Horde_Imap_Client_Fetch_Query(); $query->flags(); $cnt = $search_ret['count'] / self::MAX_FETCH + 1; for ($i = 0; $i <= $cnt; $i++) { $ids = new Horde_Imap_Client_Ids(array_slice($search_ret['match']->ids, $i * self::MAX_FETCH, self::MAX_FETCH)); $fetch_ret = $imap->fetch($mbox, $query, array('ids' => $ids)); foreach ($fetch_ret as $uid => $data) { $flags[$uid] = array('read' => array_search(Horde_Imap_Client::FLAG_SEEN, $data->getFlags()) !== false ? 1 : 0); if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWOFIVE) { $flags[$uid]['flagged'] = array_search(Horde_Imap_Client::FLAG_FLAGGED, $data->getFlags()) !== false ? 1 : 0; } } } $folder->setChanges($search_ret['match']->ids, $flags); } } elseif ($current_modseq == 0) { $this->_logger->info(sprintf('[%s] NO CONDSTORE or per mailbox MODSEQ. minuid: %s, total_messages: %s', $this->_procid, $folder->minuid(), $status['messages'])); $folder->checkValidity($status); $query = new Horde_Imap_Client_Search_Query(); if (!empty($options['sincedate'])) { $query->dateSearch(new Horde_Date($options['sincedate']), Horde_Imap_Client_Search_Query::DATE_SINCE); } try { $search_ret = $imap->search($mbox, $query, array('results' => array(Horde_Imap_Client::SEARCH_RESULTS_MATCH))); } catch (Horde_Imap_Client_Exception $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } $cnt = $search_ret['count'] / self::MAX_FETCH + 1; $query = new Horde_Imap_Client_Fetch_Query(); $query->flags(); for ($i = 0; $i <= $cnt; $i++) { $ids = new Horde_Imap_Client_Ids(array_slice($search_ret['match']->ids, $i * self::MAX_FETCH, self::MAX_FETCH)); try { $fetch_ret = $imap->fetch($mbox, $query, array('ids' => $ids)); } catch (Horde_Imap_Client_Exception $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } foreach ($fetch_ret as $uid => $data) { $flags[$uid] = array('read' => array_search(Horde_Imap_Client::FLAG_SEEN, $data->getFlags()) !== false ? 1 : 0); if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWOFIVE) { $flags[$uid]['flagged'] = array_search(Horde_Imap_Client::FLAG_FLAGGED, $data->getFlags()) !== false ? 1 : 0; } } } if (!empty($flags)) { $folder->setChanges($search_ret['match']->ids, $flags); } $folder->setRemoved($imap->vanished($mbox, null, array('ids' => new Horde_Imap_Client_Ids($folder->messages())))->ids); } elseif ($current_modseq > 0 && $folder->modseq() == 0) { throw new Horde_ActiveSync_Exception_StaleState('Transition to MODSEQ enabled server'); } $folder->setStatus($status); return $folder; }
/** * Constructor. * * @param Horde_Imap_Client_Base $base_ob Base driver object. * @param mixed $mailbox Mailbox to sync. * @param array $sync Token sync data. * @param array $curr Current sync data. * @param integer $criteria Mask of criteria to return. * @param Horde_Imap_Client_Ids $ids List of known UIDs. * * @throws Horde_Imap_Client_Exception * @throws Horde_Imap_Client_Exception_Sync */ public function __construct(Horde_Imap_Client_Base $base_ob, $mailbox, $sync, $curr, $criteria, $ids) { foreach (self::$map as $key => $val) { if (isset($sync[$key])) { $this->{$val} = $sync[$key]; } } /* Check uidvalidity. */ if (!$this->uidvalidity || $curr['V'] != $this->uidvalidity) { throw new Horde_Imap_Client_Exception_Sync('UIDs in cached mailbox have changed.', Horde_Imap_Client_Exception_Sync::UIDVALIDITY_CHANGED); } $this->mailbox = $mailbox; /* This was a UIDVALIDITY check only. */ if (!$criteria) { return; } $sync_all = $criteria & Horde_Imap_Client::SYNC_ALL; /* New messages. */ if ($sync_all || $criteria & Horde_Imap_Client::SYNC_NEWMSGS || $criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS) { $this->newmsgs = empty($this->uidnext) ? !empty($curr['U']) : !empty($curr['U']) && $curr['U'] > $this->uidnext; if ($this->newmsgs && ($sync_all || $criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS)) { $new_ids = empty($this->uidnext) ? Horde_Imap_Client_Ids::ALL : $this->uidnext . ':' . $curr['U']; $squery = new Horde_Imap_Client_Search_Query(); $squery->ids(new Horde_Imap_Client_Ids($new_ids)); $sres = $base_ob->search($mailbox, $squery); $this->_newmsgsuids = $sres['match']; } } /* Do single status call to get all necessary data. */ if ($this->highestmodseq && ($sync_all || $criteria & Horde_Imap_Client::SYNC_FLAGS || $criteria & Horde_Imap_Client::SYNC_FLAGSUIDS || $criteria & Horde_Imap_Client::SYNC_VANISHED || $criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS)) { $status_sync = $base_ob->status($mailbox, Horde_Imap_Client::STATUS_SYNCMODSEQ | Horde_Imap_Client::STATUS_SYNCFLAGUIDS | Horde_Imap_Client::STATUS_SYNCVANISHED); if (!is_null($ids)) { $ids = $base_ob->resolveIds($mailbox, $ids); } } /* Flag changes. */ if ($sync_all || $criteria & Horde_Imap_Client::SYNC_FLAGS) { $this->flags = $this->highestmodseq ? $this->highestmodseq != $curr['H'] : true; } if ($sync_all || $criteria & Horde_Imap_Client::SYNC_FLAGSUIDS) { if ($this->highestmodseq) { if ($this->highestmodseq == $status_sync['syncmodseq']) { $this->_flagsuids = is_null($ids) ? $status_sync['syncflaguids'] : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncflaguids']->ids)); } else { $squery = new Horde_Imap_Client_Search_Query(); $squery->modseq($this->highestmodseq + 1); $sres = $base_ob->search($mailbox, $squery, array('ids' => $ids)); $this->_flagsuids = $sres['match']; } } else { /* Without MODSEQ, need to mark all FLAGS as changed. */ $this->_flagsuids = $base_ob->resolveIds($mailbox, is_null($ids) ? $base_ob->getIdsOb(Horde_Imap_Client_Ids::ALL) : $ids); } } /* Vanished messages. */ if ($sync_all || $criteria & Horde_Imap_Client::SYNC_VANISHED || $criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS) { if ($this->highestmodseq && $this->highestmodseq == $status_sync['syncmodseq']) { $vanished = is_null($ids) ? $status_sync['syncvanished'] : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncvanished']->ids)); } else { $vanished = $base_ob->vanished($mailbox, $this->highestmodseq ? $this->highestmodseq : 1, array('ids' => $ids)); } $this->vanished = (bool) count($vanished); $this->_vanisheduids = $vanished; } }
/** * Wrapper for fetch() to allow internal state to be reset on exception. * * @internal * @see fetch() */ private function _fetchWrapper($mailbox, $query, $options) { $this->login(); $query = clone $query; $cache_array = $header_cache = $new_query = array(); if (empty($options['ids'])) { $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL); } elseif ($options['ids']->isEmpty()) { return new Horde_Imap_Client_Fetch_Results($this->_fetchDataClass); } elseif ($options['ids']->search_res && !$this->_capability('SEARCHRES')) { /* SEARCHRES requires server support. */ throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES'); } $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO); $mbox_ob = $this->_mailboxOb(); if (!empty($options['nocache'])) { $this->_temp['fetch_nocache'] = true; } $cf = $this->_initCache(true) ? $this->_cacheFields() : array(); if (!empty($cf)) { /* If using cache, we store by UID so we need to return UIDs. */ $query->uid(); } $modseq_check = !empty($options['changedsince']); if ($query->contains(Horde_Imap_Client::FETCH_MODSEQ)) { if (!$this->_capability()->isEnabled('CONDSTORE')) { unset($query[Horde_Imap_Client::FETCH_MODSEQ]); } elseif (empty($options['changedsince'])) { $modseq_check = true; } } if ($modseq_check && !$mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) { /* RFC 7162 [3.1.2.2] - trying to do a MODSEQ FETCH on a mailbox * that doesn't support it will return BAD. */ throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Mailbox does not support mod-sequences."), Horde_Imap_Client_Exception::MBOXNOMODSEQ); } /* Determine if caching is available and if anything in $query is * cacheable. */ foreach ($cf as $k => $v) { if (isset($query[$k])) { switch ($k) { case Horde_Imap_Client::FETCH_ENVELOPE: case Horde_Imap_Client::FETCH_FLAGS: case Horde_Imap_Client::FETCH_IMAPDATE: case Horde_Imap_Client::FETCH_SIZE: case Horde_Imap_Client::FETCH_STRUCTURE: $cache_array[$k] = $v; break; case Horde_Imap_Client::FETCH_HEADERS: $this->_temp['headers_caching'] = array(); foreach ($query[$k] as $key => $val) { /* Only cache if directly requested. Iterate through * requests to ensure at least one can be cached. */ if (!empty($val['cache']) && !empty($val['peek'])) { $cache_array[$k] = $v; ksort($val); $header_cache[$key] = hash('md5', serialize($val)); } } break; } } } $ret = new Horde_Imap_Client_Fetch_Results($this->_fetchDataClass, $options['ids']->sequence ? Horde_Imap_Client_Fetch_Results::SEQUENCE : Horde_Imap_Client_Fetch_Results::UID); /* If nothing is cacheable, we can do a straight search. */ if (empty($cache_array)) { $options['_query'] = $query; $this->_fetch($ret, array($options)); return $ret; } $cs_ret = empty($options['changedsince']) ? null : clone $ret; /* Convert special searches to UID lists and create mapping. */ $ids = $this->resolveIds($this->_selected, $options['ids'], empty($options['exists']) ? 1 : 2); /* Add non-user settable cache fields. */ $cache_array[Horde_Imap_Client::FETCH_DOWNGRADED] = self::CACHE_DOWNGRADED; /* Get the cached values. */ $data = $this->_cache->get($this->_selected, $ids->ids, array_values($cache_array), $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY)); /* Build a list of what we still need. */ $map = array_flip($mbox_ob->map->map); $sequence = $options['ids']->sequence; foreach ($ids as $uid) { $crit = clone $query; if ($sequence) { if (!isset($map[$uid])) { continue; } $entry_idx = $map[$uid]; } else { $entry_idx = $uid; unset($crit[Horde_Imap_Client::FETCH_UID]); } $entry = $ret->get($entry_idx); if (isset($map[$uid])) { $entry->setSeq($map[$uid]); unset($crit[Horde_Imap_Client::FETCH_SEQ]); } $entry->setUid($uid); foreach ($cache_array as $key => $cid) { switch ($key) { case Horde_Imap_Client::FETCH_DOWNGRADED: if (!empty($data[$uid][$cid])) { $entry->setDowngraded(true); } break; case Horde_Imap_Client::FETCH_ENVELOPE: if (isset($data[$uid][$cid]) && $data[$uid][$cid] instanceof Horde_Imap_Client_Data_Envelope) { $entry->setEnvelope($data[$uid][$cid]); unset($crit[$key]); } break; case Horde_Imap_Client::FETCH_FLAGS: if (isset($data[$uid][$cid]) && is_array($data[$uid][$cid])) { $entry->setFlags($data[$uid][$cid]); unset($crit[$key]); } break; case Horde_Imap_Client::FETCH_HEADERS: foreach ($header_cache as $hkey => $hval) { if (isset($data[$uid][$cid][$hval])) { /* We have found a cached entry with the same * MD5 sum. */ $entry->setHeaders($hkey, $data[$uid][$cid][$hval]); $crit->remove($key, $hkey); } else { $this->_temp['headers_caching'][$hkey] = $hval; } } break; case Horde_Imap_Client::FETCH_IMAPDATE: if (isset($data[$uid][$cid]) && $data[$uid][$cid] instanceof Horde_Imap_Client_DateTime) { $entry->setImapDate($data[$uid][$cid]); unset($crit[$key]); } break; case Horde_Imap_Client::FETCH_SIZE: if (isset($data[$uid][$cid])) { $entry->setSize($data[$uid][$cid]); unset($crit[$key]); } break; case Horde_Imap_Client::FETCH_STRUCTURE: if (isset($data[$uid][$cid]) && $data[$uid][$cid] instanceof Horde_Mime_Part) { $entry->setStructure($data[$uid][$cid]); unset($crit[$key]); } break; } } if (count($crit)) { $sig = $crit->hash(); if (isset($new_query[$sig])) { $new_query[$sig]['i'][] = $entry_idx; } else { $new_query[$sig] = array('c' => $crit, 'i' => array($entry_idx)); } } } $to_fetch = array(); foreach ($new_query as $val) { $ids_ob = $this->getIdsOb(null, $sequence); $ids_ob->duplicates = true; $ids_ob->add($val['i']); $to_fetch[] = array_merge($options, array('_query' => $val['c'], 'ids' => $ids_ob)); } if (!empty($to_fetch)) { $this->_fetch(is_null($cs_ret) ? $ret : $cs_ret, $to_fetch); } if (is_null($cs_ret)) { return $ret; } /* If doing changedsince query, and all other data is cached, we still * need to hit IMAP server to determine proper results set. */ if (empty($new_query)) { $squery = new Horde_Imap_Client_Search_Query(); $squery->modseq($options['changedsince'] + 1); $squery->ids($options['ids']); $cs = $this->search($this->_selected, $squery, array('sequence' => $sequence)); foreach ($cs['match'] as $val) { $entry = $ret->get($val); if ($sequence) { $entry->setSeq($val); } else { $entry->setUid($val); } $cs_ret[$val] = $entry; } } else { foreach ($cs_ret as $key => $val) { $val->merge($ret->get($key)); } } return $cs_ret; }
/** * Return a folder object containing all IMAP server change information. * * @param array $options An array of options. * @see Horde_ActiveSync_Imap_Adapter::getMessageChanges * * @return Horde_ActiveSync_Folder_Base The populated folder object. */ public function getChanges(array $options) { $this->_logger->info(sprintf('[%s] CONDSTORE and CHANGES', $this->_procid)); $flags = array(); $current_modseq = $this->_status[Horde_ActiveSync_Folder_Imap::HIGHESTMODSEQ]; $query = new Horde_Imap_Client_Search_Query(); // Increment since $imap->search uses >= operator. if ($this->_modseq_valid) { $query->modseq($this->_folder->modseq() + 1); } if (!empty($options['sincedate'])) { $query->dateSearch(new Horde_Date($options['sincedate']), Horde_Imap_Client_Search_Query::DATE_SINCE); } $search_ret = $this->_imap_ob->search($this->_mbox, $query, array('results' => array(Horde_Imap_Client::SEARCH_RESULTS_MATCH))); $search_uids = $search_ret['count'] ? $search_ret['match']->ids : array(); // Catch changes to FILTERTYPE. if (!empty($options['refreshfilter'])) { $this->_logger->info(sprintf('[%s] Checking for additional messages within the new FilterType parameters.', $this->_procid)); $search_ret = $this->_searchQuery($options, false); if ($search_ret['count']) { $this->_logger->info(sprintf('[%s] Found %d messages that are now outside FilterType.', $this->_procid, $search_ret['count'])); $search_uids = array_merge($search_uids, $search_ret['match']->ids); } } // Protect against very large change sets. $cnt = count($search_uids) / Horde_ActiveSync_Imap_Adapter::MAX_FETCH + 1; $query = new Horde_Imap_Client_Fetch_Query(); if ($this->_modseq_valid) { $query->modseq(); } $query->flags(); $changes = array(); $categories = array(); for ($i = 0; $i <= $cnt; $i++) { $ids = new Horde_Imap_Client_Ids(array_slice($search_uids, $i * Horde_ActiveSync_Imap_Adapter::MAX_FETCH, Horde_ActiveSync_Imap_Adapter::MAX_FETCH)); try { $fetch_ret = $this->_imap_ob->fetch($this->_mbox, $query, array('ids' => $ids)); } catch (Horde_Imap_Client_Exception $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } $this->_buildModSeqChanges($changes, $flags, $categories, $fetch_ret, $options, $current_modseq); } // Set the changes in the folder object. $this->_folder->setChanges($changes, $flags, $categories, !empty($options['softdelete']) || !empty($options['refreshfilter'])); // Check for deleted messages. try { $deleted = $this->_imap_ob->vanished($this->_mbox, $this->_folder->modseq(), array('ids' => new Horde_Imap_Client_Ids($this->_folder->messages()))); } catch (Horde_Imap_Client_Excetion $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } $this->_folder->setRemoved($deleted->ids); $this->_logger->info(sprintf('[%s] Found %d deleted messages.', $this->_procid, $deleted->count())); // Check for SOFTDELETE messages. if (!empty($options['sincedate']) && (!empty($options['softdelete']) || !empty($options['refreshfilter']))) { $this->_logger->info(sprintf('[%s] Polling for SOFTDELETE in %s before %d', $this->_procid, $this->_folder->serverid(), $options['sincedate'])); $search_ret = $this->_searchQuery($options, true); if ($search_ret['count']) { $this->_logger->info(sprintf('[%s] Found %d messages to SOFTDELETE.', $this->_procid, count($search_ret['match']->ids))); $this->_folder->setSoftDeleted($search_ret['match']->ids); } $this->_folder->setSoftDeleteTimes($options['sincedate'], time()); } return $this->_folder; }