示例#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;
}
示例#2
0
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;
}