public function testInitialState() { $folder = new Horde_ActiveSync_Folder_Imap('INBOX', Horde_ActiveSync::CLASS_EMAIL); $thrown = false; try { $folder->checkValidity(array(Horde_ActiveSync_Folder_Imap::UIDVALIDITY => '123')); } catch (Horde_ActiveSync_Exception $e) { $thrown = true; } $this->assertEquals(true, $thrown); $this->assertEquals(0, $folder->uidnext()); $this->assertEquals(0, $folder->modseq()); $this->assertEquals(array(), $folder->messages()); $this->assertEquals(array(), $folder->flags()); $this->assertEquals(array(), $folder->added()); $this->assertEquals(array(), $folder->changed()); $this->assertEquals(array(), $folder->removed()); $this->assertEquals(0, $folder->minuid()); }
/** * 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 * * @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()); // 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))); $flags = array(); $categories = array(); $modseq = $status[Horde_ActiveSync_Folder_Imap::HIGHESTMODSEQ]; if ($modseq && $folder->modseq() > 0 && $folder->modseq() < $modseq) { $this->_logger->info(sprintf('[%s] CONDSTORE and CHANGES', $this->_procid)); $folder->checkValidity($status); $query = new Horde_Imap_Client_Fetch_Query(); $query->modseq(); $query->flags(); $query->headerText(array('peek' => true)); try { $fetch_ret = $imap->fetch($mbox, $query, array('changedsince' => $folder->modseq())); } catch (Horde_Imap_Client_Exception $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } // Prepare the changes and flags array, ensuring no changes after // $modseq sneak in yet (they will be caught on the next PING or // SYNC). $changes = array(); // Get custom flags to use as categories. $msgFlags = $this->_getMsgFlags(); foreach ($fetch_ret as $uid => $data) { if ($options['sincedate']) { $since = new Horde_Date($options['sincedate']); $headers = Horde_Mime_Headers::parseHeaders($data->getHeaderText()); try { $date = new Horde_Date($headers->getValue('Date')); if ($date->compareDate($since) <= -1) { // Ignore, it's out of the FILTERTYPE range. $this->_logger->info(sprintf('[%s] Ignoring UID %s since it is outside of the FILTERTYPE (%s)', $this->_procid, $uid, $headers->getValue('Date'))); continue; } } catch (Horde_Date_Exception $e) { } } if ($data->getModSeq() <= $modseq) { $changes[] = $uid; $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 ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWELVEONE) { $categories[$uid] = array(); foreach ($data->getFlags() as $flag) { if (($key = array_search(strtolower($flag), array_map('strtolower', $msgFlags))) !== false) { $categories[$uid][] = $msgFlags[$key]; } } } } } $folder->setChanges($changes, $flags, $categories); 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); if (!empty($options['softdelete']) && !empty($options['sincedate'])) { $this->_logger->info(sprintf('[%s] Polling for SOFTDELETE in %s before %d', $this->_procid, $folder->serverid(), $options['sincedate'])); $query = new Horde_Imap_Client_Search_Query(); $query->dateSearch(new Horde_Date($options['sincedate']), Horde_Imap_Client_Search_Query::DATE_BEFORE); $query->ids(new Horde_Imap_Client_Ids($folder->messages())); 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); } 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 ($modseq && $folder->modseq() > 0 && $search_ret['count']) { $folder->setChanges($search_ret['match']->ids, array()); } elseif (count($search_ret['match']->ids)) { $query = new Horde_Imap_Client_Fetch_Query(); $query->flags(); $fetch_ret = $imap->fetch($mbox, $query, array('ids' => $search_ret['match'])); 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 ($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); } if (count($search_ret['match']->ids)) { // Update flags. $query = new Horde_Imap_Client_Fetch_Query(); $query->flags(); try { $fetch_ret = $imap->fetch($mbox, $query, array('ids' => $search_ret['match'])); } 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; } } $folder->setChanges($search_ret['match']->ids, $flags); } $folder->setRemoved($imap->vanished($mbox, null, array('ids' => new Horde_Imap_Client_Ids($folder->messages())))->ids); } elseif ($modseq > 0 && $folder->modseq() == 0) { throw new Horde_ActiveSync_Exception_StaleState('Transition to MODSEQ enabled server'); } $folder->setStatus($status); return $folder; }