Пример #1
0
 /**
  * process the XML file and add, change, delete or fetches data
  *
  * @todo can we get rid of LIBXML_NOWARNING
  * @todo we need to stored the initial data for folders and lifetime as the phone is sending them only when they change
  * @return resource
  */
 public function handle()
 {
     $intervalStart = time();
     $status = self::STATUS_NO_CHANGES_FOUND;
     // the client does not send a wbxml document, if the Ping parameters did not change compared with the last request
     if ($this->_requestBody instanceof DOMDocument) {
         $xml = simplexml_import_dom($this->_requestBody);
         $xml->registerXPathNamespace('Ping', 'Ping');
         if (isset($xml->HeartbeatInterval)) {
             $this->_device->pinglifetime = (int) $xml->HeartbeatInterval;
         }
         if (isset($xml->Folders->Folder)) {
             $folders = array();
             foreach ($xml->Folders->Folder as $folderXml) {
                 try {
                     // does the folder exist?
                     $folder = $this->_folderBackend->getFolder($this->_device, (string) $folderXml->Id);
                     $folders[$folder->id] = $folder;
                 } catch (Syncroton_Exception_NotFound $senf) {
                     if ($this->_logger instanceof Syncroton_Log) {
                         $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $senf->getMessage());
                     }
                     $status = self::STATUS_FOLDER_NOT_FOUND;
                     break;
                 }
             }
             $this->_device->pingfolder = serialize(array_keys($folders));
         }
     }
     $this->_device->lastping = new DateTime('now', new DateTimeZone('utc'));
     if ($status == self::STATUS_NO_CHANGES_FOUND) {
         $this->_device = $this->_deviceBackend->update($this->_device);
     }
     $lifeTime = $this->_device->pinglifetime;
     $maxLifeTime = Syncroton_Registry::getMaxPingInterval();
     if ($maxLifeTime > 0 && $lifeTime > $maxLifeTime) {
         $ping = $this->_outputDom->documentElement;
         $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Status', self::STATUS_INTERVAL_TO_GREAT_OR_SMALL));
         $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'HeartbeatInterval', $maxLifeTime));
         return;
     }
     $intervalEnd = $intervalStart + $lifeTime;
     $secondsLeft = $intervalEnd;
     $folders = unserialize($this->_device->pingfolder);
     if ($status === self::STATUS_NO_CHANGES_FOUND && (!is_array($folders) || count($folders) == 0)) {
         $status = self::STATUS_MISSING_PARAMETERS;
     }
     if ($this->_logger instanceof Syncroton_Log) {
         $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " Folders to monitor({$lifeTime} / {$intervalStart} / {$intervalEnd} / {$status}): " . print_r($folders, true));
     }
     if ($status === self::STATUS_NO_CHANGES_FOUND) {
         do {
             // take a break to save battery lifetime
             sleep(Syncroton_Registry::getPingTimeout());
             try {
                 $device = $this->_deviceBackend->get($this->_device->id);
             } catch (Syncroton_Exception_NotFound $e) {
                 if ($this->_logger instanceof Syncroton_Log) {
                     $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
                 }
                 $status = self::STATUS_FOLDER_NOT_FOUND;
                 break;
             } catch (Exception $e) {
                 if ($this->_logger instanceof Syncroton_Log) {
                     $this->_logger->err(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
                 }
                 // do nothing, maybe temporal issue, should we stop?
                 continue;
             }
             // if another Ping command updated lastping property, we can stop processing this Ping command request
             if (isset($device->lastping) && $device->lastping instanceof DateTime && $device->pingfolder === $this->_device->pingfolder && $device->lastping->getTimestamp() > $this->_device->lastping->getTimestamp()) {
                 break;
             }
             $now = new DateTime('now', new DateTimeZone('utc'));
             foreach ($folders as $folderId) {
                 try {
                     $folder = $this->_folderBackend->get($folderId);
                     $dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp);
                 } catch (Syncroton_Exception_NotFound $e) {
                     if ($this->_logger instanceof Syncroton_Log) {
                         $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
                     }
                     $status = self::STATUS_FOLDER_NOT_FOUND;
                     break;
                 } catch (Exception $e) {
                     if ($this->_logger instanceof Syncroton_Log) {
                         $this->_logger->error(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
                     }
                     // do nothing, maybe temporal issue, should we stop?
                     continue;
                 }
                 try {
                     $syncState = $this->_syncStateBackend->getSyncState($this->_device, $folder);
                     // another process synchronized data of this folder already. let's skip it
                     if ($syncState->lastsync > $this->_syncTimeStamp) {
                         continue;
                     }
                     // safe battery time by skipping folders which got synchronied less than Syncroton_Registry::getQuietTime() seconds ago
                     if ($now->getTimestamp() - $syncState->lastsync->getTimestamp() < Syncroton_Registry::getQuietTime()) {
                         continue;
                     }
                     $foundChanges = $dataController->hasChanges($this->_contentStateBackend, $folder, $syncState);
                 } catch (Syncroton_Exception_NotFound $e) {
                     // folder got never synchronized to client
                     if ($this->_logger instanceof Syncroton_Log) {
                         $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
                     }
                     if ($this->_logger instanceof Syncroton_Log) {
                         $this->_logger->info(__METHOD__ . '::' . __LINE__ . ' syncstate not found. enforce sync for folder: ' . $folder->serverId);
                     }
                     $foundChanges = true;
                 }
                 if ($foundChanges == true) {
                     $this->_foldersWithChanges[] = $folder;
                     $status = self::STATUS_CHANGES_FOUND;
                 }
             }
             if ($status != self::STATUS_NO_CHANGES_FOUND) {
                 break;
             }
             $secondsLeft = $intervalEnd - time();
             if ($this->_logger instanceof Syncroton_Log) {
                 $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " seconds left: " . $secondsLeft);
             }
             // See: http://www.tine20.org/forum/viewtopic.php?f=12&t=12146
             //
             // break if there are less than PingTimeout + 10 seconds left for the next loop
             // otherwise the response will be returned after the client has finished his Ping
             // request already maybe
         } while ($secondsLeft > Syncroton_Registry::getPingTimeout() + 10);
     }
     if ($this->_logger instanceof Syncroton_Log) {
         $this->_logger->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " Lifetime: {$lifeTime} SecondsLeft: {$secondsLeft} Status: {$status})");
     }
     $ping = $this->_outputDom->documentElement;
     $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Status', $status));
     if ($status === self::STATUS_CHANGES_FOUND) {
         $folders = $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Folders'));
         foreach ($this->_foldersWithChanges as $changedFolder) {
             $folder = $folders->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Folder', $changedFolder->serverId));
             if ($this->_logger instanceof Syncroton_Log) {
                 $this->_logger->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " changes in folder: " . $changedFolder->serverId);
             }
         }
     }
 }