Example #1
0
 }
 ZPush::CheckAdvancedConfig();
 // Process request headers and look for AS headers
 Request::ProcessHeaders();
 // Check required GET parameters
 if (Request::IsMethodPOST() && (Request::GetCommandCode() === false || !Request::GetGETUser() || !Request::GetDeviceID() || !Request::GetDeviceType())) {
     throw new FatalException("Requested the Z-Push URL without the required GET parameters");
 }
 // Load the backend
 $backend = ZPush::GetBackend();
 // always request the authorization header
 if (!Request::AuthenticationInfo()) {
     throw new AuthenticationRequiredException("Access denied. Please send authorisation information");
 }
 // check the provisioning information
 if (PROVISIONING === true && Request::IsMethodPOST() && ZPush::CommandNeedsProvisioning(Request::GetCommandCode()) && (Request::WasPolicyKeySent() && Request::GetPolicyKey() == 0 || ZPush::GetDeviceManager()->ProvisioningRequired(Request::GetPolicyKey())) && (LOOSE_PROVISIONING === false || LOOSE_PROVISIONING === true && Request::WasPolicyKeySent())) {
     //TODO for AS 14 send a wbxml response
     throw new ProvisioningRequiredException();
 }
 // most commands require an authenticated user
 if (ZPush::CommandNeedsAuthentication(Request::GetCommandCode())) {
     RequestProcessor::Authenticate();
 }
 // Do the actual processing of the request
 if (Request::IsMethodGET()) {
     throw new NoPostRequestException("This is the Z-Push location and can only be accessed by Microsoft ActiveSync-capable devices", NoPostRequestException::GET_REQUEST);
 }
 // Do the actual request
 header(ZPush::GetServerHeader());
 // announce the supported AS versions (if not already sent to device)
 if (ZPush::GetDeviceManager()->AnnounceASVersion()) {
 /**
  * Checks if the currently known collections for changes for $lifetime seconds.
  * If the backend provides a ChangesSink the sink will be used.
  * If not every $interval seconds an exporter will be configured for each
  * folder to perform GetChangeCount().
  *
  * @param int       $lifetime       (opt) total lifetime to wait for changes / default 600s
  * @param int       $interval       (opt) time between blocking operations of sink or polling / default 30s
  * @param boolean   $onlyPingable   (opt) only check for folders which have the PingableFlag
  *
  * @access public
  * @return boolean              indicating if changes were found
  * @throws StatusException      with code SyncCollections::ERROR_NO_COLLECTIONS if no collections available
  *                              with code SyncCollections::ERROR_WRONG_HIERARCHY if there were errors getting changes
  */
 public function CheckForChanges($lifetime = 600, $interval = 30, $onlyPingable = false)
 {
     $classes = array();
     foreach ($this->collections as $folderid => $spa) {
         if ($onlyPingable && $spa->GetPingableFlag() !== true) {
             continue;
         }
         if (!isset($classes[$spa->GetContentClass()])) {
             $classes[$spa->GetContentClass()] = 0;
         }
         $classes[$spa->GetContentClass()] += 1;
     }
     if (empty($classes)) {
         $checkClasses = "policies only";
     } else {
         if (array_sum($classes) > 4) {
             $checkClasses = "";
             foreach ($classes as $class => $count) {
                 if ($count == 1) {
                     $checkClasses .= sprintf("%s ", $class);
                 } else {
                     $checkClasses .= sprintf("%s(%d) ", $class, $count);
                 }
             }
         } else {
             $checkClasses = implode(" ", array_keys($classes));
         }
     }
     $pingTracking = ZPush::GetPingTracking();
     $this->changes = array();
     $changesAvailable = false;
     ZPush::GetDeviceManager()->AnnounceProcessAsPush();
     ZPush::GetTopCollector()->AnnounceInformation(sprintf("lifetime %ds", $lifetime), true);
     ZLog::Write(LOGLEVEL_INFO, sprintf("SyncCollections->CheckForChanges(): Waiting for %s changes... (lifetime %d seconds)", empty($classes) ? 'policy' : 'store', $lifetime));
     // use changes sink where available
     $changesSink = false;
     $forceRealExport = 0;
     // do not create changessink if there are no folders
     if (!empty($classes) && ZPush::GetBackend()->HasChangesSink()) {
         $changesSink = true;
         // initialize all possible folders
         foreach ($this->collections as $folderid => $spa) {
             if ($onlyPingable && $spa->GetPingableFlag() !== true) {
                 continue;
             }
             // switch user store if this is a additional folder and initialize sink
             ZPush::GetBackend()->Setup(ZPush::GetAdditionalSyncFolderStore($folderid));
             if (!ZPush::GetBackend()->ChangesSinkInitialize($folderid)) {
                 throw new StatusException(sprintf("Error initializing ChangesSink for folder id '%s'", $folderid), self::ERROR_WRONG_HIERARCHY);
             }
         }
     }
     // wait for changes
     $started = time();
     $endat = time() + $lifetime;
     // always use policy key from the request if it was sent
     $policyKey = $this->GetReferencePolicyKey();
     if (Request::WasPolicyKeySent() && Request::GetPolicyKey() != 0) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("refpolkey:'%s', sent polkey:'%s'", $policyKey, Request::GetPolicyKey()));
         $policyKey = Request::GetPolicyKey();
     }
     while (($now = time()) < $endat) {
         // how long are we waiting for changes
         $this->waitingTime = $now - $started;
         $nextInterval = $interval;
         // we should not block longer than the lifetime
         if ($endat - $now < $nextInterval) {
             $nextInterval = $endat - $now;
         }
         // Check if provisioning is necessary
         // if a PolicyKey was sent use it. If not, compare with the ReferencePolicyKey
         if (PROVISIONING === true && $policyKey !== false && ZPush::GetDeviceManager()->ProvisioningRequired($policyKey, true)) {
             // the hierarchysync forces provisioning
             throw new StatusException("SyncCollections->CheckForChanges(): PolicyKey changed. Provisioning required.", self::ERROR_WRONG_HIERARCHY);
         }
         // Check if a hierarchy sync is necessary
         if (ZPush::GetDeviceManager()->IsHierarchySyncRequired()) {
             throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
         }
         // Check if there are newer requests
         // If so, this process should be terminated if more than 60 secs to go
         if ($pingTracking->DoForcePingTimeout()) {
             // do not update CPOs because another process has already read them!
             $this->saveData = false;
             // more than 60 secs to go?
             if ($now + 60 < $endat) {
                 ZPush::GetTopCollector()->AnnounceInformation(sprintf("Forced timeout after %ds", $now - $started), true);
                 throw new StatusException(sprintf("SyncCollections->CheckForChanges(): Timeout forced after %ss from %ss due to other process", $now - $started, $lifetime), self::OBSOLETE_CONNECTION);
             }
         }
         // Use changes sink if available
         if ($changesSink) {
             // in some occasions we do realize a full export to see if there are pending changes
             // every 5 minutes this is also done to see if there were "missed" notifications
             if (SINK_FORCERECHECK !== false && $forceRealExport + SINK_FORCERECHECK <= $now) {
                 if ($this->CountChanges($onlyPingable)) {
                     ZLog::Write(LOGLEVEL_DEBUG, "SyncCollections->CheckForChanges(): Using ChangesSink but found relevant changes on regular export");
                     return true;
                 }
                 $forceRealExport = $now;
             }
             ZPush::GetTopCollector()->AnnounceInformation(sprintf("Sink %d/%ds on %s", $now - $started, $lifetime, $checkClasses));
             $notifications = ZPush::GetBackend()->ChangesSink($nextInterval);
             $validNotifications = false;
             foreach ($notifications as $folderid) {
                 // check if the notification on the folder is within our filter
                 if ($this->CountChange($folderid)) {
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s'", $folderid));
                     $validNotifications = true;
                     $this->waitingTime = time() - $started;
                 } else {
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s', but it is not relevant", $folderid));
                 }
             }
             if ($validNotifications) {
                 return true;
             }
         } else {
             ZPush::GetTopCollector()->AnnounceInformation(sprintf("Polling %d/%ds on %s", $now - $started, $lifetime, $checkClasses));
             if ($this->CountChanges($onlyPingable)) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Found changes polling"));
                 return true;
             } else {
                 sleep($nextInterval);
             }
         }
         // end polling
     }
     // end wait for changes
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): no changes found after %ds", time() - $started));
     return false;
 }
