Пример #1
0
 /**
  * Create a new mailbox on the server, and subscribe to it.
  *
  * @param string $name    The new mailbox name.
  * @param string $parent  The parent mailbox, if any.
  *
  *  @return string  The new serverid for the mailbox. This is the UTF-8 name
  *                  of the mailbox. @since 2.9.0
  *  @throws Horde_ActiveSync_Exception, Horde_ActiveSync_Exception_FolderExists
  */
 public function createMailbox($name, $parent = null)
 {
     if (!empty($parent)) {
         $ns = $this->_defaultNamespace();
         $name = $parent . $ns['delimiter'] . $name;
     }
     $mbox = new Horde_Imap_Client_Mailbox($this->_prependNamespace($name));
     $imap = $this->_getImapOb();
     try {
         $imap->createMailbox($mbox);
         $imap->subscribeMailbox($mbox, true);
     } catch (Horde_Imap_Client_Exception $e) {
         if ($e->getCode() == Horde_Imap_Client_Exception::ALREADYEXISTS) {
             $this->_logger->warn(sprintf('[%s] Mailbox %s already exists, subscribing to it.', $this->_procid, $name));
             try {
                 $imap->subscribeMailbox($mbox, true);
             } catch (Horde_Imap_Client_Exception $e) {
                 // Exists, but could not subscribe to it, something is
                 // *really* wrong.
                 throw new Horde_ActiveSync_Exception_FolderExists('Folder Exists!');
             }
         }
         throw new Horde_ActiveSync_Exception($e);
     }
     return $mbox->utf8;
 }
Пример #2
0
 /**
  * Set a collection as non-PINGable.
  *
  * @param string $collectionid  The collection id.
  */
 public function removePingableCollection($id)
 {
     if (empty($this->_data['collections'][$id])) {
         $this->_logger->warn(sprintf('[%s] Collection %s was asked to be removed from PINGABLE but does not exist.', $this->_procid, $id));
         return;
     }
     $this->_data['collections'][$id]['pingable'] = false;
     $this->_markCollectionsDirty($id);
 }
