Пример #1
0
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;
}