Пример #1
0
function HandleFolderSync($backend, $protocolversion)
{
    global $zpushdtd;
    global $input, $output;
    // Maps serverid -> clientid for items that are received from the PIM
    $map = array();
    $decoder = new WBXMLDecoder($input, $zpushdtd);
    $encoder = new WBXMLEncoder($output, $zpushdtd);
    // Parse input
    if (!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC)) {
        return false;
    }
    if (!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) {
        return false;
    }
    $synckey = $decoder->getElementContent();
    if (!$decoder->getElementEndTag()) {
        return false;
    }
    if ($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
        // Ignore <Count> if present
        if ($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
            $decoder->getElementContent();
            if (!$decoder->getElementEndTag()) {
                return false;
            }
        }
        // Process the changes (either <Add>, <Modify>, or <Remove>)
        $element = $decoder->getElement();
        if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
            return false;
        }
        while (1) {
            $folder = new SyncFolder();
            if (!$folder->decode($decoder)) {
                break;
            }
            switch ($element[EN_TAG]) {
                case SYNC_ADD:
                case SYNC_MODIFY:
                    $serverid = $backend->folderimporter->ImportFolderChange($folder);
                    break;
                case SYNC_REMOVE:
                    $serverid = $backend->folderimporter->ImportFolderDeletion($folder);
                    break;
            }
            if ($serverid) {
                $map[$serverid] = $folder->clientid;
            }
        }
        if (!$decoder->getElementEndTag()) {
            return false;
        }
    }
    if (!$decoder->getElementEndTag()) {
        return false;
    }
    // We have processed incoming foldersync requests, now send the PIM
    // our changes
    // First, get the syncstate that is associated with this synckey
    $statemachine = new StateMachine();
    // The state machine will discard any sync states before this one, as they are no
    // longer required
    $syncstate = $statemachine->getSyncState($synckey);
    // We will be saving the sync state under 'newsynckey'
    $newsynckey = $statemachine->getNewSyncKey($synckey);
    // The MemImporter caches all imports in-memory, so we can send a change count
    // before sending the actual data. As the amount of data done in this operation
    // is rather low, this is not memory problem. Note that this is not done when
    // sync'ing messages - we let the exporter write directly to WBXML.
    $importer = new ImportHierarchyChangesMem($encoder);
    // Request changes from backend, they will be sent to the MemImporter passed as the first
    // argument, which stores them in $importer. Returns the new sync state for this exporter.
    $exporter = $backend->GetExporter();
    $exporter->Config($importer, false, false, $syncstate, 0, 0);
    while (is_array($exporter->Synchronize())) {
    }
    // Output our WBXML reply now
    $encoder->StartWBXML();
    $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
    $encoder->startTag(SYNC_FOLDERHIERARCHY_ERROR);
    $encoder->content(1);
    $encoder->endTag();
    $encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
    $encoder->content($newsynckey);
    $encoder->endTag();
    $encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
    $encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
    $encoder->content($importer->count);
    $encoder->endTag();
    if (count($importer->changed) > 0) {
        foreach ($importer->changed as $folder) {
            $encoder->startTag(SYNC_FOLDERHIERARCHY_ADD);
            $folder->encode($encoder);
            $encoder->endTag();
        }
    }
    if (count($importer->deleted) > 0) {
        foreach ($importer->deleted as $folder) {
            $encoder->startTag(SYNC_FOLDERHIERARCHY_REMOVE);
            $encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
            $encoder->content($folder);
            $encoder->endTag();
            $encoder->endTag();
        }
    }
    $encoder->endTag();
    $encoder->endTag();
    // Save the sync state for the next time
    $syncstate = $exporter->GetState();
    $statemachine->setSyncState($newsynckey, $syncstate);
    return true;
}
Пример #2
0
function HandleFolderSync($backend, $protocolversion)
{
    global $zpushdtd;
    global $input, $output;
    // Maps serverid -> clientid for items that are received from the PIM
    $map = array();
    $decoder = new WBXMLDecoder($input, $zpushdtd);
    $encoder = new WBXMLEncoder($output, $zpushdtd);
    // Parse input
    if (!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC)) {
        return false;
    }
    if (!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) {
        return false;
    }
    $synckey = $decoder->getElementContent();
    if (!$decoder->getElementEndTag()) {
        return false;
    }
    // First, get the syncstate that is associated with this synckey
    $statemachine = new StateMachine();
    // The state machine will discard any sync states before this one, as they are no
    // longer required
    $syncstate = $statemachine->getSyncState($synckey);
    // additional information about already seen folders
    $sfolderstate = $statemachine->getSyncState("s" . $synckey);
    if (!$sfolderstate) {
        $foldercache = array();
        if ($sfolderstate === false) {
            debugLog("Error: FolderChacheState for state 's" . $synckey . "' not found. Reinitializing...");
        }
    } else {
        $foldercache = unserialize($sfolderstate);
        // transform old seenfolder array
        if (array_key_exists("0", $foldercache)) {
            $tmp = array();
            foreach ($foldercache as $s) {
                $tmp[$s] = new SyncFolder();
            }
            $foldercache = $tmp;
        }
    }
    // We will be saving the sync state under 'newsynckey'
    $newsynckey = $statemachine->getNewSyncKey($synckey);
    $changes = false;
    if ($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
        // Ignore <Count> if present
        if ($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
            $decoder->getElementContent();
            if (!$decoder->getElementEndTag()) {
                return false;
            }
        }
        // Process the changes (either <Add>, <Modify>, or <Remove>)
        $element = $decoder->getElement();
        if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
            return false;
        }
        while (1) {
            $folder = new SyncFolder();
            if (!$folder->decode($decoder)) {
                break;
            }
            // Configure importer with last state
            $importer = $backend->GetHierarchyImporter();
            $importer->Config($syncstate);
            switch ($element[EN_TAG]) {
                case SYNC_ADD:
                case SYNC_MODIFY:
                    $serverid = $importer->ImportFolderChange($folder);
                    // add folder to the serverflags
                    $foldercache[$serverid] = $folder;
                    $changes = true;
                    break;
                case SYNC_REMOVE:
                    $serverid = $importer->ImportFolderDeletion($folder);
                    $changes = true;
                    // remove folder from the folderchache
                    if (array_key_exists($serverid, $foldercache)) {
                        unset($foldercache[$serverid]);
                    }
                    break;
            }
            if ($serverid) {
                $map[$serverid] = $folder->clientid;
            }
        }
        if (!$decoder->getElementEndTag()) {
            return false;
        }
    }
    if (!$decoder->getElementEndTag()) {
        return false;
    }
    // We have processed incoming foldersync requests, now send the PIM
    // our changes
    // The MemImporter caches all imports in-memory, so we can send a change count
    // before sending the actual data. As the amount of data done in this operation
    // is rather low, this is not memory problem. Note that this is not done when
    // sync'ing messages - we let the exporter write directly to WBXML.
    $importer = new ImportHierarchyChangesMem($foldercache);
    // Request changes from backend, they will be sent to the MemImporter passed as the first
    // argument, which stores them in $importer. Returns the new sync state for this exporter.
    $exporter = $backend->GetExporter();
    $exporter->Config($importer, false, false, $syncstate, 0, 0);
    while (is_array($exporter->Synchronize())) {
    }
    // Output our WBXML reply now
    $encoder->StartWBXML();
    // Simple Public Folder Sync
    global $pubfolders;
    if (!empty($pubfolders)) {
        foreach ($pubfolders as $pf) {
            $publicf = new SyncFolder();
            $publicf->type = $pf['type'];
            $publicf->displayname = $pf['name'];
            $publicf->parentid = "0";
            $publicf->serverid = $pf['puid'];
            $publicf->nodebug = true;
            $importer->ImportFolderChange($publicf);
        }
    }
    $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
    $encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
    $encoder->content(1);
    $encoder->endTag();
    $encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
    // only send new synckey if changes were processed or there are outgoing changes
    $encoder->content($changes || $importer->count > 0 ? $newsynckey : $synckey);
    $encoder->endTag();
    $encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
    $encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
    $encoder->content($importer->count);
    $encoder->endTag();
    if (count($importer->changed) > 0) {
        foreach ($importer->changed as $folder) {
            // send a modify flag if the folder is already known on the device
            if (isset($folder->serverid) && array_key_exists($folder->serverid, $foldercache) !== false) {
                $encoder->startTag(SYNC_FOLDERHIERARCHY_UPDATE);
            } else {
                $encoder->startTag(SYNC_FOLDERHIERARCHY_ADD);
            }
            $foldercache[$folder->serverid] = $folder;
            $folder->encode($encoder);
            $encoder->endTag();
        }
    }
    if (count($importer->deleted) > 0) {
        foreach ($importer->deleted as $folder) {
            $encoder->startTag(SYNC_FOLDERHIERARCHY_REMOVE);
            $encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
            $encoder->content($folder);
            $encoder->endTag();
            $encoder->endTag();
            // remove folder from the folderchache
            if (array_key_exists($folder, $foldercache)) {
                unset($foldercache[$folder]);
            }
        }
    }
    $encoder->endTag();
    $encoder->endTag();
    // Save the sync state for the next time
    $syncstate = $exporter->GetState();
    $statemachine->setSyncState($newsynckey, $syncstate);
    $statemachine->setSyncState("s" . $newsynckey, serialize($foldercache));
    return true;
}
Пример #3
0
function HandleFolderSync($backend, $devid, $protocolversion)
{
    global $zpushdtd;
    global $input, $output;
    global $useragent;
    global $user;
    // Maps serverid -> clientid for items that are received from the PIM
    $map = array();
    $decoder = new WBXMLDecoder($input, $zpushdtd);
    $encoder = new WBXMLEncoder($output, $zpushdtd);
    // Parse input
    if (!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC)) {
        return false;
    }
    if (!$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) {
        return false;
    }
    $synckey = $decoder->getElementContent();
    if (!$decoder->getElementEndTag()) {
        return false;
    }
    // First, get the syncstate that is associated with this synckey
    $statemachine = new StateMachine($devid, $user);
    // The state machine will discard any sync states before this one, as they are no
    // longer required
    $syncstate = $statemachine->getSyncState($synckey);
    // Get Foldercache
    $SyncCache = unserialize($statemachine->getSyncCache());
    if (isset($SyncCache['folders']) && is_array($SyncCache['folders'])) {
        foreach ($SyncCache['folders'] as $key => $value) {
            if (!isset($value['class'])) {
                $statemachine->deleteSyncCache();
                _ErrorHandleFolderSync("9");
                return true;
            }
            $exporter = $backend->GetExporter($key);
            if (isset($exporter->exporter) && $exporter->exporter === false) {
                unset($SyncCache['folders'][$key]);
            }
        }
    }
    // additional information about already seen folders
    if ($synckey != "0") {
        $seenfolders = $statemachine->getSyncState("s" . $synckey);
    }
    // if we have any error with one of the requests bail out here!
    if ($synckey != "0" && is_numeric($seenfolders) && $seenfolders < 0 || is_numeric($syncstate) && $syncstate < 0) {
        // if we get a numeric syncstate back it means we have an error...
        debugLog("GetSyncState ERROR (Seenfolders: " . abs($seenfolders) . ", Syncstate: " . abs($syncstate) . ")");
        if ($seenfolders < 0) {
            $status = abs($seenfolders);
        }
        if ($syncstate < 0) {
            $status = abs($syncstate);
        }
        // Output our WBXML reply now
        _ErrorHandleFolderSync(abs($status));
        return true;
    } else {
        $foldercache = unserialize($statemachine->getSyncCache());
        // Clear the foldercache in SyncCache in case the SyncKey = 0
        if ($synckey == "0") {
            // $statemachine->deleteSyncCache();
            unset($foldercache['folders']);
            debugLog("Clean the folders in foldercache");
        }
        debugLog("GetSyncState OK");
    }
    if ($synckey == "0" && (!isset($seenfolders) || is_numeric($seenfolders) && $seenfolders < 0)) {
        $seenfolders = false;
    }
    $seenfolders = unserialize($seenfolders);
    if (!$seenfolders) {
        $seenfolders = array();
    }
    if (!$foldercache || !is_array($foldercache)) {
        $foldercache = array();
    }
    // lets clean the old state files away...
    if ($synckey != "0") {
        $statemachine->cleanOldSyncState("s" . $synckey);
        if (($delstatus = $statemachine->cleanOldSyncState($synckey)) !== true) {
            _ErrorHandleFolderSync(abs($delstatus));
            return true;
        }
    }
    // We will be saving the sync state under 'newsynckey'
    $newsynckey = $statemachine->getNewSyncKey($synckey);
    $changes = false;
    if ($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
        // Ignore <Count> if present
        if ($decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
            $decoder->getElementContent();
            if (!$decoder->getElementEndTag()) {
                return false;
            }
        }
        // Process the changes (either <Add>, <Modify>, or <Remove>)
        $element = $decoder->getElement();
        if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
            return false;
        }
        while (1) {
            $folder = new SyncFolder();
            if (!$folder->decode($decoder)) {
                break;
            }
            // Configure importer with last state
            $importer = $backend->GetHierarchyImporter();
            $importer->Config($syncstate);
            switch ($element[EN_TAG]) {
                case SYNC_ADD:
                case SYNC_MODIFY:
                    $serverid = $importer->ImportFolderChange($folder);
                    $statemachine->updateSyncCacheFolder($foldercache, $serverid, $folder->parentid, $folder->displayname, $folder->type);
                    // add folder to the serverflags
                    $seenfolders[] = $serverid;
                    $changes = true;
                    break;
                case SYNC_REMOVE:
                    $serverid = $importer->ImportFolderDeletion($folder);
                    // remove folder from the folderflags array
                    $changes = true;
                    if (($sid = array_search($serverid, $seenfolders)) !== false) {
                        unset($seenfolders[$sid]);
                        $seenfolders = array_values($seenfolders);
                    }
                    $statemachine->deleteSyncCacheFolder($foldercache, $serverid);
                    break;
            }
            if ($serverid) {
                $map[$serverid] = $folder->clientid;
            }
        }
        if (!$decoder->getElementEndTag()) {
            return false;
        }
    }
    if (!$decoder->getElementEndTag()) {
        return false;
    }
    // We have processed incoming foldersync requests, now send the PIM
    // our changes
    // The MemImporter caches all imports in-memory, so we can send a change count
    // before sending the actual data. As the amount of data done in this operation
    // is rather low, this is not memory problem. Note that this is not done when
    // sync'ing messages - we let the exporter write directly to WBXML.
    $importer = new ImportHierarchyChangesMem($encoder);
    // Request changes from backend, they will be sent to the MemImporter passed as the first
    // argument, which stores them in $importer. Returns the new sync state for this exporter.
    $exporter = $backend->GetExporter();
    $exporter->Config($importer, false, false, $syncstate, 0, 0, false, false);
    while (is_array($exporter->Synchronize())) {
    }
    // Output our WBXML reply now
    $encoder->StartWBXML();
    $encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
    $encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
    $encoder->content(1);
    $encoder->endTag();
    $encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
    $encoder->content($changes || $importer->count > 0 ? $newsynckey : $synckey);
    $foldercache['hierarchy']['synckey'] = $changes || $importer->count > 0 ? $newsynckey : $synckey;
    $encoder->endTag();
    $encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
    // remove unnecessary updates where details between cache and real folder are equal
    if (count($importer->changed) > 0) {
        foreach ($importer->changed as $key => $folder) {
            if (isset($folder->serverid) && in_array($folder->serverid, $seenfolders) && isset($foldercache['folders'][$folder->serverid]) && $foldercache['folders'][$folder->serverid]['parentid'] == $folder->parentid && $foldercache['folders'][$folder->serverid]['displayname'] == $folder->displayname && $foldercache['folders'][$folder->serverid]['type'] == $folder->type) {
                debugLog("Ignoring " . $folder->serverid . " from importer->changed because it is folder update requests!");
                unset($importer->changed[$key]);
                $importer->count--;
            }
        }
    }
    // remove unnecessary deletes where folders never got sent to the device
    if (count($importer->deleted) > 0) {
        foreach ($importer->deleted as $key => $folder) {
            if (($sid = array_search($folder, $seenfolders)) === false) {
                debugLog("Removing {$folder} from importer->deleted because sid {$sid} (not in seenfolders)!");
                unset($importer->deleted[$key]);
                $importer->count--;
            }
        }
    }
    $encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
    $encoder->content($importer->count);
    $encoder->endTag();
    if (count($importer->changed) > 0) {
        foreach ($importer->changed as $folder) {
            if (isset($folder->serverid) && in_array($folder->serverid, $seenfolders)) {
                $encoder->startTag(SYNC_FOLDERHIERARCHY_UPDATE);
            } else {
                $seenfolders[] = $folder->serverid;
                $encoder->startTag(SYNC_FOLDERHIERARCHY_ADD);
            }
            $statemachine->updateSyncCacheFolder($foldercache, $folder->serverid, $folder->parentid, $folder->displayname, $folder->type);
            $folder->encode($encoder);
            $encoder->endTag();
        }
    }
    if (count($importer->deleted) > 0) {
        foreach ($importer->deleted as $folder) {
            if (($sid = array_search($folder, $seenfolders)) !== false) {
                $encoder->startTag(SYNC_FOLDERHIERARCHY_REMOVE);
                $encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
                $encoder->content($folder);
                $encoder->endTag();
                $encoder->endTag();
                // remove folder from the folderflags array
                unset($seenfolders[$sid]);
                $statemachine->deleteSyncCacheFolder($foldercache, $folder);
                $seenfolders = array_values($seenfolders);
            } else {
                debugLog("Don't send {$folder} because sid {$sid} (not in seenfolders)!");
            }
        }
    }
    $encoder->endTag();
    $encoder->endTag();
    // Save the sync state for the next time
    $syncstate = $exporter->GetState();
    $statemachine->setSyncState($newsynckey, $syncstate);
    $statemachine->setSyncState("s" . $newsynckey, serialize($seenfolders));
    // Remove collections from foldercache for that no folder exists
    if (isset($foldercache['collections'])) {
        foreach ($foldercache['collections'] as $key => $value) {
            if (!isset($foldercache['folders'][$key])) {
                unset($foldercache['collections'][$key]);
            }
        }
    }
    $statemachine->setSyncCache(serialize($foldercache), true);
    return true;
}