/** * Returns a list of all known devices with users and when they synchronized for the first time * * @access public * @return array */ public function ListDevicesDetails() { $devices = ZPushAdmin::ListDevices(false); $output = array(); ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceUsers::ListLastSync(): found %d devices", count($devices))); ZPush::GetTopCollector()->AnnounceInformation(sprintf("Retrieved details of %d devices and getting users", count($devices)), true); foreach ($devices as $deviceId) { $output[$deviceId] = array(); $users = ZPushAdmin::ListUsers($deviceId); foreach ($users as $user) { $output[$deviceId][$user] = ZPushAdmin::GetDeviceDetails($deviceId, $user); } } return $output; }
/** * Returns a list of folders of the Request::GetGETUser(). * If the user has not enough permissions an empty result is returned. * * @access public * @return array */ public function ListUserFolders() { $user = Request::GetGETUser(); $output = array(); $hasRights = ZPush::GetBackend()->Setup($user); ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceInfo::ListUserFolders(): permissions to open store '%s': %s", $user, Utils::PrintAsString($hasRights))); if ($hasRights) { $folders = ZPush::GetBackend()->GetHierarchy(); ZPush::GetTopCollector()->AnnounceInformation(sprintf("Retrieved details of %d folders", count($folders)), true); foreach ($folders as $folder) { $folder->StripData(); unset($folder->Store, $folder->flags, $folder->content, $folder->NoBackendFolder); $output[] = $folder; } } return $output; }
/** * Constructor * * @access public */ public function DeviceManager() { $this->statemachine = ZPush::GetStateMachine(); $this->deviceHash = false; $this->devid = Request::GetDeviceID(); $this->windowSize = array(); $this->latestFolder = false; $this->hierarchySyncRequired = false; // only continue if deviceid is set if ($this->devid) { $this->device = new ASDevice($this->devid, Request::GetDeviceType(), Request::GetGETUser(), Request::GetUserAgent()); $this->loadDeviceData(); ZPush::GetTopCollector()->SetUserAgent($this->device->GetDeviceUserAgent()); } else { throw new FatalNotImplementedException("Can not proceed without a device id."); } $this->loopdetection = new LoopDetection(); $this->loopdetection->ProcessLoopDetectionInit(); $this->loopdetection->ProcessLoopDetectionPreviousConnectionFailed(); $this->stateManager = new StateManager(); $this->stateManager->SetDevice($this->device); }
if ($ex instanceof AuthenticationRequiredException) { ZPush::PrintZPushLegal($exclass, sprintf('<pre>%s</pre>', $ex->getMessage())); // log the failed login attemt e.g. for fail2ban if (defined('LOGAUTHFAIL') && LOGAUTHFAIL != false) { ZLog::Write(LOGLEVEL_WARN, sprintf("IP: %s failed to authenticate user '%s'", Request::GetRemoteAddr(), Request::GetAuthUser() ? Request::GetAuthUser() : Request::GetGETUser())); } } else { if ($ex instanceof WBXMLException) { ZLog::Write(LOGLEVEL_FATAL, "Request could not be processed correctly due to a WBXMLException. Please report this."); } else { if (!$ex instanceof ZPushException || $ex->showLegalNotice()) { $cmdinfo = Request::GetCommand() ? sprintf(" processing command <i>%s</i>", Request::GetCommand()) : ""; $extrace = $ex->getTrace(); $trace = !empty($extrace) ? "\n\nTrace:\n" . print_r($extrace, 1) : ""; ZPush::PrintZPushLegal($exclass . $cmdinfo, sprintf('<pre>%s</pre>', $ex->getMessage() . $trace)); } } } // Announce exception to process loop detection if (ZPush::GetDeviceManager(false)) { ZPush::GetDeviceManager()->AnnounceProcessException($ex); } // Announce exception if the TopCollector if available ZPush::GetTopCollector()->AnnounceInformation(get_class($ex), true); } // save device data if the DeviceManager is available if (ZPush::GetDeviceManager(false)) { ZPush::GetDeviceManager()->Save(); } // end gracefully ZLog::Write(LOGLEVEL_DEBUG, '-------- End');
/** * 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 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; }
/** * Constructor * * @access public */ public function ZPushTop() { $this->starttime = time(); $this->currenttime = time(); $this->action = ""; $this->filter = false; $this->status = false; $this->statusexpire = 0; $this->helpexpire = 0; $this->doingTail = false; $this->wide = false; $this->terminate = false; $this->showPush = true; $this->showOption = self::SHOW_DEFAULT; $this->showTermSec = self::SHOW_TERM_DEFAULT_TIME; $this->scrSize = array('width' => 80, 'height' => 24); $this->pingInterval = defined('PING_INTERVAL') && PING_INTERVAL > 0 ? PING_INTERVAL : 12; // get a TopCollector $this->topCollector = ZPush::GetTopCollector(); }
/** * Indicates if the next messages should be ignored (not be sent to the mobile!) * * @param string $messageid (opt) id of the message which is to be exported next * @param string $folderid (opt) parent id of the message * @param boolean $markAsIgnored (opt) to peek without setting the next message to be * ignored, set this value to false * @access public * @return boolean */ public function IgnoreNextMessage($markAsIgnored = true, $messageid = false, $folderid = false) { // as the next message id is not available at all point this method is called, we use different indicators. // potentialbroken indicates that we know that the broken message should be exported next, // alltho we do not know for sure as it's export message orders can change // if the $messageid is available and matches then we are sure and only then really ignore it $potentialBroken = false; $realBroken = false; if (Request::GetCommandCode() == ZPush::COMMAND_SYNC && $this->ignore_messageid !== false) { $potentialBroken = true; } if ($messageid !== false && $this->ignore_messageid == $messageid) { $realBroken = true; } // this call is just to know what should be happening // no further actions necessary if ($markAsIgnored === false) { return $potentialBroken; } // we should really do something here // first we check if we are in the loop mode, if so, // we update the potential broken id message so we loop count the same message $changedData = false; // exclusive block if ($this->blockMutex()) { $loopdata = $this->hasData() ? $this->getData() : array(); // check and initialize the array structure $this->checkArrayStructure($loopdata, $folderid); $current = $loopdata[self::$devid][self::$user][$folderid]; // we found our broken message! if ($realBroken) { $this->ignore_messageid = false; $current['ignored'] = $messageid; $changedData = true; // check if this message was broken before - here we know that it still is and remove it from the tracking $brokenkey = self::BROKENMSGS . "-" . $folderid; if (isset($loopdata[self::$devid][self::$user][$brokenkey]) && isset($loopdata[self::$devid][self::$user][$brokenkey][$messageid])) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): previously broken message '%s' is still broken and will not be tracked anymore", $messageid)); unset($loopdata[self::$devid][self::$user][$brokenkey][$messageid]); } } else { // update potential id if looping on an item if (isset($current['loopcount'])) { $current['potential'] = $messageid; // this message should be the broken one, but is not!! // we should reset the loop count because this is certainly not the broken one if ($potentialBroken) { $current['loopcount'] = 1; ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IgnoreNextMessage(): this should be the broken one, but is not! Resetting loop count."); } ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): Loop mode, potential broken message id '%s'", $current['potential'])); $changedData = true; } } // update loop data if ($changedData == true) { $loopdata[self::$devid][self::$user][$folderid] = $current; $ok = $this->setData($loopdata); } $this->releaseMutex(); } // end exclusive block if ($realBroken) { ZPush::GetTopCollector()->AnnounceInformation("Broken message ignored", true); } return $realBroken; }
/** * Marks a a device of the Request::GetGETUser() for resynchronization * * @param string $deviceId the device id * * @access public * @return boolean * @throws SoapFault */ public function ResyncDevice($deviceId) { $deviceId = preg_replace("/[^A-Za-z0-9]/", "", $deviceId); ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::ResyncDevice('%s'): mark device of user '%s' for resynchronization", $deviceId, Request::GetGETUser())); if (!ZPushAdmin::ResyncDevice(Request::GetGETUser(), $deviceId)) { ZPush::GetTopCollector()->AnnounceInformation(ZLog::GetLastMessage(LOGLEVEL_ERROR), true); throw new SoapFault("ERROR", ZLog::GetLastMessage(LOGLEVEL_ERROR)); } ZPush::GetTopCollector()->AnnounceInformation(sprintf("Resync requested - device id '%s'", $deviceId), true); return true; }
/** * Initialize the RequestProcessor * * @access public * @return */ public static function Initialize() { self::$backend = ZPush::GetBackend(); self::$deviceManager = ZPush::GetDeviceManager(); self::$topCollector = ZPush::GetTopCollector(); if (!ZPush::CommandNeedsPlainInput(Request::GetCommandCode())) { self::$decoder = new WBXMLDecoder(Request::GetInputStream()); } self::$encoder = new WBXMLEncoder(Request::GetOutputStream(), Request::GetGETAcceptMultipart()); }
/** * Announces that the current process is a push connection to the process loop * detection and to the Top collector * * @access public * @return boolean */ public function AnnounceProcessAsPush() { ZLog::Write(LOGLEVEL_DEBUG, "Announce process as PUSH connection"); return $this->loopdetection->ProcessLoopDetectionSetAsPush() && ZPush::GetTopCollector()->SetAsPushConnection(); }
/** * Removes an additional folder from the given device and the Request::GetGETUser(). * * @param string $deviceId device id of where the folder should be removed. * @param string $add_folderid the folder id of the additional folder. * * @access public * @return boolean */ public function AdditionalFolderRemove($deviceId, $add_folderid) { $user = Request::GetGETUser(); $deviceId = preg_replace("/[^A-Za-z0-9]/", "", $deviceId); $add_folderid = preg_replace("/[^A-Za-z0-9]/", "", $add_folderid); $status = ZPushAdmin::AdditionalFolderRemove($user, $deviceId, $add_folderid); if (!$status) { ZPush::GetTopCollector()->AnnounceInformation(ZLog::GetLastMessage(LOGLEVEL_ERROR), true); throw new SoapFault("ERROR", ZLog::GetLastMessage(LOGLEVEL_ERROR)); } ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::AdditionalFolderRemove(): removed folder for device '%s' of user '%s': %s", $deviceId, $user, Utils::PrintAsString($status))); ZPush::GetTopCollector()->AnnounceInformation("Removed additional folder", true); return $status; }
/** * Indicates if the next messages should be ignored (not be sent to the mobile!) * * @param string $messageid (opt) id of the message which is to be exported next * @param string $folderid (opt) parent id of the message * @param boolean $markAsIgnored (opt) to peek without setting the next message to be * ignored, set this value to false * @access public * @return boolean */ public function IgnoreNextMessage($markAsIgnored = true, $messageid = false, $folderid = false) { // as the next message id is not available at all point this method is called, we use different indicators. // potentialbroken indicates that we know that the broken message should be exported next, // alltho we do not know for sure as it's export message orders can change // if the $messageid is available and matches then we are sure and only then really ignore it $potentialBroken = false; $realBroken = false; if (Request::GetCommandCode() == ZPush::COMMAND_SYNC && $this->ignore_messageid !== false) { $potentialBroken = true; } if ($messageid !== false && $this->ignore_messageid == $messageid) { $realBroken = true; } // this call is just to know what should be happening // no further actions necessary if ($markAsIgnored === false) { return $potentialBroken; } // we should really do something here // first we check if we are in the loop mode, if so, // we update the potential broken id message so we loop count the same message // we found our broken message! if ($realBroken) { $this->ignore_messageid = false; self::$redis->hSet(self::$keyfolder . $folderid, 'ignored', $messageid); // check if this message was broken before - here we know that it still is and remove it from the tracking if ($this->RemoveBrokenMessage($folderid, $messageid)) { ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IgnoreNextMessage(): previously broken message '{$messageid}' is still broken and will not be tracked anymore (folder = {$folderid})"); } ZPush::GetTopCollector()->AnnounceInformation("Broken message ignored", true); } else { // update potential id if looping on an item if (self::$redis->hGet(self::$keyfolder . $folderid, 'loopcount') !== false) { // this message should be the broken one, but is not!! // we should reset the loop count because this is certainly not the broken one if ($potentialBroken) { self::$redis->hMset(self::$keyfolder . $folderid, array('potential' => $messageid, 'loopcount' => 1)); ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IgnoreNextMessage(): this should be the broken one, but is not! Resetting loop count."); } else { self::$redis->hSet(self::$keyfolder . $folderid, 'potential', $messageid); } ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): Loop mode, potential broken message id '%s'", $messageid)); } } return $realBroken; }