Пример #3
0
 /**
  * Poll the backend for changes.
  *
  * @param integer $heartbeat  The heartbeat lifetime to wait for changes.
  * @param integer $interval   The wait interval between poll iterations.
  * @param array $options      An options array containing any of:
  *   - pingable: (boolean)  Only poll collections with the pingable flag set.
  *                DEFAULT: false
  *
  * @return boolean|integer True if changes were detected in any of the
  *                         collections, false if no changes detected
  *                         or a status code if failed.
  */
 public function pollForChanges($heartbeat, $interval, array $options = array())
 {
     $dataavailable = false;
     $started = time();
     $until = $started + $heartbeat;
     $this->_logger->info(sprintf('Waiting for changes for %s seconds', $heartbeat));
     // If pinging, make sure we have pingable collections. Note we can't
     // filter on them here because the collections might change during the
     // loop below.
     if (!empty($options['pingable']) && !$this->havePingableCollections()) {
         $this->_logger->err('No pingable collections.');
         return self::COLLECTION_ERR_SERVER;
     }
     // Need to update AND SAVE the timestamp for race conditions to be
     // detected.
     $this->lasthbsyncstarted = $started;
     $this->save();
     // We only check for remote wipe request once every 5 iterations to
     // save on DB load since we must reload the device's state each time.
     $rw_check_countdown = 5;
     while (($now = time()) < $until) {
         // Try not to go over the heartbeat interval.
         if ($until - $now < $interval) {
             $interval = $until - $now;
         }
         // See if another process has altered the sync_cache.
         if ($this->checkStaleRequest()) {
             return self::COLLECTION_ERR_STALE;
         }
         // Make sure the collections are still there (there might have been
         // an error in refreshing them from the cache). Ideally this should
         // NEVER happen.
         if (!count($this->_collections)) {
             $this->_logger->err('NO COLLECTIONS! This should not happen!');
             return self::COLLECTION_ERR_SERVER;
         }
         // Check for WIPE request once every 5 iterations to balance between
         // performance and speed of catching a remote wipe request.
         if ($rw_check_countdown-- == 0) {
             $rw_check_countdown = 5;
             if ($this->_as->provisioning != Horde_ActiveSync::PROVISIONING_NONE) {
                 $rwstatus = $this->_as->state->getDeviceRWStatus($this->_as->device->id, true);
                 if ($rwstatus == Horde_ActiveSync::RWSTATUS_PENDING || $rwstatus == Horde_ActiveSync::RWSTATUS_WIPED) {
                     return self::COLLECTION_ERR_FOLDERSYNC_REQUIRED;
                 }
             }
         }
         // Check each collection we are interested in.
         foreach ($this->_collections as $id => $collection) {
             // Initialize the collection's state data in the state handler.
             try {
                 $this->initCollectionState($collection, true);
             } catch (Horde_ActiveSync_Exception_StateGone $e) {
                 $this->_logger->notice(sprintf('[%s] State not found for %s. Continuing.', $this->_procid, $id));
                 if (!empty($options['pingable'])) {
                     return self::COLLECTION_ERR_PING_NEED_FULL;
                 }
                 $dataavailable = true;
                 $this->setGetChangesFlag($id);
                 continue;
             } catch (Horde_ActiveSync_Exception_InvalidRequest $e) {
                 // Thrown when state is unable to be initialized because the
                 // collection has not yet been synched, but was requested to
                 // be pinged.
                 $this->_logger->err(sprintf('[%s] Unable to initialize state for %s. Ignoring during pollForChanges: %s.', $this->_procid, $id, $e->getMessage()));
                 continue;
             } catch (Horde_ActiveSync_Exception_FolderGone $e) {
                 $this->_logger->warn('Folder gone for collection ' . $collection['id']);
                 return self::COLLECTION_ERR_FOLDERSYNC_REQUIRED;
             } catch (Horde_ActiveSync_Exception $e) {
                 $this->_logger->err('Error loading state: ' . $e->getMessage());
                 $this->_as->state->loadState(array(), null, Horde_ActiveSync::REQUEST_TYPE_SYNC, $id);
                 $this->setGetChangesFlag($id);
                 $dataavailable = true;
                 continue;
             }
             if (!empty($options['pingable']) && !$this->_cache->collectionIsPingable($id)) {
                 $this->_logger->notice(sprintf('[%s] Skipping %s because it is not PINGable.', $this->_procid, $id));
                 continue;
             }
             try {
                 if ($cnt = $this->getCollectionChangeCount(true)) {
                     $dataavailable = true;
                     $this->setGetChangesFlag($id);
                     if (!empty($options['pingable'])) {
                         $this->_cache->setPingChangeFlag($id);
                     }
                 } else {
                     try {
                         $this->_as->state->updateSyncStamp();
                     } catch (Horde_ActiveSync_Exception $e) {
                         $this->_logger->err($e->getMessage());
                     }
                 }
             } catch (Horde_ActiveSync_Exception_StaleState $e) {
                 $this->_logger->notice(sprintf('[%s] SYNC terminating and force-clearing device state: %s', $this->_procid, $e->getMessage()));
                 $this->_as->state->loadState(array(), null, Horde_ActiveSync::REQUEST_TYPE_SYNC, $id);
                 $this->setGetChangesFlag($id);
                 $dataavailable = true;
             } catch (Horde_ActiveSync_Exception_FolderGone $e) {
                 $this->_logger->notice(sprintf('[%s] SYNC terminating: %s', $this->_procid, $e->getMessage()));
                 // If we are missing a folder, we should clear the PING
                 // cache also, to be sure it picks up any hierarchy changes
                 // since most clients don't seem smart enough to figure this
                 // out on their own.
                 $this->resetPingCache();
                 return self::COLLECTION_ERR_FOLDERSYNC_REQUIRED;
             } catch (Horde_Exception_AuthenticationFailure $e) {
                 // We lost authentication for some reason.
                 $this->_logger->err(sprintf('[%s] Authentication lost during PING!!', $this->_procid));
                 return self::COLLECTION_ERR_AUTHENTICATION;
             } catch (Horde_ActiveSync_Exception $e) {
                 $this->_logger->err(sprintf('[%s] Sync object cannot be configured, throttling: %s', $this->_procid, $e->getMessage()));
                 $this->_sleep(30);
                 continue;
             }
         }
         if (!empty($dataavailable)) {
             $this->_logger->info(sprintf('[%s] Found changes!', $this->_procid));
             break;
         }
         // Wait a bit...
         $this->_sleep($interval);
         // Refresh the collections.
         $this->updateCollectionsFromCache();
     }
     // Check that no other Sync process already started
     // If so, we exit here and let the other process do the export.
     if ($this->checkStaleRequest()) {
         $this->_logger->info('Changes in cache determined during Sync Wait/Heartbeat, exiting here.');
         return self::COLLECTION_ERR_STALE;
     }
     $this->_logger->info(sprintf('[%s] Looping Sync complete: DataAvailable: %s, DataImported: %s', $this->_procid, $dataavailable, $this->importedChanges));
     return $dataavailable;
 }