Example #3
0
 /**
  * Checks if the currently known collections for changes for $lifetime seconds.
  * If the backend provides a ChangesSink the sink will be used.
  * If not every $interval seconds an exporter will be configured for each
  * folder to perform GetChangeCount().
  *
  * @param int       $lifetime       (opt) total lifetime to wait for changes / default 600s
  * @param int       $interval       (opt) time between blocking operations of sink or polling / default 30s
  * @param boolean   $onlyPingable   (opt) only check for folders which have the PingableFlag
  *
  * @access public
  * @return boolean              indicating if changes were found
  * @throws StatusException      with code SyncCollections::ERROR_NO_COLLECTIONS if no collections available
  *                              with code SyncCollections::ERROR_WRONG_HIERARCHY if there were errors getting changes
  */
 public function CheckForChanges($lifetime = 600, $interval = 30, $onlyPingable = false)
 {
     $classes = array();
     foreach ($this->collections as $folderid => $spa) {
         if ($onlyPingable && $spa->GetPingableFlag() !== true || !$folderid) {
             continue;
         }
         // the class name will be overwritten for KOE-GAB
         $class = $this->getPingClass($spa);
         if (!isset($classes[$class])) {
             $classes[$class] = 0;
         }
         $classes[$class] += 1;
     }
     if (empty($classes)) {
         $checkClasses = "policies only";
     } else {
         if (array_sum($classes) > 4) {
             $checkClasses = "";
             foreach ($classes as $class => $count) {
                 if ($count == 1) {
                     $checkClasses .= sprintf("%s ", $class);
                 } else {
                     $checkClasses .= sprintf("%s(%d) ", $class, $count);
                 }
             }
         } else {
             $checkClasses = implode(" ", array_keys($classes));
         }
     }
     $pingTracking = new PingTracking();
     $this->changes = array();
     ZPush::GetDeviceManager()->AnnounceProcessAsPush();
     ZPush::GetTopCollector()->AnnounceInformation(sprintf("lifetime %ds", $lifetime), true);
     ZLog::Write(LOGLEVEL_INFO, sprintf("SyncCollections->CheckForChanges(): Waiting for %s changes... (lifetime %d seconds)", empty($classes) ? 'policy' : 'store', $lifetime));
     // use changes sink where available
     $changesSink = ZPush::GetBackend()->HasChangesSink();
     // create changessink and check folder stats if there are folders to Ping
     if (!empty($classes)) {
         // initialize all possible folders
         foreach ($this->collections as $folderid => $spa) {
             if ($onlyPingable && $spa->GetPingableFlag() !== true || !$folderid) {
                 continue;
             }
             $backendFolderId = $spa->GetBackendFolderId();
             // get the user store if this is a additional folder
             $store = ZPush::GetAdditionalSyncFolderStore($backendFolderId);
             // initialize sink if no immediate changes were found so far
             if ($changesSink && empty($this->changes)) {
                 ZPush::GetBackend()->Setup($store);
                 if (!ZPush::GetBackend()->ChangesSinkInitialize($backendFolderId)) {
                     throw new StatusException(sprintf("Error initializing ChangesSink for folder id %s/%s", $folderid, $backendFolderId), self::ERROR_WRONG_HIERARCHY);
                 }
             }
             // check if the folder stat changed since the last sync, if so generate a change for it (only on first run)
             $currentFolderStat = ZPush::GetBackend()->GetFolderStat($store, $backendFolderId);
             if ($this->waitingTime == 0 && ZPush::GetBackend()->HasFolderStats() && $currentFolderStat !== false && $spa->IsExporterRunRequired($currentFolderStat, true)) {
                 $this->changes[$spa->GetFolderId()] = 1;
             }
         }
     }
     if (!empty($this->changes)) {
         ZLog::Write(LOGLEVEL_DEBUG, "SyncCollections->CheckForChanges(): Using ChangesSink but found changes verifying the folder stats");
         return true;
     }
     // wait for changes
     $started = time();
     $endat = time() + $lifetime;
     // always use policy key from the request if it was sent
     $policyKey = $this->GetReferencePolicyKey();
     if (Request::WasPolicyKeySent() && Request::GetPolicyKey() != 0) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("refpolkey:'%s', sent polkey:'%s'", $policyKey, Request::GetPolicyKey()));
         $policyKey = Request::GetPolicyKey();
     }
     while (($now = time()) < $endat) {
         // how long are we waiting for changes
         $this->waitingTime = $now - $started;
         $nextInterval = $interval;
         // we should not block longer than the lifetime
         if ($endat - $now < $nextInterval) {
             $nextInterval = $endat - $now;
         }
         // Check if provisioning is necessary
         // if a PolicyKey was sent use it. If not, compare with the ReferencePolicyKey
         if (PROVISIONING === true && $policyKey !== false && ZPush::GetDeviceManager()->ProvisioningRequired($policyKey, true, false)) {
             // the hierarchysync forces provisioning
             throw new StatusException("SyncCollections->CheckForChanges(): Policies or PolicyKey changed. Provisioning required.", self::ERROR_WRONG_HIERARCHY);
         }
         // Check if a hierarchy sync is necessary
         if ($this->countHierarchyChange()) {
             throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
         }
         // Check if there are newer requests
         // If so, this process should be terminated if more than 60 secs to go
         if ($pingTracking->DoForcePingTimeout()) {
             // do not update CPOs because another process has already read them!
             $this->saveData = false;
             // more than 60 secs to go?
             if ($now + 60 < $endat) {
                 ZPush::GetTopCollector()->AnnounceInformation(sprintf("Forced timeout after %ds", $now - $started), true);
                 throw new StatusException(sprintf("SyncCollections->CheckForChanges(): Timeout forced after %ss from %ss due to other process", $now - $started, $lifetime), self::OBSOLETE_CONNECTION);
             }
         }
         // Use changes sink if available
         if ($changesSink) {
             ZPush::GetTopCollector()->AnnounceInformation(sprintf("Sink %d/%ds on %s", $now - $started, $lifetime, $checkClasses));
             $notifications = ZPush::GetBackend()->ChangesSink($nextInterval);
             $validNotifications = false;
             foreach ($notifications as $backendFolderId) {
                 // Check hierarchy notifications
                 if ($backendFolderId === IBackend::HIERARCHYNOTIFICATION) {
                     // wait two seconds before validating this notification, because it could potentially be made by the mobile and we need some time to update the states.
                     sleep(2);
                     // check received hierarchy notifications by exporting
                     if ($this->countHierarchyChange(true)) {
                         throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
                     }
                 } else {
                     // the backend will notify on the backend folderid
                     $folderid = ZPush::GetDeviceManager()->GetFolderIdForBackendId($backendFolderId);
                     // check if the notification on the folder is within our filter
                     if ($this->CountChange($folderid)) {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s'", $folderid));
                         $validNotifications = true;
                         $this->waitingTime = time() - $started;
                     } else {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s', but it is not relevant", $folderid));
                     }
                 }
             }
             if ($validNotifications) {
                 return true;
             }
         } else {
             ZPush::GetTopCollector()->AnnounceInformation(sprintf("Polling %d/%ds on %s", $now - $started, $lifetime, $checkClasses));
             if ($this->CountChanges($onlyPingable)) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Found changes polling"));
                 return true;
             } else {
                 sleep($nextInterval);
             }
         }
         // end polling
     }
     // end wait for changes
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): no changes found after %ds", time() - $started));
     return false;
 }
 /**
  * Checks if the sent policykey matches the latest policykey
  * saved for the device
  *
  * @param string        $policykey
  * @param boolean       $noDebug        (opt) by default, debug message is shown
  *
  * @access public
  * @return boolean
  */
 public function ProvisioningRequired($policykey, $noDebug = false)
 {
     $this->loadDeviceData();
     // check if a remote wipe is required
     if ($this->device->GetWipeStatus() > SYNC_PROVISION_RWSTATUS_OK) {
         ZLog::Write(LOGLEVEL_INFO, sprintf("DeviceManager->ProvisioningRequired('%s'): YES, remote wipe requested", $policykey));
         return true;
     }
     $p = $this->device->GetWipeStatus() != SYNC_PROVISION_RWSTATUS_NA && $policykey != $this->device->GetPolicyKey() || Request::WasPolicyKeySent() && $this->device->GetPolicyKey() == ASDevice::UNDEFINED;
     if (!$noDebug || $p) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->ProvisioningRequired('%s') saved device key '%s': %s", $policykey, $this->device->GetPolicyKey(), Utils::PrintAsString($p)));
     }
     return $p;
 }