function HandleSync($backend, $protocolversion, $devid) { global $zpushdtd; global $input, $output; // Contains all containers requested $collections = array(); // Init WBXML decoder $decoder = new WBXMLDecoder($input, $zpushdtd); // Init state machine $statemachine = new StateMachine(); // Start decode if (!$decoder->getElementStartTag(SYNC_SYNCHRONIZE)) { return false; } if (!$decoder->getElementStartTag(SYNC_FOLDERS)) { return false; } while ($decoder->getElementStartTag(SYNC_FOLDER)) { $collection = array(); $collection["truncation"] = SYNC_TRUNCATION_ALL; $collection["clientids"] = array(); $collection["fetchids"] = array(); if (!$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { return false; } $collection["class"] = $decoder->getElementContent(); debugLog("Sync folder:{$collection["class"]}"); if (!$decoder->getElementEndTag()) { return false; } if (!$decoder->getElementStartTag(SYNC_SYNCKEY)) { return false; } $collection["synckey"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } if ($decoder->getElementStartTag(SYNC_FOLDERID)) { $collection["collectionid"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } } if ($decoder->getElementStartTag(SYNC_SUPPORTED)) { while (1) { $el = $decoder->getElement(); if ($el[EN_TYPE] == EN_TYPE_ENDTAG) { break; } } } if ($decoder->getElementStartTag(SYNC_DELETESASMOVES)) { $collection["deletesasmoves"] = true; } if ($decoder->getElementStartTag(SYNC_GETCHANGES)) { $collection["getchanges"] = true; } if ($decoder->getElementStartTag(SYNC_MAXITEMS)) { $collection["maxitems"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } } if ($decoder->getElementStartTag(SYNC_OPTIONS)) { while (1) { if ($decoder->getElementStartTag(SYNC_FILTERTYPE)) { $collection["filtertype"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } } if ($decoder->getElementStartTag(SYNC_TRUNCATION)) { $collection["truncation"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } } if ($decoder->getElementStartTag(SYNC_RTFTRUNCATION)) { $collection["rtftruncation"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } } if ($decoder->getElementStartTag(SYNC_MIMESUPPORT)) { $collection["mimesupport"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } } if ($decoder->getElementStartTag(SYNC_MIMETRUNCATION)) { $collection["mimetruncation"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } } if ($decoder->getElementStartTag(SYNC_CONFLICT)) { $collection["conflict"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } } $e = $decoder->peek(); if ($e[EN_TYPE] == EN_TYPE_ENDTAG) { $decoder->getElementEndTag(); break; } } } // compatibility mode - get folderid from the state directory if (!isset($collection["collectionid"])) { $collection["collectionid"] = _getFolderID($devid, $collection["class"]); } // compatibility mode - set default conflict behavior if no conflict resolution algorithm is set (OVERWRITE_PIM) if (!isset($collection["conflict"])) { $collection["conflict"] = 1; } //compatibility mode - set maxitems if the client doesn't send it as it breaks some devices if (!isset($collection["maxitems"])) { $collection["maxitems"] = 100; } // Get our sync state for this collection $collection["syncstate"] = $statemachine->getSyncState($collection["synckey"]); if ($decoder->getElementStartTag(SYNC_PERFORM)) { // Configure importer with last state $importer = $backend->GetContentsImporter($collection["collectionid"]); $importer->Config($collection["syncstate"], $collection["conflict"]); $nchanges = 0; while (1) { $element = $decoder->getElement(); // MODIFY or REMOVE or ADD or FETCH if ($element[EN_TYPE] != EN_TYPE_STARTTAG) { $decoder->ungetElement($element); break; } $nchanges++; if ($decoder->getElementStartTag(SYNC_SERVERENTRYID)) { $serverid = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { // end serverid return false; } } else { $serverid = false; } if ($decoder->getElementStartTag(SYNC_CLIENTENTRYID)) { $clientid = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { // end clientid return false; } } else { $clientid = false; } // Get application data if available if ($decoder->getElementStartTag(SYNC_DATA)) { switch ($collection["class"]) { case "Email": $appdata = new SyncMail(); $appdata->decode($decoder); break; case "Contacts": $appdata = new SyncContact($protocolversion); $appdata->decode($decoder); break; case "Calendar": $appdata = new SyncAppointment(); $appdata->decode($decoder); break; case "Tasks": $appdata = new SyncTask(); $appdata->decode($decoder); break; } if (!$decoder->getElementEndTag()) { // end applicationdata return false; } } switch ($element[EN_TAG]) { case SYNC_MODIFY: if (isset($appdata)) { if (isset($appdata->read)) { // Currently, 'read' is only sent by the PDA when it is ONLY setting the read flag. $importer->ImportMessageReadFlag($serverid, $appdata->read); } else { $importer->ImportMessageChange($serverid, $appdata); } $collection["importedchanges"] = true; } break; case SYNC_ADD: if (isset($appdata)) { $id = $importer->ImportMessageChange(false, $appdata); if ($clientid && $id) { $collection["clientids"][$clientid] = $id; $collection["importedchanges"] = true; } } break; case SYNC_REMOVE: if (isset($collection["deletesasmoves"])) { $folderid = $backend->GetWasteBasket(); if ($folderid) { $importer->ImportMessageMove($serverid, $folderid); $collection["importedchanges"] = true; break; } } $importer->ImportMessageDeletion($serverid); $collection["importedchanges"] = true; break; case SYNC_FETCH: array_push($collection["fetchids"], $serverid); break; } if (!$decoder->getElementEndTag()) { // end change/delete/move return false; } } debugLog("Processed {$nchanges} incoming changes"); // Save the updated state, which is used for the exporter later $collection["syncstate"] = $importer->getState(); if (!$decoder->getElementEndTag()) { // end commands return false; } } if (!$decoder->getElementEndTag()) { // end collection return false; } array_push($collections, $collection); } if (!$decoder->getElementEndTag()) { // end collections return false; } if (!$decoder->getElementEndTag()) { // end sync return false; } $encoder = new WBXMLEncoder($output, $zpushdtd); $encoder->startWBXML(); $encoder->startTag(SYNC_SYNCHRONIZE); $encoder->startTag(SYNC_FOLDERS); foreach ($collections as $collection) { // initialize exporter to get changecount $changecount = 0; if (isset($collection["getchanges"])) { // Use the state from the importer, as changes may have already happened $exporter = $backend->GetExporter($collection["collectionid"]); $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : false; $exporter->Config($importer, $collection["class"], $filtertype, $collection["syncstate"], 0, $collection["truncation"]); $changecount = $exporter->GetChangeCount(); } // Get a new sync key to output to the client if any changes have been requested or will be send if (isset($collection["importedchanges"]) || $changecount > 0 || $collection["synckey"] == "0") { $collection["newsynckey"] = $statemachine->getNewSyncKey($collection["synckey"]); } $encoder->startTag(SYNC_FOLDER); $encoder->startTag(SYNC_FOLDERTYPE); $encoder->content($collection["class"]); $encoder->endTag(); $encoder->startTag(SYNC_SYNCKEY); if (isset($collection["newsynckey"])) { $encoder->content($collection["newsynckey"]); } else { $encoder->content($collection["synckey"]); } $encoder->endTag(); $encoder->startTag(SYNC_FOLDERID); $encoder->content($collection["collectionid"]); $encoder->endTag(); $encoder->startTag(SYNC_STATUS); $encoder->content(1); $encoder->endTag(); //check the mimesupport because we need it for advanced emails $mimesupport = isset($collection['mimesupport']) ? $collection['mimesupport'] : 0; // Output server IDs for new items we received from the PDA if (isset($collection["clientids"]) || count($collection["fetchids"]) > 0) { $encoder->startTag(SYNC_REPLIES); foreach ($collection["clientids"] as $clientid => $serverid) { $encoder->startTag(SYNC_ADD); $encoder->startTag(SYNC_CLIENTENTRYID); $encoder->content($clientid); $encoder->endTag(); $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($serverid); $encoder->endTag(); $encoder->startTag(SYNC_STATUS); $encoder->content(1); $encoder->endTag(); $encoder->endTag(); } foreach ($collection["fetchids"] as $id) { $data = $backend->Fetch($collection["collectionid"], $id, $mimesupport); if ($data !== false) { $encoder->startTag(SYNC_FETCH); $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($id); $encoder->endTag(); $encoder->startTag(SYNC_STATUS); $encoder->content(1); $encoder->endTag(); $encoder->startTag(SYNC_DATA); $data->encode($encoder); $encoder->endTag(); $encoder->endTag(); } else { debugLog("unable to fetch {$id}"); } } $encoder->endTag(); } if (isset($collection["getchanges"])) { // exporter already intialized if ($changecount > $collection["maxitems"]) { $encoder->startTag(SYNC_MOREAVAILABLE, false, true); } // Output message changes per folder $encoder->startTag(SYNC_PERFORM); // Stream the changes to the PDA $importer = new ImportContentsChangesStream($encoder, GetObjectClassFromFolderClass($collection["class"])); $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : 0; $n = 0; while (1) { $progress = $exporter->Synchronize(); if (!is_array($progress)) { break; } $n++; if ($n >= $collection["maxitems"]) { debugLog("Exported maxItems of messages: " . $collection["maxitems"] . " - more available"); break; } } $encoder->endTag(); } $encoder->endTag(); // Save the sync state for the next time if (isset($collection["newsynckey"])) { if (isset($exporter) && $exporter) { $state = $exporter->GetState(); } else { if (isset($importer) && $importer) { $state = $importer->GetState(); } else { if ($collection["synckey"] == "0") { $state = ""; } } } if (isset($state)) { $statemachine->setSyncState($collection["newsynckey"], $state); } else { debugLog("error saving " . $collection["newsynckey"] . " - no state information available"); } } } $encoder->endTag(); $encoder->endTag(); return true; }
function HandleSync($backend, $protocolversion, $devid) { global $zpushdtd; global $input, $output; global $user, $auth_pw; global $sessionstarttime; // Contains all containers requested $collections = array(); // Init WBXML decoder $decoder = new WBXMLDecoder($input, $zpushdtd); // Init state machine $statemachine = new StateMachine($devid, $user); // Start decode $shortsyncreq = false; $fetchitems = false; $dataimported = false; $dataavailable = false; $partial = false; $maxcacheage = 960; // 15 Minutes + 1 to store it long enough for Device being connected to ActiveSync PC. $SyncStatus = SYNC_STATUS_SUCCESS; // AS14 over all SyncStatus if (!$decoder->getElementStartTag(SYNC_SYNCHRONIZE)) { if ($protocolversion >= 12.1) { if (!($SyncCache = unserialize($statemachine->getSyncCache())) || !isset($SyncCache['collections'])) { _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); debugLog("HandleSync: Empty Sync request and no SyncCache or SyncCache without collections. " . "(SyncCache[lastuntil]+" . $maxcacheage . "=" . ($SyncCache['lastuntil'] + $maxcacheage) . ", " . "Time now" . time() . ", " . "SyncCache[collections]=" . (isset($SyncCache['collections']) ? "Yes" : "No") . ", " . "SyncCache array=" . (is_array($SyncCache) ? "Yes" : "No") . ") " . "(STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); return true; } else { if (sizeof($SyncCache['confirmed_synckeys']) > 0) { debugLog("HandleSync: We have unconfirmed sync keys but during short request. Enforce full Sync Request (STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); return true; } $shortsyncreq = true; $SyncCache['timestamp'] = time(); $statemachine->setSyncCache(serialize($SyncCache)); debugLog("HandleSync: Empty Sync request and taken info from SyncCache."); $collections = array(); foreach ($SyncCache['collections'] as $key => $value) { $collection = $value; $collection['collectionid'] = $key; if (isset($collection['synckey'])) { $collection['onlyoptionbodypreference'] = $protocolversion >= 14.0 && (!isset($collection["BodyPreference"][1]) && !isset($collection["BodyPreference"][2]) && !isset($collection["BodyPreference"][3]) && !isset($collection["BodyPreference"][4])); $collection['syncstate'] = $statemachine->getSyncState($collection['synckey']); if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype'] . 'syncstate'] = $statemachine->getSyncState($collection['optionfoldertype'] . $collection['synckey']); } if ($collection['synckey'] == "0") { $msginfos[$key] = array(); } else { $msginfos[$key] = unserialize($statemachine->getSyncState("mi" . $collection['synckey'])); } array_push($collections, $collection); } } if (count($collections) == 0) { debugLog("HandleSync: Don't have any collections. Enforce full request. (STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); return true; } } } else { _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); debugLog("HandleSync: Empty Sync request and protocolversion < 12.1 (STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); return true; } } else { if (!isset($SyncCache)) { $SyncCache = unserialize($statemachine->getSyncCache()); } // Just to update the timestamp... $SyncCache['timestamp'] = time(); // Check if time of last sync is too long ago (but only in case we don't expect a full request!) $statemachine->setSyncCache(serialize($SyncCache)); $SyncCache['wait'] = false; $SyncCache['hbinterval'] = false; while (($synctag = $decoder->getElementStartTag(SYNC_MAXITEMS) ? SYNC_MAXITEMS : ($decoder->getElementStartTag(SYNC_FOLDERS) ? SYNC_FOLDERS : ($decoder->getElementStartTag(SYNC_PARTIAL) ? SYNC_PARTIAL : ($decoder->getElementStartTag(SYNC_WAIT) ? SYNC_WAIT : ($decoder->getElementStartTag(SYNC_HEARTBEATINTERVAL) ? SYNC_HEARTBEATINTERVAL : -1))))) != -1) { switch ($synctag) { case SYNC_HEARTBEATINTERVAL: if ($SyncCache['hbinterval'] = $decoder->getElementContent()) { $decoder->getElementEndTag(); } debugLog('HandleSync: Got Heartbeat Interval Sync (' . $SyncCache['hbinterval'] . ' Seconds)'); if ($SyncCache['hbinterval'] > REAL_SCRIPT_TIMEOUT - 600) { _HandleSyncError(SYNC_STATUS_INVALID_WAIT_HEARTBEATINTERVAL, REAL_SCRIPT_TIMEOUT - 600); debugLog('HandleSync: HeartbeatInterval larger than ' . (REAL_SCRIPT_TIMEOUT - 600) . ' Seconds. This violates the protocol spec. (STATUS = ".SYNC_STATUS_INVALID_WAIT_HEARTBEATINTERVAL.", LIMIT = ' . (REAL_SCRIPT_TIMEOUT - 600) . ')'); return true; } break; case SYNC_WAIT: if ($SyncCache['wait'] = $decoder->getElementContent()) { $decoder->getElementEndTag(); } debugLog('HandleSync: Got Wait Sync (' . $SyncCache['wait'] . ' Minutes)'); if ($SyncCache['wait'] > (REAL_SCRIPT_TIMEOUT - 600) / 60) { _HandleSyncError(SYNC_STATUS_INVALID_WAIT_HEARTBEATINTERVAL, (REAL_SCRIPT_TIMEOUT - 600) / 60); debugLog('HandleSync: Wait larger than ' . (REAL_SCRIPT_TIMEOUT - 600) / 60 . ' Minutes. This violates the protocol spec. (STATUS = ".SYNC_STATUS_INVALID_WAIT_HEARTBEATINTERVAL.", LIMIT = ' . (REAL_SCRIPT_TIMEOUT - 600) / 60 . ')'); return true; } break; case SYNC_PARTIAL: if ($decoder->getElementContent(SYNC_PARTIAL)) { $decoder->getElementEndTag(); } $partial = true; break; case SYNC_MAXITEMS: // _HandleSyncError("12"); // return true; // Sending Max Items outside a collection is invalid according to specs... $default_maxitems = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_FOLDERS: $dataimported = false; while ($decoder->getElementStartTag(SYNC_FOLDER)) { $collection = array(); // Intializing the collection $collection['clientids'] = array(); $collection['fetchids'] = array(); $msginfo = array(); // set default truncation value $collection['truncation'] = SYNC_TRUNCATION_ALL; // set default conflict behavior from config if the device doesn't send a conflict resolution parameter $collection['conflict'] = SYNC_CONFLICT_DEFAULT; $collection['onlyoptionbodypreference'] = false; $collection['RightsManagementSupport'] = false; while (($foldertag = $decoder->getElementStartTag(SYNC_FOLDERTYPE) ? SYNC_FOLDERTYPE : ($decoder->getElementStartTag(SYNC_SYNCKEY) ? SYNC_SYNCKEY : ($decoder->getElementStartTag(SYNC_FOLDERID) ? SYNC_FOLDERID : ($decoder->getElementStartTag(SYNC_MAXITEMS) ? SYNC_MAXITEMS : ($decoder->getElementStartTag(SYNC_SUPPORTED) ? SYNC_SUPPORTED : ($decoder->getElementStartTag(SYNC_CONVERSATIONMODE) ? SYNC_CONVERSATIONMODE : ($decoder->getElementStartTag(SYNC_DELETESASMOVES) ? SYNC_DELETESASMOVES : ($decoder->getElementStartTag(SYNC_GETCHANGES) ? SYNC_GETCHANGES : ($decoder->getElementStartTag(SYNC_OPTIONS) ? SYNC_OPTIONS : ($decoder->getElementStartTag(SYNC_PERFORM) ? SYNC_PERFORM : -1)))))))))) != -1) { switch ($foldertag) { case SYNC_SYNCKEY: $collection["synckey"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } // Get our sync state for this collection $collection["syncstate"] = $statemachine->getSyncState($collection["synckey"]); if ($collection['synckey'] != "0") { $msginfo = unserialize($statemachine->getSyncState("mi" . $collection['synckey'])); } if (($delstatus = $statemachine->cleanOldSyncState($collection["synckey"])) !== true) { _HandleSyncError(abs($delstatus)); return true; } $statemachine->cleanOldSyncState("mi" . $collection["synckey"]); if (is_numeric($collection['syncstate']) && $collection['syncstate'] < 0 && strlen($collection['syncstate']) < 8) { debugLog("HandleSync: GetSyncState - Got an error in HandleSync ( STATUS = " . SYNC_STATUS_INVALID_SYNCKEY . ")"); _HandleSyncError(SYNC_STATUS_INVALID_SYNCKEY); return false; } // Reset the msginfos for the collectionid if set and synckey is 0 if ($collection['synckey'] == '0' && isset($msginfo)) { debugLog("HandleSync: SyncKey 0 detected and msginfos contains information for the collection - resetting msginfos"); unset($msginfo); } break; case SYNC_FOLDERID: $collection["collectionid"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } if ($collection['onlyoptionbodypreference'] == false && isset($SyncCache['collections'][$collection["collectionid"]]["BodyPreference"])) { $collection['onlyoptionbodypreference'] = $protocolversion >= 14.0 && (!isset($SyncCache['collections'][$collection["collectionid"]]["BodyPreference"][1]) && !isset($SyncCache['collections'][$collection["collectionid"]]["BodyPreference"][2]) && !isset($SyncCache['collections'][$collection["collectionid"]]["BodyPreference"][3]) && !isset($SyncCache['collections'][$collection["collectionid"]]["BodyPreference"][4])); } break; case SYNC_FOLDERTYPE: $collection["class"] = $decoder->getElementContent(); debugLog("HandleSync: Sync folder:{$collection["class"]}"); if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_MAXITEMS: $collection["maxitems"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_CONVERSATIONMODE: if (($collection["conversationmode"] = $decoder->getElementContent()) !== false) { if (!$decoder->getElementEndTag()) { return false; } } else { $collection["conversationmode"] = true; } break; case SYNC_SUPPORTED: while (1) { $el = $decoder->getElement(); if ($el[EN_TYPE] == EN_TYPE_ENDTAG) { break; } } break; case SYNC_DELETESASMOVES: if (($collection["deletesasmoves"] = $decoder->getElementContent()) !== false) { if (!$decoder->getElementEndTag()) { return false; } } else { $collection["deletesasmoves"] = true; } break; case SYNC_GETCHANGES: if (($collection["getchanges"] = $decoder->getElementContent()) !== false) { if (!$decoder->getElementEndTag()) { return false; } } else { $collection["getchanges"] = true; } break; case SYNC_OPTIONS: while (($syncoptionstag = $decoder->getElementStartTag(SYNC_FOLDERTYPE) ? SYNC_FOLDERTYPE : ($decoder->getElementStartTag(SYNC_FILTERTYPE) ? SYNC_FILTERTYPE : ($decoder->getElementStartTag(SYNC_TRUNCATION) ? SYNC_TRUNCATION : ($decoder->getElementStartTag(SYNC_RTFTRUNCATION) ? SYNC_RTFTRUNCATION : ($decoder->getElementStartTag(SYNC_MIMESUPPORT) ? SYNC_MIMESUPPORT : ($decoder->getElementStartTag(SYNC_MIMETRUNCATION) ? SYNC_MIMETRUNCATION : ($decoder->getElementStartTag(SYNC_CONFLICT) ? SYNC_CONFLICT : ($decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE) ? SYNC_AIRSYNCBASE_BODYPREFERENCE : ($decoder->getElementStartTag(SYNC_RIGHTSMANAGEMENT_RIGHTSMANAGEMENTSUPPORT) ? SYNC_RIGHTSMANAGEMENT_RIGHTSMANAGEMENTSUPPORT : -1))))))))) != -1) { // dw2412 in as14 this is used to sent SMS type messages switch ($syncoptionstag) { case SYNC_FOLDERTYPE: $collection['optionfoldertype'] = $decoder->getElementContent(); $collection[$collection['optionfoldertype']]['RightsManagementSupport'] = false; $collection[$collection['optionfoldertype']]['truncation'] = SYNC_TRUNCATION_ALL; $collection[$collection['optionfoldertype']]['conflict'] = SYNC_CONFLICT_DEFAULT; if (!$decoder->getElementEndTag()) { return false; } // In case there is no optionfoldertype set in our cache, remove all old // optionfoldertype sync keys in case any exist if (!isset($SyncCache['collections'][$collection['collectionid']]['optionfoldertype'])) { $statemachine->removeSyncState($collection['optionfoldertype'] . $collection["synckey"]); } $collection[$collection['optionfoldertype'] . 'syncstate'] = $statemachine->getSyncState($collection['optionfoldertype'] . $collection['synckey']); if (($delstatus = $statemachine->cleanOldSyncState($collection['optionfoldertype'] . $collection["synckey"])) !== true) { _HandleSyncError(abs($delstatus)); return true; } break; case SYNC_FILTERTYPE: if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype']]["filtertype"] = $decoder->getElementContent(); } else { $collection["filtertype"] = $decoder->getElementContent(); } if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_TRUNCATION: if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype']]["truncation"] = $decoder->getElementContent(); } else { $collection["truncation"] = $decoder->getElementContent(); } if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_RTFTRUNCATION: if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype']]["rtftruncation"] = $decoder->getElementContent(); } else { $collection["rtftruncation"] = $decoder->getElementContent(); } if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_MIMESUPPORT: if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype']]["mimesupport"] = $decoder->getElementContent(); } else { $collection["mimesupport"] = $decoder->getElementContent(); } if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_MIMETRUNCATION: if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype']]["mimetruncation"] = $decoder->getElementContent(); } else { $collection["mimetruncation"] = $decoder->getElementContent(); } if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_CONFLICT: if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype']]["conflict"] = $decoder->getElementContent(); } else { $collection["conflict"] = $decoder->getElementContent(); } if (!$decoder->getElementEndTag()) { return false; } break; // START ADDED dw2412 V12.0 Sync Support // START ADDED dw2412 V12.0 Sync Support case SYNC_AIRSYNCBASE_BODYPREFERENCE: if (!isset($bodypreference)) { $bodypreference = array(); } while (($bodypreferencefield = $decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE) ? SYNC_AIRSYNCBASE_TYPE : ($decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE) ? SYNC_AIRSYNCBASE_TRUNCATIONSIZE : ($decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW) ? SYNC_AIRSYNCBASE_PREVIEW : ($decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE) ? SYNC_AIRSYNCBASE_ALLORNONE : -1)))) != -1) { switch ($bodypreferencefield) { case SYNC_AIRSYNCBASE_TYPE: $bodypreference["Type"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_AIRSYNCBASE_TRUNCATIONSIZE: $bodypreference["TruncationSize"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_AIRSYNCBASE_PREVIEW: $bodypreference["Preview"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } break; case SYNC_AIRSYNCBASE_ALLORNONE: $bodypreference["AllOrNone"] = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { return false; } break; } } $decoder->getElementEndTag(); if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype']]["BodyPreference"][$bodypreference["Type"]] = $bodypreference; } else { $collection["BodyPreference"][$bodypreference["Type"]] = $bodypreference; } if ($collection['onlyoptionbodypreference'] == false) { $collection['onlyoptionbodypreference'] = $protocolversion >= 14.0 && (!isset($collection["BodyPreference"][1]) && !isset($collection["BodyPreference"][2]) && !isset($collection["BodyPreference"][3]) && !isset($collection["BodyPreference"][4])); } break; // END ADDED dw2412 V12.0 Sync Support // END ADDED dw2412 V12.0 Sync Support case SYNC_RIGHTSMANAGEMENT_RIGHTSMANAGEMENTSUPPORT: if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype']]['RightsManagementSupport'] = $decoder->getElementContent(); } else { $collection['RightsManagementSupport'] = $decoder->getElementContent(); } if (!$decoder->getElementEndTag()) { return false; } break; } } $decoder->getElementEndTag(); break; case SYNC_PERFORM: // compatibility mode - get folderid from the state directory if (!isset($collection["collectionid"])) { $collection["collectionid"] = _getFolderID($devid, $collection["class"]); } // Start error checking // Since we're not working sequential with the fields we need to do error checking prior actual perform can take place. // If needed elements are missing we will return error Status to the client if ($collection["collectionid"] == "" || $collection["collectionid"] == false) { _HandleSyncError(SYNC_STATUS_INVALID_SYNCKEY); debugLog("HandleSync: Should do a perform but don't have a collectionid, sending status " . SYNC_STATUS_INVALID_SYNCKEY . " to recover from this ( STATUS = " . SYNC_STATUS_INVALID_SYNCKEY . ")"); return true; } if (!isset($collection["synckey"])) { _HandleSyncError(SYNC_STATUS_PROTOCOL_ERROR); debugLog("HandleSync: Should do a perform in collection " . $collection["collectionid"] . " without any synckey, sending status " . SYNC_STATUS_PROTOCOL_ERROR . " to recover from this ( STATUS = " . SYNC_STATUS_PROTOCOL_ERROR . ")"); return true; } if ($protocolversion >= 12.1 && !isset($collection["class"]) && isset($collection["collectionid"])) { if (isset($SyncCache['folders'][$collection["collectionid"]]["class"])) { $collection["class"] = $SyncCache['folders'][$collection["collectionid"]]["class"]; debugLog("HandleSync: Sync folder:{$collection["class"]}"); } else { _HandleSyncError(SYNC_STATUS_FOLDER_HIERARCHY_CHANGED); debugLog("HandleSync: No Class even in cache, sending status " . SYNC_STATUS_FOLDER_HIERARCHY_CHANGED . " to recover from this ( STATUS = " . SYNC_STATUS_FOLDER_HIERARCHY_CHANGED . ")"); return true; } } // End error checking, everything seems to be ok until this point. Doing the requested SYNC_PERFORM // Configure importer with last state $importer[$collection["collectionid"]] = $backend->GetContentsImporter($collection["collectionid"]); $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]]["filtertype"] : 0); $mclass = isset($collection["class"]) ? $collection["class"] : (isset($SyncCache['collections'][$collection["collectionid"]]["class"]) ? $SyncCache['collections'][$collection["collectionid"]]["class"] : false); $bodypreference = isset($collection["BodyPreference"]) ? $collection["BodyPreference"] : (isset($SyncCache['collections'][$collection["collectionid"]]["BodyPreference"]) ? $SyncCache['collections'][$collection["collectionid"]]["BodyPreference"] : false); if (isset($collection["optionfoldertype"])) { $optionfiltertype = isset($collection[$collection['optionfoldertype']]['filtertype']) ? $collection[$collection['optionfoldertype']]['filtertype'] : (isset($SyncCache['collections'][$collection['collectionid']][$collection['optionfoldertype']]['filtertype']) ? $SyncCache['collections'][$collection['collectionid']][$collection['optionfoldertype']]['filtertype'] : 0); $optionbodypreference = isset($collection[$collection["optionfoldertype"]]["BodyPreference"]) ? $collection[$collection["optionfoldertype"]]["BodyPreference"] : (isset($SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["BodyPreference"]) ? $SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["BodyPreference"] : false); $importer[$collection['optionfoldertype'] . $collection["collectionid"]] = $backend->GetContentsImporter($collection["collectionid"]); $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->Config($collection[$collection['optionfoldertype'] . 'syncstate'], $collection["conflict"], $collection['optionfoldertype'], $optionfiltertype, false, $optionbodypreference); } else { $optionbodypreference = false; } debugLog("HandleSync: FilterTypes Perform: " . $filtertype . " " . (isset($optionfiltertype) ? $optionfiltertype : "")); if ($collection['onlyoptionbodypreference'] === false) { $importer[$collection["collectionid"]]->Config($collection['syncstate'], $collection["conflict"], $mclass, $filtertype, $bodypreference, false); } $nchanges = 0; while (($performtag = $decoder->getElementStartTag(SYNC_ADD) ? SYNC_ADD : ($decoder->getElementStartTag(SYNC_MODIFY) ? SYNC_MODIFY : ($decoder->getElementStartTag(SYNC_REMOVE) ? SYNC_REMOVE : ($decoder->getElementStartTag(SYNC_FETCH) ? SYNC_FETCH : -1)))) != -1) { $nchanges++; // dw2412 in as14 this is used to sent SMS type messages $foldertype = false; $serverid = false; $clientid = false; while (($addmodifyfetchtag = $decoder->getElementStartTag(SYNC_FOLDERTYPE) ? SYNC_FOLDERTYPE : ($decoder->getElementStartTag(SYNC_SERVERENTRYID) ? SYNC_SERVERENTRYID : ($decoder->getElementStartTag(SYNC_CLIENTENTRYID) ? SYNC_CLIENTENTRYID : ($decoder->getElementStartTag(SYNC_DATA) ? SYNC_DATA : -1)))) != -1) { switch ($addmodifyfetchtag) { case SYNC_FOLDERTYPE: $foldertype = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { // end foldertype return false; } break; case SYNC_SERVERENTRYID: $serverid = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { // end serverid return false; } break; case SYNC_CLIENTENTRYID: $clientid = $decoder->getElementContent(); if (!$decoder->getElementEndTag()) { // end clientid return false; } break; case SYNC_DATA: // Get application data if available if (!isset($collection["class"])) { debugLog("HandleSync: No Class found for collection " . $collection["collectionid"]); if (isset($SyncCache["collections"][$collection["collectionid"]]["class"])) { debugLog("HandleSync: SyncCache search results in " . $SyncCache["collections"][$collection["collectionid"]]["class"]); $collection["class"] = $SyncCache["collections"][$collection["collectionid"]]["class"]; } else { debugLog("HandleSync: SyncCache search results in nothing :-("); } } switch ($collection["class"]) { case "Email": if ($foldertype) { $appdata = new SyncSMS(); } else { $appdata = new SyncMail(); } $appdata->decode($decoder); break; case "Contacts": $appdata = new SyncContact($protocolversion); $appdata->decode($decoder); break; case "Calendar": $appdata = new SyncAppointment(); $appdata->decode($decoder); break; case "Tasks": $appdata = new SyncTask(); $appdata->decode($decoder); break; case "Notes": $appdata = new SyncNote(); $appdata->decode($decoder); break; } if (!$decoder->getElementEndTag()) { // end applicationdata return false; } break; } } switch ($performtag) { case SYNC_MODIFY: if (isset($appdata)) { if ($appdata->_setchange == true || $appdata->_setread == false && $appdata->_setflag == false && $appdata->_setcategories == false) { if (isset($collection['optionfoldertype']) && $foldertype == $collection['optionfoldertype']) { $collection['changeids'][$serverid]['optionfoldertype'] = $foldertype; if (!isset($msginfo[$serverid])) { $collection[$collection['optionfoldertype'] . 'changeids'][$serverid]['status'] = SYNC_STATUS_OBJECT_NOT_FOUND; } else { $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->ImportMessageChange($serverid, $appdata); $collection[$collection['optionfoldertype'] . 'changeids'][$serverid]['status'] = SYNC_STATUS_SUCCESS; } } else { if (!isset($msginfo[$serverid])) { $collection['changeids'][$serverid]['status'] = SYNC_STATUS_OBJECT_NOT_FOUND; } else { $importer[$collection["collectionid"]]->ImportMessageChange($serverid, $appdata); $collection['changeids'][$serverid]['status'] = SYNC_STATUS_SUCCESS; } } } else { if ($appdata->_setflag == true) { if (isset($collection['optionfoldertype']) && $foldertype == $collection['optionfoldertype']) { $collection[$collection['optionfoldertype'] . "flagids"][$serverid]['data'] = $appdata->poommailflag; $collection['changeids'][$serverid]['optionfoldertype'] = $foldertype; if (!isset($msginfo[$serverid])) { $collection[$collection['optionfoldertype'] . 'changeids'][$serverid]['status'] = SYNC_STATUS_OBJECT_NOT_FOUND; } else { $collection[$collection['optionfoldertype'] . "flagids"][$serverid]['status'] = $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->ImportMessageFlag($serverid, $appdata->poommailflag); $collection[$collection['optionfoldertype'] . 'changeids'][$serverid]['status'] = SYNC_STATUS_SUCCESS; } } else { $collection["flagids"][$serverid]['data'] = $appdata->poommailflag; if (!isset($msginfo[$serverid])) { $collection['changeids'][$serverid]['status'] = SYNC_STATUS_OBJECT_NOT_FOUND; } else { $collection["flagids"][$serverid]['status'] = $importer[$collection["collectionid"]]->ImportMessageFlag($serverid, $appdata->poommailflag); $collection['changeids'][$serverid]['status'] = SYNC_STATUS_SUCCESS; } } } if ($appdata->_setread == true) { if (isset($collection['optionfoldertype']) && $foldertype == $collection['optionfoldertype']) { $collection[$collection['optionfoldertype'] . "readids"][$serverid]['data'] = $appdata->read; $collection['changeids'][$serverid]['optionfoldertype'] = $foldertype; if (!isset($msginfo[$serverid])) { $collection[$collection['optionfoldertype'] . 'changeids'][$serverid]['status'] = SYNC_STATUS_OBJECT_NOT_FOUND; } else { $collection[$collection['optionfoldertype'] . "readids"][$serverid]['status'] = $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->ImportMessageReadFlag($serverid, $appdata->read); $collection[$collection['optionfoldertype'] . 'changeids'][$serverid]['status'] = SYNC_STATUS_SUCCESS; } } else { $collection["readids"][$serverid]['data'] = $appdata->read; if (!isset($msginfo[$serverid])) { $collection['changeids'][$serverid]['status'] = SYNC_STATUS_OBJECT_NOT_FOUND; } else { $collection["readids"][$serverid]['status'] = $importer[$collection["collectionid"]]->ImportMessageReadFlag($serverid, $appdata->read); $collection['changeids'][$serverid]['status'] = SYNC_STATUS_SUCCESS; } } } } $collection["importedchanges"] = true; } break; case SYNC_ADD: if (isset($appdata)) { if (isset($collection['optionfoldertype']) && $foldertype == $collection['optionfoldertype']) { $id = $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->ImportMessageChange(false, $appdata); } else { $id = $importer[$collection["collectionid"]]->ImportMessageChange(false, $appdata); } if ($clientid && $id) { $collection["clientids"][$clientid]['serverid'] = $id; if ($foldertype) { $collection["clientids"][$clientid]['optionfoldertype'] = $foldertype; $md5msg = array('datereceived' => isset($appdata->datereceived) ? strval($appdata->datereceived) : '', 'importance' => isset($appdata->importance) ? strval($appdata->importance) : '', 'messageclass' => isset($appdata->messageclass) ? strval($appdata->messageclass) : '', 'to' => isset($appdata->to) ? strval($appdata->to) : '', 'cc' => isset($appdata->cc) ? strval($appdata->cc) : '', 'from' => isset($appdata->from) ? strval($appdata->from) : '', 'internetcpid' => isset($appdata->internetcpid) ? strval($appdata->internetcpid) : '', 'body' => isset($appdata->body) ? strval($appdata->body) : ''); $md5flags = array('flagstatus' => isset($appdata->poommailflag->flagstatus) ? strval($appdata->poommailflag->flagstatus) : '', 'flagtype' => isset($appdata->poommailflag->flagtype) ? strval($appdata->poommailflag->flagtype) : '', 'startdate' => isset($appdata->poommailflag->startdate) ? strval($appdata->poommailflag->startdate) : '', 'utcstartdate' => isset($appdata->poommailflag->utcstartdate) ? strval($appdata->poommailflag->utcstartdate) : '', 'duedate' => isset($appdata->poommailflag->duedate) ? strval($appdata->poommailflag->duedate) : '', 'utcduedate' => isset($appdata->poommailflag->utcduedate) ? strval($appdata->poommailflag->utcduedate) : '', 'datecomplete' => isset($appdata->poommailflag->datecompleted) ? strval($appdata->poommailflag->datecompleted) : '', 'reminderset' => isset($appdata->poommailflag->reminderset) ? strval($appdata->poommailflag->reminderset) : '', 'subject' => isset($appdata->poommailflag->subject) ? strval($appdata->poommailflag->subject) : '', 'ordinaldate' => isset($appdata->poommailflag->ordinaldate) ? strval($appdata->poommailflag->ordinaldate) : '', 'subordinaldate' => isset($appdata->poommailflag->subordinaldate) ? strval($appdata->poommailflag->subordinaldate) : '', 'completetime' => isset($appdata->poommailflag->completetime) ? strval($appdata->poommailflag->completetime) : ''); $msginf['md5msg'] = md5(serialize($md5msg)); $msginf['md5flags'] = md5(serialize($md5flags)); $msginf['read'] = isset($appdata->read) ? $appdata->read : ''; $msginf['class'] = "syncsms"; unset($md5msg); unset($md5flags); $msginfo[$id['sourcekey']] = $msginf; debugLog("HandleSync: Generated msginfos for " . $id['sourcekey'] . " with following values: " . print_r($msginf, true)); unset($msginf); } else { $msginfo[$id] = array('md5msg' => 0, 'read' => '', 'md5flags' => '', 'class' => strtolower(get_class($appdata))); debugLog("HandleSync: Generated msginfos for " . $id . " with following values: " . print_r($msginfo, true)); } $collection["importedchanges"] = true; } } break; case SYNC_REMOVE: if (isset($collection["deletesasmoves"])) { $folderid = $backend->GetWasteBasket(); if ($folderid) { if (isset($collection['optionfoldertype']) && $foldertype == $collection['optionfoldertype']) { $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->ImportMessageMove($serverid, $folderid); } else { $importer[$collection["collectionid"]]->ImportMessageMove($serverid, $folderid); } $collection["importedchanges"] = true; break; } else { debugLog("HandleSync: SYNC_REMOVE failed because there is no waste basket returned!"); } } if (isset($importer[$collection["collectionid"]])) { if (isset($collection['optionfoldertype']) && $foldertype == $collection['optionfoldertype']) { $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->ImportMessageDeletion($serverid); } else { $importer[$collection["collectionid"]]->ImportMessageDeletion($serverid); } } else { debugLog("HandleSync: SYNC_REMOVE failed because there is no importer for collection"); } $collection["importedchanges"] = true; if (isset($collection['changeids'][$serverid])) { $collection['changeids'][$serverid]['status'] = SYNC_STATUS_OBJECT_NOT_FOUND; } break; case SYNC_FETCH: array_push($collection["fetchids"], $serverid); break; } if (!$decoder->getElementEndTag()) { // end add/remove/modify/fetch return false; } } debugLog("HandleSync: Processed {$nchanges} incoming changes"); // Save the updated state, which is used for the exporter later if (isset($importer[$collection["collectionid"]])) { $collection['syncstate'] = $importer[$collection["collectionid"]]->getState(); } if (isset($collection['optionfoldertype']) && isset($importer[$collection['optionfoldertype'] . $collection["collectionid"]])) { $collection[$collection['optionfoldertype'] . 'syncstate'] = $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->getState(); } if (isset($collection["importedchanges"]) && $collection["importedchanges"] == true) { $dataimported = true; } if (isset($collection["fetchids"])) { $fetchitems = true; } if (!$decoder->getElementEndTag()) { // end SYNC_PERFORM return false; } break; } } if (!$decoder->getElementEndTag()) { // end collection return false; } if (isset($msginfo)) { $statemachine->setSyncState('mi' . $collection['synckey'], serialize($msginfo)); } array_push($collections, $collection); if (isset($collection['collectionid'])) { $msginfos[$collection['collectionid']] = isset($msginfo) ? $msginfo : array(); if (isset($collection['class'])) { $SyncCache['collections'][$collection['collectionid']]['class'] = $collection['class']; } if (isset($collection['maxitems'])) { $SyncCache['collections'][$collection['collectionid']]['maxitems'] = $collection['maxitems']; } if (isset($collection['deletesasmoves'])) { $SyncCache['collections'][$collection['collectionid']]['deletesasmoves'] = $collection['deletesasmoves']; } if (isset($collection['conversationmode'])) { $SyncCache['collections'][$collection['collectionid']]['conversationmode'] = $collection['conversationmode']; } if (isset($collection['filtertype'])) { $SyncCache['collections'][$collection['collectionid']]['filtertype'] = $collection['filtertype']; } if (isset($collection['truncation'])) { $SyncCache['collections'][$collection['collectionid']]['truncation'] = $collection['truncation']; } if (isset($collection['rtftruncation'])) { $SyncCache['collections'][$collection['collectionid']]['rtftruncation'] = $collection['rtftruncation']; } if (isset($collection['mimesupport'])) { $SyncCache['collections'][$collection['collectionid']]['mimesupport'] = $collection['mimesupport']; } if (isset($collection['mimetruncation'])) { $SyncCache['collections'][$collection['collectionid']]['mimetruncation'] = $collection['mimetruncation']; } if (isset($collection['conflict'])) { $SyncCache['collections'][$collection['collectionid']]['conflict'] = $collection['conflict']; } if (isset($collection['BodyPreference'])) { $SyncCache['collections'][$collection['collectionid']]['BodyPreference'] = $collection['BodyPreference']; } if (isset($collection['optionfoldertype'])) { if (isset($collection[$collection['optionfoldertype']]['filtertype'])) { $SyncCache['collections'][$collection['collectionid']][$collection['optionfoldertype']]['filtertype'] = $collection[$collection['optionfoldertype']]['filtertype']; } if (isset($collection[$collection['optionfoldertype']]['BodyPreference'])) { $SyncCache['collections'][$collection['collectionid']][$collection['optionfoldertype']]['BodyPreference'] = $collection[$collection['optionfoldertype']]['BodyPreference']; } $SyncCache['collections'][$collection['collectionid']]['optionfoldertype'] = $collection['optionfoldertype']; } elseif (isset($SyncCache['collections'][$collection['collectionid']]['optionfoldertype'])) { $optionfoldertype = $SyncCache['collections'][$collection['collectionid']]['optionfoldertype']; if (isset($SyncCache['collections'][$collection['collectionid']][$optionfoldertype])) { unset($SyncCache['collections'][$collection['collectionid']][$optionfoldertype]); unset($SyncCache['collections'][$collection['collectionid']]['optionfoldertype']); } } } } if (!$decoder->getElementEndTag()) { // end collections return false; } break; } } if (!isset($collections)) { debugLog("HandleSync: HERE S " . (isset($SyncCache['lastuntil']) ? strftime("%x %X", $SyncCache['lastuntil'] + $maxcacheage) : "NO LASTUNTIL!")); $found = false; foreach ($SyncCache['collections'] as $value) { if (isset($value['synckey'])) { $found = true; break; } } if ($found == false) { $SyncCache['lastuntil'] = time(); $statemachine->setSyncCache(serialize($SyncCache)); _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); debugLog("HandleSync: No Collections with SyncKeys. Enforce Full Sync Request (STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); return true; } } // Fill up collections with values from cache in case they're missing foreach ($collections as $key => $values) { if (!isset($values["class"]) && isset($SyncCache['folders'][$values["collectionid"]]["class"])) { $collections[$key]["class"] = $SyncCache['folders'][$values["collectionid"]]["class"]; } if (!isset($values["filtertype"]) && isset($SyncCache['collections'][$values["collectionid"]]["filtertype"])) { $collections[$key]["filtertype"] = $SyncCache['collections'][$values["collectionid"]]["filtertype"]; } if (!isset($values["mimesupport"]) && isset($SyncCache['collections'][$values["collectionid"]]["mimesupport"])) { $collections[$key]["mimesupport"] = $SyncCache['collections'][$values["collectionid"]]["mimesupport"]; } if (!isset($values["BodyPreference"]) && isset($SyncCache['collections'][$values["collectionid"]]["BodyPreference"])) { $collections[$key]["BodyPreference"] = $SyncCache['collections'][$values["collectionid"]]["BodyPreference"]; } if (isset($value['optionfoldertype'])) { if (!isset($values[$value['optionfoldertype']]["filtertype"]) && isset($SyncCache['collections'][$values["collectionid"]][$value['optionfoldertype']]["filtertype"])) { $collections[$key][$value['optionfoldertype']]["filtertype"] = $SyncCache['collections'][$values["collectionid"]][$value['optionfoldertype']]["filtertype"]; } if (!isset($values[$value['optionfoldertype']]["BodyPreference"]) && isset($SyncCache['collections'][$values["collectionid"]][$value['optionfoldertype']]["BodyPreference"])) { $collections[$key][$value['optionfoldertype']]["BodyPreference"] = $SyncCache['collections'][$values["collectionid"]][$value['optionfoldertype']]["BodyPreference"]; } } $collections[$key]['onlyoptionbodypreference'] = $protocolversion >= 14.0 && (!isset($SyncCache['collections'][$values["collectionid"]]["BodyPreference"][1]) && !isset($SyncCache['collections'][$values["collectionid"]]["BodyPreference"][2]) && !isset($SyncCache['collections'][$values["collectionid"]]["BodyPreference"][3]) && !isset($SyncCache['collections'][$values["collectionid"]]["BodyPreference"][4])); // Set the maxitems (windowsize) to either what is being declared in cache or to 100 if nothing can be found in cache. // 100 is according to spec the default if nothing is being sent by the client if (!isset($values["maxitems"])) { $collections[$key]["maxitems"] = isset($SyncCache['collections'][$values["collectionid"]]['maxitems']) ? $SyncCache['collections'][$values["collectionid"]]['maxitems'] : 100; } // in case the maxitems (windowsize) is above 512 or 0 it should be interpreted as 512 according to specs. if ($collections[$key]["maxitems"] > 512 || $collections[$key]["maxitems"] == 0) { $collections[$key]["maxitems"] = 512; } if (isset($values['synckey']) && $values['synckey'] == '0' && isset($SyncCache['collections'][$values["collectionid"]]['synckey']) && $SyncCache['collections'][$values["collectionid"]]['synckey'] != '0') { unset($SyncCache['collections'][$values["collectionid"]]['synckey']); } } // Give up in case we don't have a synched hierarchy synckey! if (!isset($SyncCache['hierarchy']['synckey'])) { _HandleSyncError(SYNC_STATUS_FOLDER_HIERARCHY_CHANGED); debugLog("HandleSync: HandleSync Error No Hierarchy SyncKey in SyncCache... Invalidate! (STATUS = " . SYNC_STATUS_FOLDER_HIERARCHY_CHANGED . ")"); return true; } // Just in case some client runs amok. This HB Interval & Wait in one request is not allowed by definition if ($SyncCache['hbinterval'] !== false && $SyncCache['wait'] !== false) { _HandleSyncError(SYNC_STATUS_PROTOCOL_ERROR); debugLog("HandleSync: HandleSync got Found HeartbeatInterval and Wait in request. This violates the protocol spec. (STATUS = " . SYNC_STATUS_PROTOCOL_ERROR . ")"); return true; } // Partial sync but with Folders and Options so we need to set collections $foundsynckey = false; if ($partial === true) { debugLog("HandleSync: Partial Sync"); $TempSyncCache = unserialize($statemachine->getSyncCache()); // Removing all from TempSyncCache that we already got information on $CollectionsUnchanged = 0; $CollectionKeys = 0; $ConfirmedKeys = 0; foreach ($collections as $key => $value) { // Discover if any collection got really changed $v1 = $collections[$key]; if (isset($v1['collectionid'])) { unset($v1['collectionid']); } if (isset($v1['clientids'])) { unset($v1['clientids']); } if (isset($v1['fetchids'])) { unset($v1['fetchids']); } if (isset($v1['getchanges'])) { unset($v1['getchanges']); } if (isset($v1['changeids'])) { unset($v1['changeids']); } if (isset($v1['onlyoptionbodypreference'])) { unset($v1['onlyoptionbodypreference']); } if (isset($v1['syncstate'])) { unset($v1['syncstate']); } if (isset($v1['optionfoldertype'])) { if (isset($v1[$v1['optionfoldertype'] . 'syncstate'])) { unset($v1[$v1['optionfoldertype'] . 'syncstate']); } } $v2 = $TempSyncCache['collections'][$value['collectionid']]; ksort($v1); if (isset($v1['BodyPreference'])) { ksort($v1['BodyPreference']); foreach ($v1['BodyPreference'] as $key => $v) { ksort($v1['BodyPreference'][$key]); } } if (isset($v1['optionfoldertype'])) { ksort($v1[$v1['optionfoldertype']]); if (isset($v1[$v1['optionfoldertype']]['BodyPreference'])) { ksort($v1[$v1['optionfoldertype']]['BodyPreference']); foreach ($v1[$v1['optionfoldertype']]['BodyPreference'] as $key => $v) { ksort($v1[$v1['optionfoldertype']]['BodyPreference'][$key]); } } } if (isset($v1['BodyPreference'])) { ksort($v1['BodyPreference']); } ksort($v2); if (isset($v2['BodyPreference'])) { ksort($v2['BodyPreference']); foreach ($v2['BodyPreference'] as $key => $v) { ksort($v2['BodyPreference'][$key]); } } if (isset($v2['optionfoldertype'])) { ksort($v2[$v2['optionfoldertype']]); if (isset($v2[$v2['optionfoldertype']]['BodyPreference'])) { ksort($v2[$v2['optionfoldertype']]['BodyPreference']); foreach ($v2[$v2['optionfoldertype']]['BodyPreference'] as $key => $v) { ksort($v2[$v2['optionfoldertype']]['BodyPreference'][$key]); } } } if (md5(serialize($v1)) == md5(serialize($v2))) { $CollectionsUnchanged++; } if (isset($v2['optionfoldertype']) && !isset($v1['optionfoldertype']) || isset($v1['optionfoldertype']) && !isset($v2['optionfoldertype'])) { $SyncStatus = SYNC_STATUS_REQUEST_INCOMPLETE; } unset($v1); unset($v2); // Unset Collection in TempSyncCache in case we already have it in our collections if (isset($TempSyncCache['collections'][$value['collectionid']])) { debugLog("HandleSync: Removing " . $value['collectionid'] . " from TempSyncCache"); unset($TempSyncCache['collections'][$value['collectionid']]); } // Remove keys from confirmed synckeys array and count them if (isset($value['synckey'])) { $foundsynckey = true; if (isset($SyncCache['confirmed_synckeys'][$value['synckey']])) { debugLog('HandleSync: Removed ' . $SyncCache['confirmed_synckeys'][$value['synckey']] . ' from confirmed_synckeys array'); unset($SyncCache['confirmed_synckeys'][$value['synckey']]); $statemachine->deleteSyncCacheConfirmedSyncKey($SyncCache, $value['synckey']); $ConfirmedKeys++; } } // Count all current Collections with SyncKey set if (isset($value['synckey'])) { $CollectionKeys++; } } $CacheKeys = 0; foreach ($SyncCache['collections'] as $value) { // Count all cached Collections with SyncKey set if (isset($value['synckey'])) { $CacheKeys++; } } debugLog("HandleSync: CollectionKeys vs SyncCacheKeys vs Unchanged Collections vs ConfirmedKeys: " . $CollectionKeys . " / " . $CacheKeys . " / " . $CollectionsUnchanged . " / " . $ConfirmedKeys); debugLog("HandleSync: Wait Cache / TempCache: " . $SyncCache['wait'] . " / " . $TempSyncCache['wait']); debugLog("HandleSync: Heartbeat Cache / TempCache: " . $SyncCache['hbinterval'] . " / " . $TempSyncCache['hbinterval']); debugLog("HandleSync: Time now is <= SyncCache lastuntil (" . time() . " - " . $SyncCache['lastuntil'] . " = " . (time() - $SyncCache['lastuntil']) . ")"); debugLog("HandleSync: Last HB Sync started vs Last Sync normal end " . $SyncCache['lasthbsyncstarted'] . " / " . $SyncCache['lastsyncendnormal'] . ")"); if (isset($SyncCache['lasthbsyncstarted']) && $SyncCache['lasthbsyncstarted'] > $SyncCache['lastsyncendnormal']) { debugLog("HandleSync: lasthbsyncstarted is larger than lastsyncendnormal. Request a full request now (STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); return true; } if (isset($SyncCache['lastuntil']) && isset($SyncCache['lasthbsyncstarted']) && isset($SyncCache['lastsyncendnormal']) && $SyncCache['lasthbsyncstarted'] > $SyncCache['lastsyncendnormal'] && time() < $SyncCache['lastuntil']) { debugLog("HandleSync: Current Time is lower than lastuntil. Request a full request now (STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); return true; } // If there are no changes within partial sync, send status 13 since sending partial elements without any changes is suspicius // (Could be a remove folder from sync...) // Logic is: // Collection SyncKeys are being send by device // No SyncKeys got confirmed // Collections in request are equal with Collections in Cache // Current Heartbeat/Wait is still running // No new Heartbeat/Wait Value is being sent if ($CollectionKeys > 0 && $ConfirmedKeys == 0 && $CollectionsUnchanged == $CollectionKeys && time() <= $SyncCache['lastuntil'] && ($SyncCache['wait'] == false && $SyncCache['hbinterval'] == false)) { debugLog("HandleSync: Partial Request with completely unchanged collections. Request a full request now (STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); return true; } // Updating Collections with all necessary informations that we don't have informations for but with a synckey in foldercache foreach ($TempSyncCache['collections'] as $key => $value) { if (isset($value['synckey'])) { $collection = $value; $collection['collectionid'] = $key; if (isset($default_maxitems)) { $collection["maxitems"] = $default_maxitems; } $collection['onlyoptionbodypreference'] = $protocolversion >= 14.0 && (!isset($collection["BodyPreference"][1]) && !isset($collection["BodyPreference"][2]) && !isset($collection["BodyPreference"][3]) && !isset($collection["BodyPreference"][4])); $collection['syncstate'] = $statemachine->getSyncState($collection["synckey"]); if (isset($collection['optionfoldertype'])) { $collection[$collection['optionfoldertype'] . 'syncstate'] = $statemachine->getSyncState($collection['optionfoldertype'] . $collection["synckey"]); } if ($collection['synckey'] == "0") { debugLog('HandleSync: Here4 : Setting $msginfos[' . $collection['collectionid'] . '] to array()'); $msginfos[$collection['collectionid']] = array(); } else { if (!isset($msginfos[$collection['collectionid']])) { $msginfos[$collection['collectionid']] = unserialize($statemachine->getSyncState("mi" . $collection['synckey'])); } } if (isset($SyncCache['confirmed_synckeys'][$collection["synckey"]]) && (strlen($collection['syncstate']) == 0 || bin2hex(substr($collection['syncstate'], 4, 4)) == "00000000")) { debugLog("HandleSync: InitialSync determined for collection. No need to confirm this key! " . $collection["synckey"]); unset($SyncCache['confirmed_synckeys'][$collection["synckey"]]); } if ($collection['onlyoptionbodypreference'] === false && $collection['syncstate'] < 0 && strlen($collection['syncstate']) < 8 || isset($collection['optionfoldertype']) && $collection[$collection['optionfoldertype'] . 'syncstate'] < 0 && $collection[$collection['optionfoldertype'] . 'syncstate'] < 8) { _HandleSyncError("3"); debugLog("HandleSync: GetSyncState ERROR (Syncstate: " . abs($collection['syncstate']) . ") strlen=" . strlen($collection['syncstate'])); return true; } debugLog("HandleSync: Using SyncCache State for " . $TempSyncCache['folders'][$key]['displayname']); array_push($collections, $collection); } } unset($TempSyncCache); } else { // We got a full sync so we don't need to look after any confirmed synckey in array since device never the less only knows keys that it send now $SyncCache['confirmed_synckeys'] = array(); // Reset the lastuntil heartbeat/wait time to time now since this is the new base for the heartbeat $SyncCache['lastuntil'] = time(); // No Partial Sync so in this case we have to remove all synckeys to prevent syncs in these collections. foreach ($SyncCache['collections'] as $key => $value) { debugLog("HandleSync: Not a partial sync. Removing SyncCache[synckey] from collection " . $key); unset($SyncCache['collections'][$key]['synckey']); } } // Update the SyncCache with values from current collections foreach ($collections as $key => $value) { if (isset($value['collectionid'])) { if (isset($value['synckey'])) { debugLog("HandleSync: Adding SyncCache[synckey] from collection " . $value['collectionid']); $SyncCache['collections'][$value['collectionid']]['synckey'] = $value['synckey']; } if (isset($value["class"])) { $SyncCache['collections'][$value["collectionid"]]["class"] = $value["class"]; } if (isset($value["maxitems"])) { $SyncCache['collections'][$value["collectionid"]]["maxitems"] = $value["maxitems"]; } if (isset($value["deletesasmoves"])) { $SyncCache['collections'][$value["collectionid"]]["deletesasmoves"] = $value["deletesasmoves"]; } if (isset($value["conversationmode"])) { $SyncCache['collections'][$value["collectionid"]]["conversationmode"] = $value["conversationmode"]; } if (isset($value["filtertype"])) { $SyncCache['collections'][$value["collectionid"]]["filtertype"] = $value["filtertype"]; } if (isset($value["truncation"])) { $SyncCache['collections'][$value["collectionid"]]["truncation"] = $value["truncation"]; } if (isset($value["rtftruncation"])) { $SyncCache['collections'][$value["collectionid"]]["rtftruncation"] = $value["rtftruncation"]; } if (isset($value["mimesupport"])) { $SyncCache['collections'][$value["collectionid"]]["mimesupport"] = $value["mimesupport"]; } if (isset($value["mimetruncation"])) { $SyncCache['collections'][$value["collectionid"]]["mimetruncation"] = $value["mimetruncation"]; } if (isset($value["conflict"])) { $SyncCache['collections'][$value["collectionid"]]["conflict"] = $value["conflict"]; } if (isset($value["BodyPreference"])) { $SyncCache['collections'][$value["collectionid"]]["BodyPreference"] = $value["BodyPreference"]; } if (isset($value['optionfoldertype'])) { if (isset($value[$value['optionfoldertype']]["filtertype"])) { $SyncCache['collections'][$value["collectionid"]][$value['optionfoldertype']]["filtertype"] = $value[$value['optionfoldertype']]["filtertype"]; } if (isset($value[$value['optionfoldertype']]["BodyPreference"])) { $SyncCache['collections'][$value["collectionid"]][$value['optionfoldertype']]["BodyPreference"] = $value[$value['optionfoldertype']]["BodyPreference"]; } $SyncCache['collections'][$value["collectionid"]]['optionfoldertype'] = $value['optionfoldertype']; } } else { debugLog("HandleSync: Collection without collectionid found: " . print_r($value, true)); } } // End Update the synckeys in SyncCache if (!$decoder->getElementEndTag()) { // end sync return false; } // In case some synckeys didn't get confirmed by device we issue a full sync if (isset($SyncCache['confirmed_synckeys']) && sizeof($SyncCache['confirmed_synckeys']) > 0) { debugLog("Confirmed Synckeys contains: " . print_r($SyncCache['confirmed_synckeys'], true)); unset($SyncCache['confirmed_synckeys']); $statemachine->setSyncCache(serialize($SyncCache)); debugLog("HandleSync: Some SyncKeys didn't get confirmed. To ensure sync integrity we request a full request now (STATUS = " . SYNC_STATUS_REQUEST_INCOMPLETE . ")"); _HandleSyncError(SYNC_STATUS_REQUEST_INCOMPLETE); return true; } else { debugLog("HandleSync: All SyncKeys got confirmed. We continue here..."); $statemachine->setSyncCache(serialize($SyncCache)); } $i = 0; $statemachine->setSyncState('mi' . $collection['synckey'], isset($msginfos[$collection['collectionid']]) ? serialize($msginfos[$collection['collectionid']]) : serialize(array())); foreach ($collections as $key => $value) { if (isset($value['synckey'])) { $i++; } } if ($i == 0) { debugLog("HandleSync: We don't have any synckeys in collection. Request a full request now (STATUS = " . SYNC_STATUS_PROTOCOL_ERROR . ")"); _HandleSyncError(SYNC_STATUS_PROTOCOL_ERROR); return true; } } debugLog("HandleSync: SyncStatus is " . $SyncStatus . " hbinterval is " . $SyncCache['hbinterval']); if ($protocolversion >= 12.1 && $SyncStatus == 1 && $dataimported == false && ($SyncCache['wait'] !== false || $SyncCache['hbinterval'] !== false || $shortsyncreq === true)) { $dataavailable = false; $timeout = 5; if (isset($SyncCache['wait']) && $SyncCache['wait'] !== false) { $until = time() + $SyncCache['wait'] * 60; } else { if (isset($SyncCache['hbinterval']) && $SyncCache['hbinterval'] !== false) { $until = time() + $SyncCache['hbinterval']; } else { $until = time() + 10; } } debugLog("HandleSync: Looking for changes for " . ($until - time()) . " seconds"); $SyncCache['lastuntil'] = $until; $SyncCache['lasthbsyncstarted'] = time(); $statemachine->setSyncCache(serialize($SyncCache)); // Reading current state of the hierarchy state for determining changes during heartbeat/wait $hierarchystate = $statemachine->getSyncState($SyncCache['hierarchy']['synckey']); $hbrunavrgduration = 0; $hbrunmaxduration = 0; while (time() + $hbrunavrgduration < $until - $hbrunmaxduration) { $hbrunstarttime = microtime(true); // we try to find changes as long as time is lower than wait time // In case something changed in SyncCache regarding the folder hierarchy exit this function $TempSyncCache = unserialize($statemachine->getSyncCache()); if ($TempSyncCache === false) { debugLog("HandleSync: TempSyncCache could not be read and decoded, exiting here."); return true; } if ($TempSyncCache['timestamp'] > $SyncCache['timestamp']) { debugLog("HandleSync: Changes in cache determined during Sync Wait/Heartbeat, exiting here."); return true; } if (PROVISIONING === true) { $rwstatus = $backend->getDeviceRWStatus($user, $auth_pw, $devid); if ($rwstatus == SYNC_PROVISION_RWSTATUS_PENDING || $rwstatus == SYNC_PROVISION_RWSTATUS_WIPED) { //return 12 because it forces folder sync _HandleSyncError(SYNC_STATUS_FOLDER_HIERARCHY_CHANGED); return true; } } if (count($collections) == 0) { $error = 1; break; } for ($i = 0; $i < count($collections); $i++) { $collection = $collections[$i]; $class = $collection['onlyoptionbodypreference'] === false ? $collection["class"] : $collection["optionfoldertype"]; if ($class == "SMS" && !isset($collection['nextsmssync'])) { $collection['nextsmssync'] = 0; } unset($state); unset($exporter); if ($class != "SMS" || $class == "SMS" && $collection['nextsmssync'] < time()) { // Checking SMS Folders only once per 5 minutes for changes if ($class == "SMS") { $collections[$i]['nextsmssync'] = time() + 300; debugLog("HandleSync: SMS Items now being synceed"); } $state = $collection['syncstate']; $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]]["filtertype"] : 0); $optionfiltertype = isset($collection["optionfoldertype"]) ? isset($collection[$collection["optionfoldertype"]]["filtertype"]) ? $collection[$collection["optionfoldertype"]]["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["filtertype"] : 0) : 0; $collection['onlyoptionbodypreference'] = $protocolversion >= 14.0 && (!isset($collection["BodyPreference"][1]) && !isset($collection["BodyPreference"][2]) && !isset($collection["BodyPreference"][3]) && !isset($collection["BodyPreference"][4])); if ($collection['onlyoptionbodypreference'] === false) { $waitimporter = false; $exporter = $backend->GetExporter($collection["collectionid"]); $ret = $exporter->Config($waitimporter, $class, $filtertype, $state, BACKEND_DISCARD_DATA, 0, isset($collection["BodyPreference"]) ? $collection["BodyPreference"] : false, isset($collection["optionfoldertype"]) ? $collection[$collection["optionfoldertype"]]["BodyPreference"] : false); // stop heartbeat if exporter can not be configured (e.g. after Zarafa-server restart) if ($ret === false) { debugLog("HandleSync: Sync Wait/Heartbeat error: Exporter can not be configured. Waiting 30 seconds before sync is retried."); debugLog($collection["collectionid"]); sleep(30); } $changecount = $exporter->GetChangeCount(); if ($changecount > 0) { debugLog("HandleSync: Found " . $changecount . " change(s) in folder " . $SyncCache['folders'][$collection["collectionid"]]['displayname']); $dataavailable = true; $collections[$i]["getchanges"] = true; } // Discard any data while (is_array($exporter->Synchronize())) { } usleep(500000); } if (isset($collection['optionfoldertype'])) { $waitimporter = false; $state = $collection[$collection['optionfoldertype'] . 'syncstate']; $exporter = $backend->GetExporter($collection["collectionid"]); $ret = $exporter->Config($waitimporter, $collection['optionfoldertype'], $optionfiltertype, $state, BACKEND_DISCARD_DATA, 0, false, $collection[$collection["optionfoldertype"]]["BodyPreference"]); // stop heartbeat if exporter can not be configured (e.g. after Zarafa-server restart) if ($ret === false) { debugLog("HandleSync: Sync Wait/Heartbeat error: Exporter can not be configured. Waiting 30 seconds before sync is retried."); debugLog("HandleSync: optionfoldertype: " . $collection["collectionid"]); sleep(30); } $changecount = $exporter->GetChangeCount(); if ($changecount > 0) { debugLog("HandleSync: Found " . $changecount . " change(s) for optionfoldertype in folder " . $SyncCache['folders'][$collection["collectionid"]]['displayname']); $dataavailable = true; $collections[$i]["getchanges"] = true; } // Discard any data while (is_array($exporter->Synchronize())) { } usleep(500000); } } } if ($dataavailable) { debugLog("HandleSync: Found change"); break; } // Check for folder Updates $hierarchychanged = false; if ($hierarchystate >= 0 && !(strlen($hierarchystate) == 0) && !(bin2hex(substr($hierarchystate, 4, 4)) == "00000000")) { unset($exporter); $exporter = $backend->GetExporter(); $waitimporter = false; $exporter->Config($waitimporter, false, false, $hierarchystate, BACKEND_DISCARD_DATA, 0, false, false); if ($exporter->GetChangeCount() > 0) { $hierarchychanged = true; } while (is_array($exporter->Synchronize())) { } if ($hierarchychanged) { debugLog("HandleSync: Found hierarchy changes during Wait/Heartbeat Interval... Sending status " . SYNC_STATUS_FOLDER_HIERARCHY_CHANGED . " to get changes (STATUS = " . SYNC_STATUS_FOLDER_HIERARCHY_CHANGED . ")"); _HandleSyncError(SYNC_STATUS_FOLDER_HIERARCHY_CHANGED); return true; } } else { debugLog("HandleSync: Error in Syncstate during Wait/Heartbeat Interval... Sending status " . SYNC_STATUS_FOLDER_HIERARCHY_CHANGED . " to enforce hierarchy sync (STATUS = " . SYNC_STATUS_FOLDER_HIERARCHY_CHANGED . ")"); _HandleSyncError(SYNC_STATUS_FOLDER_HIERARCHY_CHANGED); return true; } // 5 seconds sleep to keep the load low... sleep($timeout); $hbrunthisduration = microtime(true) - $hbrunstarttime; if ($hbrunavrgduration > 0) { $hbrunavrgduration = ($hbrunavrgduration + $hbrunthisduration) / 2; } else { $hbrunavrgduration = $hbrunthisduration; } if ($hbrunthisduration > $hbrunmaxduration) { $hbrunmaxduration = $hbrunthisduration; } } debugLog("HandleSync: Max Heartbeat run duration is " . $hbrunmaxduration); debugLog("HandleSync: Average Heartbeat run duration is " . $hbrunavrgduration); // Even in case we found a change, better check that no other Sync already started... If so, // we exit here and let the other process do the export. $TempSyncCache = unserialize($statemachine->getSyncCache()); if ($TempSyncCache['timestamp'] > $SyncCache['timestamp']) { debugLog("HandleSync: Changes in cache determined during Sync Wait/Heartbeat, exiting here."); return true; } } // Do a short answer to allow short sync requests debugLog("HandleSync: dataavailable: " . ($dataavailable == true ? "Yes" : "No") . " dataimported: " . ($dataimported == true ? "Yes" : "No")); if ($protocolversion >= 12.1 && $SyncStatus == SYNC_STATUS_SUCCESS && $dataavailable == false && $dataimported == false && ($SyncCache['wait'] !== false || $SyncCache['hbinterval'] !== false)) { debugLog("HandleSync: Doing a short reply since no data is available, no data was imported and syncstatus is " . $SyncStatus); $SyncCache['lastsyncendnormal'] = time(); $statemachine->setSyncCache(serialize($SyncCache)); debugLog("HandleSync: Sync runtime = " . (microtime(true) - $sessionstarttime)); return true; } // So there was either a change in heartbeat or a normal sync request. // Lets go through all collections and set getchanges to true in case collection has a valid synckey and it is not set at all in collection // since if omitted it should be true according to spec // Since we don't want to get continuesly new sync keys for collections without changes we only set it to true in case a real change is being there... debugLog("Looking for collections not having the getChanges option being set"); foreach ($collections as $key => $collection) { if (isset($collection['synckey']) && $collection['synckey'] != '0' && !isset($collection['getchanges'])) { $state = $collection['syncstate']; $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]]["filtertype"] : 0); $optionfiltertype = isset($collection["optionfoldertype"]) ? isset($collection[$collection["optionfoldertype"]]["filtertype"]) ? $collection[$collection["optionfoldertype"]]["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["filtertype"] : 0) : 0; $collection['onlyoptionbodypreference'] = $protocolversion >= 14.0 && (!isset($collection["BodyPreference"][1]) && !isset($collection["BodyPreference"][2]) && !isset($collection["BodyPreference"][3]) && !isset($collection["BodyPreference"][4])); if ($collection['onlyoptionbodypreference'] === false) { $waitimporter = false; $exporter = $backend->GetExporter($collection['collectionid']); $ret = $exporter->Config($waitimporter, $collection['class'], $filtertype, $state, BACKEND_DISCARD_DATA, 0, isset($collection["BodyPreference"]) ? $collection["BodyPreference"] : false, isset($collection["optionfoldertype"]) ? $collection[$collection["optionfoldertype"]]["BodyPreference"] : false); $changecount = $exporter->GetChangeCount(); if ($changecount > 0) { $dataavailable = true; $collections[$key]['getchanges'] = true; } debugLog("HandleSync: Found " . $changecount . " change(s) for foldertype in folder " . $SyncCache['folders'][$collection["collectionid"]]['displayname']); // Discard any data while (is_array($exporter->Synchronize())) { } } // just take care about the optionfoldertype in case the main folder class a no changes... if (isset($collection['optionfoldertype']) && !isset($collections[$key]['getchanges'])) { $waitimporter = false; $state = $collection[$collection['optionfoldertype'] . 'syncstate']; $exporter = $backend->GetExporter($collection['collectionid']); $ret = $exporter->Config($waitimporter, $collection['optionfoldertype'], $optionfiltertype, $state, BACKEND_DISCARD_DATA, 0, false, $collection[$collection["optionfoldertype"]]["BodyPreference"]); $changecount = $exporter->GetChangeCount(); debugLog("HandleSync: Found " . $changecount . " change(s) for optionfoldertype in folder " . $SyncCache['folders'][$collection["collectionid"]]['displayname']); if ($changecount > 0) { $dataavailable = true; $collections[$key]["getchanges"] = true; } // Discard any data while (is_array($exporter->Synchronize())) { } } } } if ($dataimported == false && $fetchitems == false && ($SyncCache['wait'] === false && $SyncCache['hbinterval'] === false)) { unset($foundchange); foreach ($collections as $key => $collection) { if (isset($collection["getchanges"]) && $collection["getchanges"] != 0 && !isset($collection["importedchanges"]) && $collection["synckey"] != "0") { $foundchange = false; // Try to get the exporter. In case it is not possible (i.e. folder removed) set // status according. $exporter = $backend->GetExporter($collection["collectionid"]); debugLog("HandleSync: Exporter Value: " . is_object($exporter) . " " . (isset($exporter->exporter) ? $exporter->exporter : "")); $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]]["filtertype"] : 0); $optionfiltertype = isset($collection["optionfoldertype"]) ? isset($collection[$collection["optionfoldertype"]]["filtertype"]) ? $collection[$collection["optionfoldertype"]]["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["filtertype"] : 0) : 0; debugLog("HandleSync: FilterType GetChanges : " . $filtertype . " " . $optionfiltertype); $changecount = 0; if ($collection['onlyoptionbodypreference'] === false) { $exporter = $backend->GetExporter($collection["collectionid"]); debugLog("HandleSync: Messageclass for Export: " . $collection["class"]); $exporter->Config($importer[$collection["collectionid"]], $collection["class"], $filtertype, $collection['syncstate'], 0, $collection["truncation"], $collection["BodyPreference"], false, isset($collection["mimesupport"]) ? $collection['mimesupport'] : 0); $changecount = $exporter->GetChangeCount(); } // Optionfoldertype if (isset($collection['optionfoldertype'])) { $optionexporter = $backend->GetExporter($collection["collectionid"]); debugLog("HandleSync: Messageclass for Export: " . $collection["optionfoldertype"]); $optionexporter->Config($importer[$collection['optionfoldertype'] . $collection["collectionid"]], $collection['optionfoldertype'], $optionfiltertype, $collection[$collection['optionfoldertype'] . 'syncstate'], 0, 9, false, $collection[$collection["optionfoldertype"]]["BodyPreference"], isset($collection["mimesupport"]) ? $collection['mimesupport'] : 0); $changecount = $changecount + $optionexporter->GetChangeCount(); } $collections[$key]['changecount'] = $changecount; if ($changecount > 0) { $foundchange = true; } } } if (isset($foundchange) && $foundchange == false) { if ($protocolversion >= 14.0) { debugLog("HandleSync: No changes although devices requested them. Exit silently!"); // E2K10 Behaviour! Undocumented in Open Protocols! return true; } } } $encoder = new WBXMLEncoder($output, $zpushdtd); $encoder->startWBXML(); $answerstarttime = microtime(true); $encoder->startTag(SYNC_SYNCHRONIZE); if ($protocolversion >= 14.0) { $encoder->startTag(SYNC_STATUS); $encoder->content($SyncStatus); $encoder->endTag(); } $encoder->startTag(SYNC_FOLDERS); foreach ($collections as $collection) { // START ADDED dw2412 Protocol Version 12 Support if (isset($collection["BodyPreference"])) { $encoder->_bodypreference = $collection["BodyPreference"]; } // END ADDED dw2412 Protocol Version 12 Support $folderstatus = 1; // dw2412 ensure that no older exporter definition exists and could be used // figthing against that some folder get content of another folder... if (isset($exporter)) { unset($exporter); } if (isset($optionexporter)) { unset($optionexporter); } if (isset($collection["getchanges"]) && $collection["getchanges"] != 0) { if (isset($collection['optionfoldertype'])) { $optionexporter = $backend->GetExporter($collection["collectionid"]); debugLog("HandleSync: OptionExporter Value: " . is_object($optionexporter) . " " . (isset($optionexporter->exporter) ? $optionexporter->exporter : "")); } if (isset($exporter->exporter) && $exporter->exporter === false || isset($optionexporter->exporter) && $optionexporter->exporter === false) { $folderstatus = SYNC_STATUS_OBJECT_NOT_FOUND; } } // Get a new sync key to output to the client if any changes have been requested or have been sent if (isset($collection["importedchanges"]) || isset($collection["getchanges"]) && $collection["getchanges"] != 0 || $collection["synckey"] == "0") { if (isset($collection['changecount']) && $collection['changecount'] > 0) { $collection["newsynckey"] = $statemachine->getNewSyncKey($collection["synckey"]); debugLog("HandleSync: New Synckey generated because importedchanges: " . isset($collection["importedchanges"]) . " getchanges: " . (isset($collection["getchanges"]) && $collection["getchanges"] != 0) . " changecount: " . $collection['changecount'] . " initialsync: " . ($collection["synckey"] == "0")); } else { if (!isset($collection['changecount'])) { $collection["newsynckey"] = $statemachine->getNewSyncKey($collection["synckey"]); debugLog("HandleSync: New Synckey generated because importedchanges: " . isset($collection["importedchanges"]) . " getchanges: " . (isset($collection["getchanges"]) && $collection["getchanges"] != 0) . " initialsync: " . ($collection["synckey"] == "0")); } } } $encoder->startTag(SYNC_FOLDER); // FolderType/Class is only being returned by AS up to 12.0. // In 12.1 it could break the sync. if (isset($collection["class"]) && $protocolversion <= 12.0) { $encoder->startTag(SYNC_FOLDERTYPE); $encoder->content($collection["class"]); $encoder->endTag(); } $encoder->startTag(SYNC_SYNCKEY); if (isset($collection["newsynckey"])) { $encoder->content($collection["newsynckey"]); } else { $encoder->content($collection["synckey"]); } $encoder->endTag(); $encoder->startTag(SYNC_FOLDERID); $encoder->content($collection["collectionid"]); $encoder->endTag(); $encoder->startTag(SYNC_STATUS); $encoder->content($folderstatus); $encoder->endTag(); //check the mimesupport because we need it for advanced emails $mimesupport = isset($collection['mimesupport']) ? $collection['mimesupport'] : 0; // Output server IDs for new items we received from the PDA if (isset($collection["clientids"]) || isset($collection["fetchids"]) && count($collection["fetchids"]) > 0 || isset($collection["changeids"])) { $encoder->startTag(SYNC_REPLIES); /* if (isset($collection["changeids"])) { foreach($collection["changeids"] as $serverid => $servervals) { $encoder->startTag(SYNC_MODIFY); if (isset($servervals['optionfoldertype'])) { $encoder->startTag(SYNC_FOLDERTYPE); $encoder->content($collection['optionfoldertype']); $encoder->endTag(); } $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($serverid); $encoder->endTag(); $encoder->startTag(SYNC_STATUS); $encoder->content($servervals['status']); $encoder->endTag(); $encoder->endTag(); } } */ foreach ($collection["clientids"] as $clientid => $servervals) { $encoder->startTag(SYNC_ADD); if (isset($clientid['optionfoldertype']) && is_array($servervals['serverid'])) { $encoder->startTag(SYNC_FOLDERTYPE); $encoder->content($collection['optionfoldertype']); $encoder->endTag(); } $encoder->startTag(SYNC_CLIENTENTRYID); $encoder->content($clientid); $encoder->endTag(); if (is_array($servervals['serverid'])) { $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($servervals['serverid']['sourcekey']); $encoder->endTag(); } else { $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($servervals['serverid']); $encoder->endTag(); } $encoder->startTag(SYNC_STATUS); $encoder->content(1); $encoder->endTag(); if (is_array($servervals['serverid'])) { $encoder->startTag(SYNC_DATA); $encoder->startTag(SYNC_POOMMAIL2_CONVERSATIONID); $encoder->contentopaque($servervals['serverid']['convid']); $encoder->endTag(); $encoder->startTag(SYNC_POOMMAIL2_CONVERSATIONINDEX); $encoder->contentopaque($servervals['serverid']['convidx']); $encoder->endTag(); $encoder->endTag(); } $encoder->endTag(); } foreach ($collection["fetchids"] as $id) { // CHANGED dw2412 to support bodypreference $data = $backend->Fetch($collection["collectionid"], $id, isset($collection["BodyPreference"]) ? $collection["BodyPreference"] : false, isset($collection["optionfoldertype"]) ? $collection[$collection["optionfoldertype"]]["BodyPreference"] : false, $mimesupport); if ($data !== false) { $encoder->startTag(SYNC_FETCH); $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($id); $encoder->endTag(); $encoder->startTag(SYNC_STATUS); $encoder->content(1); $encoder->endTag(); $encoder->startTag(SYNC_DATA); $data->encode($encoder); $encoder->endTag(); $encoder->endTag(); } else { debugLog("HandleSync: unable to fetch {$id}"); } } $encoder->endTag(); } if (isset($collection["getchanges"]) && $collection["getchanges"] != 0 || isset($collection["readids"]) || isset($collection["flagids"]) || isset($collection['optionfoldertype']) && (isset($collection[$collection['optionfoldertype'] . "readids"]) || isset($collection[$collection['optionfoldertype'] . "flagids"]))) { // Use the state from the importer, as changes may have already happened $filtertype = isset($collection["filtertype"]) ? $collection["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]]["filtertype"] : 0); $optionfiltertype = isset($collection["optionfoldertype"]) ? isset($collection[$collection["optionfoldertype"]]["filtertype"]) ? $collection[$collection["optionfoldertype"]]["filtertype"] : (isset($SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["filtertype"]) ? $SyncCache['collections'][$collection["collectionid"]][$collection["optionfoldertype"]]["filtertype"] : 0) : 0; debugLog("HandleSync: FilterType GetChanges : " . $filtertype . " " . $optionfiltertype); $changecount = 0; if ($collection['onlyoptionbodypreference'] === false) { $exporter = $backend->GetExporter($collection["collectionid"]); debugLog("HandleSync: Messageclass for Export: " . $collection["class"]); $exporter->Config($importer[$collection["collectionid"]], $collection["class"], $filtertype, $collection['syncstate'], 0, $collection["truncation"], $collection["BodyPreference"], false, isset($collection["mimesupport"]) ? $collection['mimesupport'] : 0); $changecount = $exporter->GetChangeCount(); } // Optionfoldertype if (isset($collection['optionfoldertype'])) { $optionexporter = $backend->GetExporter($collection["collectionid"]); debugLog("HandleSync: Messageclass for Export: " . $collection["optionfoldertype"]); $optionexporter->Config($importer[$collection['optionfoldertype'] . $collection["collectionid"]], $collection['optionfoldertype'], $optionfiltertype, $collection[$collection['optionfoldertype'] . 'syncstate'], 0, 9, false, $collection[$collection["optionfoldertype"]]["BodyPreference"], isset($collection["mimesupport"]) ? $collection['mimesupport'] : 0); $changecount = $changecount + $optionexporter->GetChangeCount(); } debugLog("HandleSync: Changecount vs maxitems: " . $changecount . " " . $collection["maxitems"]); if ($changecount > $collection["maxitems"]) { $encoder->startTag(SYNC_MOREAVAILABLE, false, true); } // Output message changes per folder $encoder->startTag(SYNC_PERFORM); $n = 0; // Stream the changes to the PDA if ($collection['onlyoptionbodypreference'] === false) { $ids = array("readids" => isset($collection["readids"]) ? $collection["readids"] : array(), "flagids" => isset($collection["flagids"]) ? $collection["flagids"] : array()); $importer[$collection["collectionid"]] = new ImportContentsChangesStream($encoder, GetObjectClassFromFolderClass($collection["class"]), $ids, $msginfos[$collection["collectionid"]]); while (1) { $progress = $exporter->Synchronize(); if (!is_array($progress)) { break; } if ($importer[$collection["collectionid"]]->_lastObjectStatus == 1) { $n++; } debugLog("HandleSync: _lastObjectStatus = " . $importer[$collection["collectionid"]]->_lastObjectStatus); if ($n >= $collection["maxitems"]) { debugLog("HandleSync: Exported maxItems of messages: " . $collection["maxitems"] . " - more available"); break; } } $msginfos[$collection["collectionid"]] = $importer[$collection["collectionid"]]->_msginfos; } if (isset($collection['optionfoldertype'])) { $ids = array("readids" => isset($collection[$collection['optionfoldertype'] . "readids"]) ? $collection[$collection['optionfoldertype'] . "readids"] : array(), "flagids" => isset($collection[$collection['optionfoldertype'] . "flagids"]) ? $collection[$collection['optionfoldertype'] . "flagids"] : array()); $importer[$collection['optionfoldertype'] . $collection["collectionid"]] = new ImportContentsChangesStream($encoder, GetObjectClassFromFolderClass($collection["optionfoldertype"]), $ids, $msginfos[$collection["collectionid"]]); while (1) { $progress = $optionexporter->Synchronize(); if (!is_array($progress)) { break; } if ($importer[$collection['optionfoldertype'] . $collection["collectionid"]]->_lastObjectStatus == 1) { $n++; } debugLog("HandleSync: _lastObjectStatus = " . $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->_lastObjectStatus); if ($n >= $collection["maxitems"]) { debugLog("HandleSync: Exported maxItems of messages: " . $collection["maxitems"] . " - more available"); break; } } $msginfos[$collection["collectionid"]] = $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->_msginfos; } // START HACK: CURRENT ICS EXPORTER DOES NOT PROVIDE READ STATE AND FLAG UPDATES IF SEND FROM DEVICE. THIS WE DO HERE JUST BECAUSE OF THIS! if ($collection['onlyoptionbodypreference'] === false) { $array_rf = array_unique(array_merge(array_keys(isset($importer[$collection["collectionid"]]) ? $importer[$collection["collectionid"]]->_readids : array()), array_keys(isset($importer[$collection["collectionid"]]) ? $importer[$collection["collectionid"]]->_flagids : array()))); debugLog("HandleSync: After Exporting Changes we still have following array_rf in importer: " . print_r($array_rf, true)); $class = GetObjectClassFromFolderClass($collection["class"]); foreach ($array_rf as $rfid) { $encoder->startTag(SYNC_MODIFY); if (!isset($msginfos[$collection["collectionid"]][$rfid])) { unset($importer[$collection["collectionid"]]->_readids[$rfid]); unset($importer[$collection["collectionid"]]->_flagids[$rfid]); $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($rfid); $encoder->endTag(); $encoder->startTag(SYNC_STATUS); $encoder->content(SYNC_STATUS_OBJECT_NOT_FOUND); $encoder->endTag(); $encoder->endTag(); continue; } if ($msginfos[$collection["collectionid"]][$rfid]['class'] != $class) { continue; } $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($rfid); $encoder->endTag(); $encoder->startTag(SYNC_DATA); if (isset($importer[$collection["collectionid"]]->_readids[$rfid]) && $importer[$collection["collectionid"]]->_readids[$rfid]['status'] == true) { $encoder->startTag(SYNC_POOMMAIL_READ); $encoder->content($importer[$collection["collectionid"]]->_readids[$rfid]['data']); $encoder->endTag(); unset($importer[$collection["collectionid"]]->_readids[$rfid]); } if (isset($importer[$collection["collectionid"]]->_flagids[$rfid]) && $importer[$collection["collectionid"]]->_flagids[$rfid]['status'] == true) { if (!isset($importer[$collection["collectionid"]]->_flagids[$rfid]['data']->flagstatus) || $importer[$collection["collectionid"]]->_flagids[$rfid]['data']->flagstatus == 0 || $importer[$collection["collectionid"]]->_flagids[$rfid]['data']->flagstatus == "") { $encoder->startTag(SYNC_POOMMAIL_FLAG, false, true); } else { $encoder->startTag(SYNC_POOMMAIL_FLAG); $importer[$collection["collectionid"]]->_flagids[$rfid]['data']->encode($importer[$collection["collectionid"]]->_encoder); $encoder->endTag(); } unset($importer[$collection["collectionid"]]->_flagids[$rfid]); } $encoder->endTag(); $encoder->endTag(); } unset($array_rf); $array_rf = array_keys(array_merge(isset($importer[$collection["collectionid"]]) ? $importer[$collection["collectionid"]]->_readids : array(), isset($importer[$collection["collectionid"]]) ? $importer[$collection["collectionid"]]->_flagids : array())); debugLog("HandleSync: After manual export of read and flag changes we still have following array_rf in importer: " . print_r($array_rf, true)); unset($array_rf); } if (isset($collection["optionfoldertype"])) { $array_rf = array_unique(array_merge(array_keys(isset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]) ? $importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids : array()), array_keys(isset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]) ? $importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_readids : array()))); debugLog("HandleSync: After Exporting Changes we still have following array_rf in importer for optionfoldertype: " . print_r($array_rf, true)); $class = GetObjectClassFromFolderClass($collection["optionfoldertype"]); foreach ($array_rf as $rfid) { $encoder->startTag(SYNC_MODIFY); $encoder->startTag(SYNC_FOLDERTYPE); $encoder->content($collection["optionfoldertype"]); $encoder->endTag(); if (!isset($msginfos[$collection["collectionid"]][$rfid])) { unset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_readids[$rfid]); unset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids[$rfid]); $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($rfid); $encoder->endTag(); $encoder->startTag(SYNC_STATUS); $encoder->content(SYNC_STATUS_OBJECT_NOT_FOUND); $encoder->endTag(); $encoder->endTag(); continue; } if ($msginfos[$collection["collectionid"]][$rfid]['class'] != $class) { continue; } $encoder->startTag(SYNC_SERVERENTRYID); $encoder->content($rfid); $encoder->endTag(); $encoder->startTag(SYNC_DATA); if (isset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_readids[$rfid]) && $importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_readids[$rfid]['status'] == true) { $encoder->startTag(SYNC_POOMMAIL_READ); $encoder->content($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_readids[$rfid]['data']); $encoder->endTag(); unset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_readids[$rfid]); } if (isset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids[$rfid]) && $importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids[$rfid]['status'] == true) { if (!isset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids[$rfid]['data']->flagstatus) || $importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids[$rfid]['data']->flagstatus == 0 || $importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids[$rfid]['data']->flagstatus == "") { $encoder->startTag(SYNC_POOMMAIL_FLAG, false, true); } else { $encoder->startTag(SYNC_POOMMAIL_FLAG); $importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids[$rfid]['data']->encode($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_encoder); $encoder->endTag(); } unset($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids[$rfid]); } $encoder->endTag(); $encoder->endTag(); } unset($array_rf); $array_rf = array_keys(array_merge($importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_readids, $importer[$collection["optionfoldertype"] . $collection["collectionid"]]->_flagids)); debugLog("HandleSync: After manual export of read and flag changes we still have following array_rf in importer for optionfoldertype: " . print_r($array_rf, true)); unset($array_rf); } // END HACK: CURRENT ICS EXPORTER DOES NOT PROVIDE READ STATE AND FLAG UPDATES IF SEND FROM DEVICE. THIS WE DO HERE JUST BECAUSE OF THIS! $encoder->endTag(); } $encoder->endTag(); // Save the sync state for the next time if (isset($collection["newsynckey"])) { unset($state); unset($optionstate); if (isset($exporter) && $exporter) { $state = $exporter->GetState(); } else { if (isset($importer[$collection["collectionid"]]) && $importer[$collection["collectionid"]]) { $state = $importer[$collection["collectionid"]]->GetState(); } else { if ($collection["synckey"] == "0") { $state = ""; } } } if (isset($optionexporter) && $optionexporter) { $optionstate = $optionexporter->GetState(); } else { if (isset($collection['optionfoldertype']) && isset($importer[$collection['optionfoldertype'] . $collection["collectionid"]]) && is_object($importer[$collection['optionfoldertype'] . $collection["collectionid"]])) { $optionstate = $importer[$collection['optionfoldertype'] . $collection["collectionid"]]->GetState(); } else { if ($collection["synckey"] == "0") { $optionstate = ""; } } } if (isset($state)) { $statemachine->setSyncState($collection["newsynckey"], $state); } else { debugLog("HandleSync: error saving " . $collection["newsynckey"] . " - no state information available"); } if (isset($optionstate) && isset($collection['optionfoldertype'])) { $statemachine->setSyncState($collection['optionfoldertype'] . $collection["newsynckey"], $optionstate); } else { if (isset($collection['optionfoldertype'])) { debugLog("HandleSync: error saving " . $collection['optionfoldertype'] . $collection["newsynckey"] . " - no state information available"); } } if (trim($collection['newsynckey']) != trim($collection['synckey'])) { debugLog("HandleSync: Current Synckey: " . $collection['synckey'] . " New Synckey: " . $collection['newsynckey']); $SyncCache['confirmed_synckeys'][$collection['newsynckey']] = true; $statemachine->setSyncState('mi' . $collection['newsynckey'], isset($msginfos[$collection['collectionid']]) ? serialize($msginfos[$collection['collectionid']]) : serialize(array())); } } if (isset($collection['collectionid'])) { if (isset($collection['newsynckey'])) { $SyncCache['collections'][$collection['collectionid']]['synckey'] = $collection['newsynckey']; } else { $SyncCache['collections'][$collection['collectionid']]['synckey'] = $collection['synckey']; } if (isset($collection['class'])) { $SyncCache['collections'][$collection['collectionid']]['class'] = $collection['class']; } if (isset($collection['maxitems'])) { $SyncCache['collections'][$collection['collectionid']]['maxitems'] = $collection['maxitems']; } if (isset($collection['deletesasmoves'])) { $SyncCache['collections'][$collection['collectionid']]['deletesasmoves'] = $collection['deletesasmoves']; } if (isset($collection['conversationmode'])) { $SyncCache['collections'][$collection['collectionid']]['conversationmode'] = $collection['conversationmode']; } if (isset($SyncCache['collections'][$collection['collectionid']]['getchanges'])) { unset($SyncCache['collections'][$collection['collectionid']]['getchanges']); } if (isset($collection['filtertype'])) { $SyncCache['collections'][$collection['collectionid']]['filtertype'] = $collection['filtertype']; } if (isset($collection['truncation'])) { $SyncCache['collections'][$collection['collectionid']]['truncation'] = $collection['truncation']; } if (isset($collection['rtftruncation'])) { $SyncCache['collections'][$collection['collectionid']]['rtftruncation'] = $collection['rtftruncation']; } if (isset($collection['mimesupport'])) { $SyncCache['collections'][$collection['collectionid']]['mimesupport'] = $collection['mimesupport']; } if (isset($collection['mimetruncation'])) { $SyncCache['collections'][$collection['collectionid']]['mimetruncation'] = $collection['mimetruncation']; } if (isset($collection['conflict'])) { $SyncCache['collections'][$collection['collectionid']]['conflict'] = $collection['conflict']; } if (isset($collection['BodyPreference'])) { $SyncCache['collections'][$collection['collectionid']]['BodyPreference'] = $collection['BodyPreference']; } if (isset($collection['optionfoldertype'])) { $SyncCache['collections'][$collection['collectionid']]['optionfoldertype'] = $collection['optionfoldertype']; if (isset($collection[$collection['optionfoldertype']]['filtertype'])) { $SyncCache['collections'][$collection['collectionid']][$collection['optionfoldertype']]['filtertype'] = $collection[$collection['optionfoldertype']]['filtertype']; } if (isset($collection[$collection['optionfoldertype']]['BodyPreference'])) { $SyncCache['collections'][$collection['collectionid']][$collection['optionfoldertype']]['BodyPreference'] = $collection[$collection['optionfoldertype']]['BodyPreference']; } } } } $encoder->endTag(); $encoder->endTag(); debugLog("HandleSync: Answer prepare duration run " . (microtime(true) - $answerstarttime)); $TempSyncCache = unserialize($statemachine->getSyncCache()); if (isset($SyncCache['timestamp']) && $TempSyncCache['timestamp'] > $SyncCache['timestamp']) { debugLog("HandleSync: Changes in cache determined during Sync Wait/Heartbeat, exiting here. SyncCache not updated!"); debugLog("HandleSync: Sync runtime = " . (microtime(true) - $sessionstarttime)); return true; } else { $SyncCache['lastsyncendnormal'] = time(); $statemachine->setSyncCache(serialize($SyncCache)); debugLog("HandleSync: Sync runtime = " . (microtime(true) - $sessionstarttime)); } return true; }