/**
  * Handles the GetItemEstimate command
  * Returns an estimation of how many items will be synchronized at the next sync
  * This is mostly used to show something in the progress bar
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     $sc = new SyncCollections();
     if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE)) {
         return false;
     }
     if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERS)) {
         return false;
     }
     while (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDER)) {
         $spa = new SyncParameters();
         $spastatus = false;
         if (Request::GetProtocolVersion() >= 14.0) {
             if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
                 try {
                     $spa->SetSyncKey(self::$decoder->getElementContent());
                 } catch (StateInvalidException $siex) {
                     $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED;
                 }
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) {
                 $spa->SetFolderId(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             // conversation mode requested
             if (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
                 $spa->SetConversationMode(true);
                 if (($conversationmode = self::$decoder->getElementContent()) !== false) {
                     $spa->SetConversationMode((bool) $conversationmode);
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
                 while (1) {
                     if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
                         $spa->SetFilterType(self::$decoder->getElementContent());
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                         $spa->SetContentClass(self::$decoder->getElementContent());
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     if (self::$decoder->getElementStartTag(SYNC_MAXITEMS)) {
                         $spa->SetWindowSize($maxitems = self::$decoder->getElementContent());
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     $e = self::$decoder->peek();
                     if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
                         self::$decoder->getElementEndTag();
                         break;
                     }
                 }
             }
         } else {
             //get items estimate does not necessarily send the folder type
             if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE)) {
                 $spa->SetContentClass(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) {
                 $spa->SetFolderId(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if (!self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
                 return false;
             }
             $spa->SetFilterType(self::$decoder->getElementContent());
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
             if (!self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
                 return false;
             }
             try {
                 $spa->SetSyncKey(self::$decoder->getElementContent());
             } catch (StateInvalidException $siex) {
                 $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED;
             }
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
         //SYNC_GETITEMESTIMATE_FOLDER
         // Process folder data
         //In AS 14 request only collectionid is sent, without class
         if (!$spa->HasContentClass() && $spa->HasFolderId()) {
             try {
                 $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
             } catch (NoHierarchyCacheAvailableException $nhca) {
                 $spastatus = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
             }
         }
         // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
         if (!$spa->HasFolderId() && $spa->HasContentClass()) {
             $spa->SetFolderId(self::$deviceManager->GetFolderIdFromCacheByClass($spa->GetContentClass()));
         }
         // Add collection to SC and load state
         $sc->AddCollection($spa);
         if ($spastatus) {
             // the CPO has a folder id now, so we can set the status
             $sc->AddParameter($spa, "status", $spastatus);
         } else {
             try {
                 $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey()));
                 // if this is an additional folder the backend has to be setup correctly
                 if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) {
                     throw new StatusException(sprintf("HandleGetItemEstimate() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 }
             } catch (StateNotFoundException $snfex) {
                 // ok, the key is invalid. Question is, if the hierarchycache is still ok
                 //if not, we have to issue SYNC_GETITEMESTSTATUS_COLLECTIONINVALID which triggers a FolderSync
                 try {
                     self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId());
                     // we got here, so the HierarchyCache is ok
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCKKEYINVALID);
                 } catch (NoHierarchyCacheAvailableException $nhca) {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 }
                 self::$topCollector->AnnounceInformation("StateNotFoundException " . $sc->GetParameter($spa, "status"), true);
             } catch (StatusException $stex) {
                 if ($stex->getCode() == SYNC_GETITEMESTSTATUS_COLLECTIONINVALID) {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 } else {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED);
                 }
                 self::$topCollector->AnnounceInformation("StatusException " . $sc->GetParameter($spa, "status"), true);
             }
         }
     }
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     //SYNC_GETITEMESTIMATE_FOLDERS
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     //SYNC_GETITEMESTIMATE_GETITEMESTIMATE
     self::$encoder->startWBXML();
     self::$encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE);
     $status = SYNC_GETITEMESTSTATUS_SUCCESS;
     // look for changes in all collections
     try {
         $sc->CountChanges();
     } catch (StatusException $ste) {
         $status = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
     }
     $changes = $sc->GetChangedFolderIds();
     foreach ($sc as $folderid => $spa) {
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE);
         if ($sc->GetParameter($spa, "status")) {
             $status = $sc->GetParameter($spa, "status");
         }
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER);
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE);
         self::$encoder->content($spa->GetContentClass());
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID);
         self::$encoder->content($spa->GetFolderId());
         self::$encoder->endTag();
         if (isset($changes[$folderid]) && $changes[$folderid] !== false) {
             self::$encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE);
             self::$encoder->content($changes[$folderid]);
             self::$encoder->endTag();
             if ($changes[$folderid] > 0) {
                 self::$topCollector->AnnounceInformation(sprintf("%s %d changes", $spa->GetContentClass(), $changes[$folderid]), true);
             }
         }
         self::$encoder->endTag();
         self::$encoder->endTag();
     }
     if (array_sum($changes) == 0) {
         self::$topCollector->AnnounceInformation("No changes found", true);
     }
     self::$encoder->endTag();
     return true;
 }
Пример #2
0
 /**
  * Handles the Ping command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     $interval = defined('PING_INTERVAL') && PING_INTERVAL > 0 ? PING_INTERVAL : 30;
     $pingstatus = false;
     $fakechanges = array();
     $foundchanges = false;
     // Contains all requested folders (containers)
     $sc = new SyncCollections();
     // Load all collections - do load states and check permissions
     try {
         $sc->LoadAllCollections(true, true, true);
     } catch (StateNotFoundException $snfex) {
         $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED;
         self::$topCollector->AnnounceInformation("StateNotFoundException: require HierarchySync", true);
     } catch (StateInvalidException $snfex) {
         // we do not have a ping status for this, but SyncCollections should have generated fake changes for the folders which are broken
         $fakechanges = $sc->GetChangedFolderIds();
         $foundchanges = true;
         self::$topCollector->AnnounceInformation("StateInvalidException: force sync", true);
     } catch (StatusException $stex) {
         $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED;
         self::$topCollector->AnnounceInformation("StatusException: require HierarchySync", true);
     }
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandlePing(): reference PolicyKey for PING: %s", $sc->GetReferencePolicyKey()));
     // receive PING initialization data
     if (self::$decoder->getElementStartTag(SYNC_PING_PING)) {
         self::$topCollector->AnnounceInformation("Processing PING data");
         ZLog::Write(LOGLEVEL_DEBUG, "HandlePing(): initialization data received");
         if (self::$decoder->getElementStartTag(SYNC_PING_LIFETIME)) {
             $sc->SetLifetime(self::$decoder->getElementContent());
             self::$decoder->getElementEndTag();
         }
         if (($el = self::$decoder->getElementStartTag(SYNC_PING_FOLDERS)) && $el[EN_FLAGS] & EN_FLAGS_CONTENT) {
             // remove PingableFlag from all collections
             foreach ($sc as $folderid => $spa) {
                 $spa->DelPingableFlag();
             }
             while (self::$decoder->getElementStartTag(SYNC_PING_FOLDER)) {
                 while (1) {
                     if (self::$decoder->getElementStartTag(SYNC_PING_SERVERENTRYID)) {
                         $folderid = self::$decoder->getElementContent();
                         self::$decoder->getElementEndTag();
                     }
                     if (self::$decoder->getElementStartTag(SYNC_PING_FOLDERTYPE)) {
                         $class = self::$decoder->getElementContent();
                         self::$decoder->getElementEndTag();
                     }
                     $e = self::$decoder->peek();
                     if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
                         self::$decoder->getElementEndTag();
                         break;
                     }
                 }
                 $spa = $sc->GetCollection($folderid);
                 if (!$spa) {
                     // The requested collection is not synchronized.
                     // check if the HierarchyCache is available, if not, trigger a HierarchySync
                     try {
                         self::$deviceManager->GetFolderClassFromCacheByID($folderid);
                     } catch (NoHierarchyCacheAvailableException $nhca) {
                         ZLog::Write(LOGLEVEL_INFO, sprintf("HandlePing(): unknown collection '%s', triggering HierarchySync", $folderid));
                         $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED;
                     }
                     // Trigger a Sync request because then the device will be forced to resync this folder.
                     $fakechanges[$folderid] = 1;
                     $foundchanges = true;
                 } else {
                     if ($class == $spa->GetContentClass()) {
                         $spa->SetPingableFlag(true);
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandlePing(): using saved sync state for '%s' id '%s'", $spa->GetContentClass(), $folderid));
                     }
                 }
             }
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
         // save changed data
         foreach ($sc as $folderid => $spa) {
             $sc->SaveCollection($spa);
         }
     } else {
         // if no ping initialization data was sent, we check if we have pingable folders
         // if not, we indicate that there is nothing to do.
         if (!$sc->PingableFolders()) {
             $pingstatus = SYNC_PINGSTATUS_FAILINGPARAMS;
             ZLog::Write(LOGLEVEL_DEBUG, "HandlePing(): no pingable folders found and no initialization data sent. Returning SYNC_PINGSTATUS_FAILINGPARAMS.");
         }
     }
     // Check for changes on the default LifeTime, set interval and ONLY on pingable collections
     try {
         if (!$pingstatus && empty($fakechanges)) {
             $foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval, true);
         }
     } catch (StatusException $ste) {
         switch ($ste->getCode()) {
             case SyncCollections::ERROR_NO_COLLECTIONS:
                 $pingstatus = SYNC_PINGSTATUS_FAILINGPARAMS;
                 break;
             case SyncCollections::ERROR_WRONG_HIERARCHY:
                 $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED;
                 self::$deviceManager->AnnounceProcessStatus(false, $pingstatus);
                 break;
             case SyncCollections::OBSOLETE_CONNECTION:
                 $foundchanges = false;
                 break;
         }
     }
     self::$encoder->StartWBXML();
     self::$encoder->startTag(SYNC_PING_PING);
     self::$encoder->startTag(SYNC_PING_STATUS);
     if (isset($pingstatus) && $pingstatus) {
         self::$encoder->content($pingstatus);
     } else {
         self::$encoder->content($foundchanges ? SYNC_PINGSTATUS_CHANGES : SYNC_PINGSTATUS_HBEXPIRED);
     }
     self::$encoder->endTag();
     if (!$pingstatus) {
         self::$encoder->startTag(SYNC_PING_FOLDERS);
         if (empty($fakechanges)) {
             $changes = $sc->GetChangedFolderIds();
         } else {
             $changes = $fakechanges;
         }
         foreach ($changes as $folderid => $changecount) {
             if ($changecount > 0) {
                 self::$encoder->startTag(SYNC_PING_FOLDER);
                 self::$encoder->content($folderid);
                 self::$encoder->endTag();
                 if (empty($fakechanges)) {
                     self::$topCollector->AnnounceInformation(sprintf("Found change in %s", $sc->GetCollection($folderid)->GetContentClass()), true);
                 }
             }
         }
         self::$encoder->endTag();
     }
     self::$encoder->endTag();
     return true;
 }
Пример #3
0
 /**
  * Handles the GetItemEstimate command
  * Returns an estimation of how many items will be synchronized at the next sync
  * This is mostly used to show something in the progress bar
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     $sc = new SyncCollections();
     if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE)) {
         return false;
     }
     if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERS)) {
         return false;
     }
     while (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDER)) {
         $spa = new SyncParameters();
         $spastatus = false;
         // read the folder properties
         WBXMLDecoder::ResetInWhile("getItemEstimateFolders");
         while (WBXMLDecoder::InWhile("getItemEstimateFolders")) {
             if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
                 try {
                     $spa->SetSyncKey(self::$decoder->getElementContent());
                 } catch (StateInvalidException $siex) {
                     $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED;
                 }
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             } elseif (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) {
                 $fid = self::$decoder->getElementContent();
                 $spa->SetFolderId($fid);
                 $spa->SetBackendFolderId(self::$deviceManager->GetBackendIdForFolderId($fid));
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             } elseif (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
                 $spa->SetConversationMode(true);
                 if (($conversationmode = self::$decoder->getElementContent()) !== false) {
                     $spa->SetConversationMode((bool) $conversationmode);
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
             } elseif (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE)) {
                 $spa->SetContentClass(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             } elseif (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
                 $spa->SetFilterType(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             while (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
                 WBXMLDecoder::ResetInWhile("getItemEstimateOptions");
                 while (WBXMLDecoder::InWhile("getItemEstimateOptions")) {
                     $firstOption = true;
                     // foldertype definition
                     if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                         $foldertype = self::$decoder->getElementContent();
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleGetItemEstimate(): specified options block with foldertype '%s'", $foldertype));
                         // switch the foldertype for the next options
                         $spa->UseCPO($foldertype);
                         // set to synchronize all changes. The mobile could overwrite this value
                         $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     } else {
                         if ($firstOption) {
                             $spa->UseCPO();
                             // set to synchronize all changes. The mobile could overwrite this value
                             $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
                         }
                     }
                     $firstOption = false;
                     if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
                         $spa->SetFilterType(self::$decoder->getElementContent());
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     if (self::$decoder->getElementStartTag(SYNC_MAXITEMS)) {
                         $spa->SetWindowSize($maxitems = self::$decoder->getElementContent());
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     $e = self::$decoder->peek();
                     if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
                         self::$decoder->getElementEndTag();
                         break;
                     }
                 }
             }
             $e = self::$decoder->peek();
             if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
                 self::$decoder->getElementEndTag();
                 //SYNC_GETITEMESTIMATE_FOLDER
                 break;
             }
         }
         // Process folder data
         //In AS 14 request only collectionid is sent, without class
         if (!$spa->HasContentClass() && $spa->HasFolderId()) {
             try {
                 $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
             } catch (NoHierarchyCacheAvailableException $nhca) {
                 $spastatus = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
             }
         }
         // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
         if (!$spa->HasFolderId() && $spa->HasContentClass()) {
             $spa->SetFolderId(self::$deviceManager->GetFolderIdFromCacheByClass($spa->GetContentClass()));
         }
         // Add collection to SC and load state
         $sc->AddCollection($spa);
         if ($spastatus) {
             // the CPO has a folder id now, so we can set the status
             $sc->AddParameter($spa, "status", $spastatus);
         } else {
             try {
                 $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey()));
                 // if this is an additional folder the backend has to be setup correctly
                 if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
                     throw new StatusException(sprintf("HandleGetItemEstimate() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 }
             } catch (StateNotFoundException $snfex) {
                 // ok, the key is invalid. Question is, if the hierarchycache is still ok
                 //if not, we have to issue SYNC_GETITEMESTSTATUS_COLLECTIONINVALID which triggers a FolderSync
                 try {
                     self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId());
                     // we got here, so the HierarchyCache is ok
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCKKEYINVALID);
                 } catch (NoHierarchyCacheAvailableException $nhca) {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 }
                 self::$topCollector->AnnounceInformation("StateNotFoundException " . $sc->GetParameter($spa, "status"), true);
             } catch (StatusException $stex) {
                 if ($stex->getCode() == SYNC_GETITEMESTSTATUS_COLLECTIONINVALID) {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 } else {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED);
                 }
                 self::$topCollector->AnnounceInformation("StatusException " . $sc->GetParameter($spa, "status"), true);
             }
         }
     }
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     //SYNC_GETITEMESTIMATE_FOLDERS
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     //SYNC_GETITEMESTIMATE_GETITEMESTIMATE
     self::$encoder->startWBXML();
     self::$encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE);
     $status = SYNC_GETITEMESTSTATUS_SUCCESS;
     // look for changes in all collections
     try {
         $sc->CountChanges();
     } catch (StatusException $ste) {
         $status = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
     }
     $changes = $sc->GetChangedFolderIds();
     foreach ($sc as $folderid => $spa) {
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE);
         if ($sc->GetParameter($spa, "status")) {
             $status = $sc->GetParameter($spa, "status");
         }
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER);
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE);
         self::$encoder->content($spa->GetContentClass());
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID);
         self::$encoder->content($spa->GetFolderId());
         self::$encoder->endTag();
         if (isset($changes[$folderid]) && $changes[$folderid] !== false) {
             self::$encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE);
             self::$encoder->content($changes[$folderid]);
             self::$encoder->endTag();
             if ($changes[$folderid] > 0) {
                 self::$topCollector->AnnounceInformation(sprintf("%s %d changes", $spa->GetContentClass(), $changes[$folderid]), true);
             }
             // update the device data to mark folders as complete when synching with WM
             if ($changes[$folderid] == 0) {
                 self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_COMPLETED);
             }
         }
         self::$encoder->endTag();
         self::$encoder->endTag();
     }
     if (array_sum($changes) == 0) {
         self::$topCollector->AnnounceInformation("No changes found", true);
     }
     self::$encoder->endTag();
     return true;
 }