Example #1
0
function get($lastmodified, $api)
{
    $sync = new Sync();
    $output = array();
    if ($lastmodified < $sync->get(Sync::USER)) {
        $output[Sync::USER] = getUser($api);
    }
    if ($lastmodified < $sync->get(Sync::SCENARIOS)) {
        $output[Sync::SCENARIOS] = getScenarios();
    }
    if ($lastmodified < $sync->get(Sync::LIGHTS)) {
        $output[Sync::LIGHTS] = getLights();
    }
    if ($lastmodified < $sync->get(Sync::ROOMS)) {
        $output[Sync::ROOMS] = getRooms();
    }
    if ($lastmodified < $sync->get(Sync::PLANTS)) {
        $output[Sync::PLANTS] = getPlants();
    }
    if ($lastmodified < $sync->get(Sync::DEVICES)) {
        $output[Sync::DEVICES] = getDevices();
    }
    if ($lastmodified < $sync->get(Sync::SENSORS)) {
        $output[Sync::SENSORS] = getSensors();
    }
    if ($lastmodified < $sync->get(Sync::EAN)) {
        $output[Sync::EAN] = getEan();
    }
    return $output;
}
Example #2
0
 public static function invokeService()
 {
     $request = file_get_contents('php://input');
     $request = json_decode($request);
     $userId = self::get_user_id($request->token);
     if (empty($userId)) {
         $response = new CliniqueServiceResponce();
         $response->response(true, 'Invalid user');
         die;
     }
     switch ($request->type) {
         case 'bookmark':
             Sync::syncBookmarks($userId, $request->data);
             break;
         case 'notes':
             Sync::syncNotes($userId, $request->data);
             break;
         case 'favorite':
             Sync::syncFavorites($userId, $request->data);
             break;
         case 'badges':
             Sync::syncBadges($userId, $request->data);
             break;
         case 'quiz':
             Sync::syncQuiz($userId, $request);
             break;
         case 'scorm':
             Sync::syncScorm($userId, $request);
             break;
         case 'completion':
             Sync::syncCompletion($userId, $request);
             break;
     }
 }
Example #3
0
 /**
  * Gets called when the script is exiting.
  * Sends out all sync records.
  */
 public static function exiting()
 {
     // Use all of the course records
     foreach (self::$coursesToSync as $course) {
         // Sync the course
         $course->perform_sync();
     }
     self::$coursesToSync = array();
 }
Example #4
0
 public function testSynchronizationFlow()
 {
     $session = ['id' => $this->sessionId, 'queues' => ['data' => ['name' => 'main', 'pages' => 1, 'total_count' => 2], 'meta' => ['type' => 'sync_queue']]];
     $queueItems = [['data' => ['id' => 1], 'meta' => ['type' => 'user', 'sync' => ['event_type' => 'created', 'ack_key' => 'User-1234-1', 'revision' => 1]]], ['data' => ['id' => 1], 'meta' => ['type' => 'source', 'sync' => ['event_type' => 'created', 'ack_key' => 'Source-1234-1', 'revision' => 1]]]];
     $ackKeys = array_map(function ($item) {
         return $item['meta']['sync']['ack_key'];
     }, $queueItems);
     $client = $this->getMockBuilder('\\BaseCRM\\Client')->disableOriginalConstructor()->getMock();
     $syncService = $this->getMockBuilder('\\BaseCRM\\SyncService')->disableOriginalConstructor()->getMock();
     $syncService->expects($this->once())->method('start')->with($this->deviceUUID)->will($this->returnValue($session));
     $syncService->expects($this->exactly(2))->method('fetch')->with($this->deviceUUID, $session['id'])->will($this->onConsecutiveCalls($queueItems, []));
     $syncService->expects($this->at(1))->method('fetch')->with($this->deviceUUID, $session['id'])->will($this->returnValue([]));
     $client->sync = $syncService;
     $sync = new Sync($client, $this->deviceUUID);
     $counter = 0;
     $sync->fetch(function ($meta, $data) use(&$counter) {
         $this->assertTrue(isset($meta['sync']));
         $this->assertEquals($data['id'], 1);
         $counter += 1;
     });
     $this->assertEquals($counter, 2);
 }
Example #5
0
function sync_controller()
{
    global $mysqli, $session, $route;
    include "Modules/feed/feed_model.php";
    $feed = new Feed($mysqli);
    include "Modules/sync/sync_model.php";
    $sync = new Sync($mysqli, $feed, $session['userid']);
    if ($route->format == 'html') {
        if ($route->action == "list" && $session['write']) {
            $result = view("Modules/sync/sync_view.php", array());
        }
    }
    if ($route->format == 'json') {
        // Register a feed to be downloaded
        if ($route->action == "feed" && $session['write']) {
            $result = $sync->add_feed($session['userid'], get('feedid'), get('name'), get('datatype'));
        }
        // Save remote url and apikey
        if ($route->action == "setsettings" && $session['write']) {
            $result = $sync->set_settings($session['userid'], get('remoteurl'), get('remotekey'));
        }
        // Save remote url and apikey
        if ($route->action == "getsettings" && $session['write']) {
            $result = $sync->get_settings($session['userid']);
        }
        // get the remote feed list, we want to load the remote feeds when the page is first loaded
        // but only queue progress updates there after
        if ($route->action == "getremotefeeds" && $session['write']) {
            $result = $sync->get_remote_feeds($session['userid']);
        }
        if ($route->action == "getimportqueue" && $session['write']) {
            $result = $sync->get_importqueue($session['userid']);
        }
        if ($route->action == "getlocalfeeds" && $session['write']) {
            $result = $sync->get_local_feeds($session['userid']);
        }
    }
    return array('content' => $result);
}
Example #6
0
 /**
  * Gets called when this entry changes.
  */
 public function changed()
 {
     Sync::course(Course::fromId($this->getCourseId()));
 }
Example #7
0
 public function testSettings()
 {
     $settingKey = 'tagColors';
     $value = array(array("name" => "_READ", "color" => "#990000"));
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $lastSyncTimestamp = (int) $xml['timestamp'];
     $libraryVersion = API::getLibraryVersion();
     // Create item via sync
     $data = '<data version="9"><settings><setting libraryID="' . self::$config['libraryID'] . '" name="' . $settingKey . '">' . htmlspecialchars(json_encode($value)) . '</setting></settings></data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Check via sync
     $xml = Sync::updated(self::$sessionID, $lastSyncTimestamp);
     $updateKey = (string) $xml['updateKey'];
     $lastSyncTimestamp = $xml['timestamp'];
     $settingXML = $xml->updated[0]->settings[0]->setting[0];
     $this->assertEquals(self::$config['libraryID'], (int) $settingXML['libraryID']);
     $this->assertEquals($settingKey, (string) $settingXML['name']);
     $this->assertEquals($value, json_decode((string) $settingXML, true));
     // Get setting via API and check value
     $response = API::userGet(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey']);
     $this->assertEquals(200, $response->getStatus());
     $json = json_decode($response->getBody(), true);
     $this->assertNotNull($json);
     $this->assertEquals($value, $json['value']);
     $this->assertEquals($libraryVersion + 1, $json['version']);
     // Delete via sync
     $xmlstr = '<data version="9">' . '<deleted>' . '<settings>' . '<setting libraryID="' . self::$config['libraryID'] . '" key="' . $settingKey . '"/>' . '</settings>' . '</deleted>' . '</data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr);
     $xml = Sync::waitForUpload(self::$sessionID, $response, $this);
     // Get setting via API and check value
     $response = API::userGet(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey']);
     $this->assertEquals(404, $response->getStatus());
     // Check for missing via sync
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $lastSyncTimestamp = $xml['timestamp'];
     $this->assertEquals(0, $xml->updated[0]->settings->count());
     $this->assertEquals(1, $xml->updated[0]->deleted[0]->settings[0]->setting->count());
     $this->assertEquals(self::$config['libraryID'], (int) $xml->updated[0]->deleted[0]->settings[0]->setting[0]['libraryID']);
     $this->assertEquals($settingKey, (string) $xml->updated[0]->deleted[0]->settings[0]->setting[0]['key']);
 }
Example #8
0
 private function _testDeleteAndDeleted($objectType)
 {
     API::userClear(self::$config['userID']);
     $objectTypePlural = API::getPluralObjectType($objectType);
     $xml = Sync::updated(self::$sessionID);
     $lastSyncTimestamp = (int) $xml['timestamp'];
     // Create via sync
     switch ($objectType) {
         case 'item':
             $keys[] = Sync::createItem(self::$sessionID, self::$config['libraryID'], "book", false, $this);
             break;
         case 'setting':
             $settingKey = "tagColors";
             $response = API::userPut(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey'], json_encode(array("value" => array(array("name" => "_READ", "color" => "#990000")))), array("Content-Type: application/json", "If-Unmodified-Since-Version: 0"));
             $this->assertEquals(204, $response->getStatus());
             $keys[] = $settingKey;
             break;
     }
     // Check via API
     foreach ($keys as $key) {
         $response = API::userGet(self::$config['userID'], "{$objectTypePlural}/{$key}?key=" . self::$config['apiKey']);
         $this->assertEquals(200, $response->getStatus());
         $version = $response->getHeader("Last-Modified-Version");
         $this->assertNotNull($version);
     }
     // Get empty deleted via API
     $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newer={$version}");
     $this->assertEquals(200, $response->getStatus());
     $json = json_decode($response->getBody(), true);
     $this->assertEmpty($json[$objectTypePlural]);
     // Get empty deleted via API with newertime
     $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime={$lastSyncTimestamp}");
     $this->assertEquals(200, $response->getStatus());
     $json = json_decode($response->getBody(), true);
     $this->assertEmpty($json[$objectTypePlural]);
     // Delete via sync
     foreach ($keys as $key) {
         switch ($objectType) {
             case 'item':
                 Sync::deleteItem(self::$sessionID, self::$config['libraryID'], $key, $this);
                 break;
             case 'setting':
                 // Delete via sync
                 $xml = Sync::updated(self::$sessionID);
                 $updateKey = (string) $xml['updateKey'];
                 $xmlstr = '<data version="9">' . '<deleted>' . '<settings>' . '<setting libraryID="' . self::$config['libraryID'] . '" key="' . $key . '"/>' . '</settings>' . '</deleted>' . '</data>';
                 $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr);
                 Sync::waitForUpload(self::$sessionID, $response, $this);
                 break;
         }
     }
     // Check 404 via API
     foreach ($keys as $key) {
         $response = API::userGet(self::$config['userID'], "{$objectTypePlural}/{$key}?key=" . self::$config['apiKey']);
         $this->assertEquals(404, $response->getStatus());
     }
     // Get deleted via API
     $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newer={$version}");
     $this->assertEquals(200, $response->getStatus());
     $json = json_decode($response->getBody(), true);
     $this->assertArrayHasKey($objectTypePlural, $json);
     $this->assertCount(sizeOf($keys), $json[$objectTypePlural]);
     foreach ($keys as $key) {
         $this->assertContains($key, $json[$objectTypePlural]);
     }
     // Get deleted via API with newertime
     $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime={$lastSyncTimestamp}");
     $this->assertEquals(200, $response->getStatus());
     $json = json_decode($response->getBody(), true);
     $this->assertArrayHasKey($objectTypePlural, $json);
     $this->assertCount(sizeOf($keys), $json[$objectTypePlural]);
     foreach ($keys as $key) {
         $this->assertContains($key, $json[$objectTypePlural]);
     }
     // Should be empty with later newertime
     $xml = Sync::updated(self::$sessionID);
     $lastSyncTimestamp = (int) $xml['timestamp'];
     $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime=" . ($lastSyncTimestamp + 2));
     $this->assertEquals(200, $response->getStatus());
     $json = json_decode($response->getBody(), true);
     $this->assertEmpty($json[$objectTypePlural]);
 }
Example #9
0
 public function testEmptyCreator()
 {
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     // Create creator via sync
     $data = '<data version="9"><creators>' . '<creator libraryID="' . self::$config['libraryID'] . '" ' . 'key="AAAAAAAA" dateAdded="2013-12-01 03:53:20" dateModified="2013-12-01 03:54:09">' . '<name></name>' . '<fieldMode>1</fieldMode>' . '</creator>' . '<creator libraryID="' . self::$config['libraryID'] . '" ' . 'key="BBBBBBBB" dateAdded="2013-12-01 04:53:20" dateModified="2013-12-01 04:54:09">' . '<name>' . chr(0xef) . chr(0xbb) . chr(0xbf) . '</name>' . '<fieldMode>1</fieldMode>' . '</creator>' . '</creators></data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Creators should have been skipped
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $this->assertEquals(0, sizeOf($xml->updated->creators->creator));
     // Create creator with valid name
     $data = '<data version="9"><creators>' . '<creator libraryID="' . self::$config['libraryID'] . '" ' . 'key="AAAAAAAA" dateAdded="2013-12-01 03:53:20" dateModified="2013-12-01 03:54:09">' . '<name>Test</name>' . '<fieldMode>1</fieldMode>' . '</creator>' . '</creators></data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $this->assertEquals(1, sizeOf($xml->updated->creators->creator));
     // Update with empty
     $data = '<data version="9"><creators>' . '<creator libraryID="' . self::$config['libraryID'] . '" ' . 'key="AAAAAAAA" dateAdded="2013-12-01 03:53:20" dateModified="2013-12-01 03:54:09">' . '<name>' . chr(0xef) . chr(0xbb) . chr(0xbf) . '</name>' . '<fieldMode>1</fieldMode>' . '</creator>' . '</creators></data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $this->assertEquals(1, sizeOf($xml->updated->creators->creator));
     // Not ideal, but for now the updated creator should just be ignored
     $this->assertEquals("Test", (string) $xml->updated->creators->creator->name);
 }
Example #10
0
 public function testCheckFieldStatusReturnBoolean()
 {
     $data = Sync::checkFieldStatus('Contact');
     $this->assertInternalType('bool', $data);
 }
Example #11
0
 public function testGroupDeleteItemLibraryAccessDenied()
 {
     // Create item
     $xml = Sync::updated(self::$sessionID2);
     $updateKey = (string) $xml['updateKey'];
     $data = '<data version="9"><items><item libraryID="' . self::$config['ownedPrivateGroupLibraryID2'] . '" itemType="note" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="AAAAAAAA"><note>Test</note></item></items></data>';
     $response = Sync::upload(self::$sessionID2, $updateKey, $data, true);
     Sync::waitForUpload(self::$sessionID2, $response, $this);
     // Delete item without permissions
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $data = '<data version="9"><deleted><items><item libraryID="' . self::$config['ownedPrivateGroupLibraryID2'] . '" key="AAAAAAAA"/>' . "</items></deleted></data>";
     $response = Sync::upload(self::$sessionID, $updateKey, $data, true);
     $xml = Sync::waitForUpload(self::$sessionID, $response, $this, true);
     $this->assertTrue(isset($xml->error));
     $this->assertEquals("LIBRARY_ACCESS_DENIED", $xml->error["code"]);
 }
Example #12
0
File: sync.php Project: jianoll/sfs
        } else {
            return FALSE;
        }
        return TRUE;
    }
}
$opts = getopt("c:p:a:u:g:h");
if ($opts === FALSE) {
    exit(1);
}
if (!empty($opts["h"])) {
    echo "Usage: {$argv[0]} [ -c config.php ] [ -p sync.pid ] [ -u uid ] [ -g gid ] [ -a start|stop ]\n";
    exit(0);
}
$configPath = isset($opts["c"]) ? $opts["c"] : dirname(__FILE__) . "/config.php";
$sync = new Sync($configPath, !empty($opts["p"]) ? $opts["p"] : null);
if (!empty($opts["g"])) {
    $gid = $opts["g"];
    if (posix_setgid($gid) === FALSE) {
        syslog(LOG_CRIT, "Could not setgid to {$gid}");
        exit(1);
    }
}
if (!empty($opts["u"])) {
    $uid = $opts["u"];
    if (posix_setuid($uid) === FALSE) {
        syslog(LOG_CRIT, "Could not setuid to {$uid}");
        exit(1);
    }
}
$oldpid = null;
<?php

/**
 * - get users + groups from ldap tree
 * - create mapping
 * - search/replace all ids in sql dump
 *
 * @author      Philipp Schüle <*****@*****.**>
 * 
 * @todo    implement
 * @todo    use Zend_Ldap to fetch users/groups
 * @todo    allow to set filename as param
 * @todo    get LDAP bind data from config file
 * @todo    get users/groups to create mapping
 * @todo    preg_replace occurrences in file
 */
// set include path to find all needed classes
set_include_path('./lib' . PATH_SEPARATOR . '/usr/share/tine20' . PATH_SEPARATOR . '/usr/share/tine20/library' . PATH_SEPARATOR . '/etc/tine20' . PATH_SEPARATOR . get_include_path());
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);
$sync = new Sync();
$sync->doSync();
Example #14
0
 public function testNoteWayTooLong()
 {
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $content = str_repeat("1", 10000000);
     // Create too-long note via sync
     $data = '<data version="9"><items><item libraryID="' . self::$config['libraryID'] . '" itemType="note" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="AAAAAAAA"><note>' . $content . '</note></item></items></data>';
     //
     // < 4.0.27
     //
     Sync::useZoteroVersion("4.0.26.4");
     $response = Sync::upload(self::$sessionID, $updateKey, $data, true);
     $xml = Sync::waitForUpload(self::$sessionID, $response, $this, true);
     $this->assertTrue(isset($xml->error));
     $this->assertEquals("ERROR_PROCESSING_UPLOAD_DATA", $xml->error["code"]);
     $this->assertRegExp('/^The note \'.+\' in your library is too long /', (string) $xml->error);
     $this->assertRegExp('/ copy and paste \'AAAAAAAA\' into /', (string) $xml->error);
     //
     // >=4.0.27
     //
     Sync::useZoteroVersion();
     $response = Sync::upload(self::$sessionID, $updateKey, $data, true);
     $xml = Sync::waitForUpload(self::$sessionID, $response, $this, true);
     $this->assertTrue(isset($xml->error));
     $this->assertEquals("NOTE_TOO_LONG", $xml->error["code"]);
     $this->assertRegExp('/^The note \'.+\' in your library is too long /', (string) $xml->error);
     $this->assertRegExp('/\\/AAAAAAAA$/', (string) $xml->item);
 }
Example #15
0
 public function testComputerProgram()
 {
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $itemKey = 'AAAAAAAA';
     // Create item via sync
     $data = '<data version="9"><items><item libraryID="' . self::$config['libraryID'] . '" itemType="computerProgram" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="' . $itemKey . '">' . '<field name="version">1.0</field>' . '</item></items></data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Get item version via API
     $response = API::userGet(self::$config['userID'], "items/{$itemKey}?key=" . self::$config['apiKey'] . "&content=json");
     $this->assertEquals(200, $response->getStatus());
     $xml = API::getItemXML($itemKey);
     $data = API::parseDataFromAtomEntry($xml);
     $json = json_decode($data['content'], true);
     $this->assertEquals('1.0', $json['version']);
     $json['version'] = '1.1';
     $response = API::userPut(self::$config['userID'], "items/{$itemKey}?key=" . self::$config['apiKey'], json_encode($json));
     $this->assertEquals(204, $response->getStatus());
     $xml = Sync::updated(self::$sessionID);
     $this->assertEquals('version', (string) $xml->updated[0]->items[0]->item[0]->field[0]['name']);
 }
Example #16
0
 /**
  * Gets called when this course changes.
  */
 private function changed()
 {
     Sync::course($this);
 }
Example #17
0
 public function testFullTextNoAccess()
 {
     API::groupClear(self::$config['ownedPrivateGroupID2']);
     // Add item to group as user 2
     $user2SessionID = Sync::login(['username' => self::$config['username2'], 'password' => self::$config['password2']]);
     $xml = Sync::updated($user2SessionID);
     $updateKey = (string) $xml['updateKey'];
     $key = Zotero_Utilities::randomString(8, 'key', true);
     $dateAdded = date('Y-m-d H:i:s', time() - 1);
     $dateModified = date('Y-m-d H:i:s');
     $xmlstr = '<data version="9">' . '<items>' . '<item libraryID="' . self::$config['ownedPrivateGroupLibraryID2'] . '" ' . 'itemType="attachment" ' . 'dateAdded="' . $dateAdded . '" ' . 'dateModified="' . $dateModified . '" ' . 'key="' . $key . '"/>' . '</items>' . '</data>';
     $response = Sync::upload($user2SessionID, $updateKey, $xmlstr);
     Sync::waitForUpload($user2SessionID, $response, $this);
     // Make sure item exists
     $xml = Sync::updated($user2SessionID, 1);
     $this->assertEquals(1, $xml->updated[0]->items->count());
     $this->assertEquals(1, $xml->updated[0]->items[0]->item->count());
     // Try to add full-text content as user 1
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $content = "This is some full-text content.";
     $totalChars = 2500;
     $xmlstr = '<data version="9">' . '<fulltexts>' . '<fulltext libraryID="' . self::$config['ownedPrivateGroupLibraryID2'] . '" ' . 'key="' . $key . '" ' . 'indexedChars="' . strlen($content) . '" ' . 'totalChars="' . $totalChars . '" ' . 'indexedPages="0" ' . 'totalPages="0">' . htmlspecialchars($content) . '</fulltext>' . '</fulltexts>' . '</data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Retrieve it as user 2
     $xml = Sync::updated($user2SessionID, 1, false, false, ["ft" => 1]);
     $this->assertEquals(0, $xml->updated[0]->fulltexts->count());
     API::groupClear(self::$config['ownedPrivateGroupID2']);
 }
Example #18
0
 public static function check_for_pid($pid)
 {
     $OS = Sync::getOS();
     if ($OS == 2) {
         $processes = explode("\n", shell_exec("tasklist.exe"));
         foreach ($processes as $key => $value) {
             if (empty($value) != '1' && strpos("Image Name", $value) === 0 || empty($value) != '1' && strpos("===", $value) === 0) {
                 continue;
             }
             $matches = false;
             preg_match("/(.*?)\\s+(\\d+).*\$/", $value, $matches);
             if (isset($matches[2]) && ($pid = $matches[2])) {
                 return true;
             }
         }
     } else {
         return file_exists("/proc/" . $pid);
     }
 }
Example #19
0
 public function testCircularRelatedItems()
 {
     $parentKey = API::createItem("book", false, null, 'key');
     $noteKeys = [API::createNoteItem("Note 1", $parentKey, null, 'key'), API::createNoteItem("Note 2", $parentKey, null, 'key'), API::createNoteItem("Note 3", $parentKey, null, 'key'), API::createNoteItem("Note 4", $parentKey, null, 'key')];
     $xml = Sync::updated(self::$sessionID);
     $updateKey = $xml['updateKey'];
     $note1XML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[0]}']"));
     $note2XML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[1]}']"));
     $note3XML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[2]}']"));
     $note4XML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[3]}']"));
     $note1XML['libraryID'] = self::$config['libraryID'];
     $note2XML['libraryID'] = self::$config['libraryID'];
     $note3XML['libraryID'] = self::$config['libraryID'];
     $note4XML['libraryID'] = self::$config['libraryID'];
     $note1XML->related = implode(' ', [$parentKey, (string) $note2XML['key'], (string) $note3XML['key'], (string) $note4XML['key']]);
     $note2XML->related = implode(' ', [$parentKey, (string) $note1XML['key'], (string) $note3XML['key'], (string) $note4XML['key']]);
     $note3XML->related = implode(' ', [$parentKey, (string) $note1XML['key'], (string) $note2XML['key'], (string) $note4XML['key']]);
     $note4XML->related = implode(' ', [$parentKey, (string) $note1XML['key'], (string) $note2XML['key'], (string) $note3XML['key']]);
     $xmlstr = '<data version="9">' . '<items>' . $note1XML->asXML() . $note2XML->asXML() . $note3XML->asXML() . $note4XML->asXML() . '</items>' . '</data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     $xml = Sync::updated(self::$sessionID);
     $noteXML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[0]}']"));
     $keys = split(' ', $noteXML->related);
     $this->assertCount(4, $keys);
     $this->assertContains($parentKey, $keys);
     $this->assertContains((string) $noteKeys[1], $keys);
     $this->assertContains((string) $noteKeys[2], $keys);
     $this->assertContains((string) $noteKeys[3], $keys);
     $noteXML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[1]}']"));
     $keys = split(' ', $noteXML->related);
     $this->assertCount(4, $keys);
     $this->assertContains($parentKey, $keys);
     $this->assertContains((string) $noteKeys[0], $keys);
     $this->assertContains((string) $noteKeys[2], $keys);
     $this->assertContains((string) $noteKeys[3], $keys);
     $noteXML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[2]}']"));
     $keys = split(' ', $noteXML->related);
     $this->assertCount(4, $keys);
     $this->assertContains($parentKey, $keys);
     $this->assertContains((string) $noteKeys[0], $keys);
     $this->assertContains((string) $noteKeys[1], $keys);
     $this->assertContains((string) $noteKeys[3], $keys);
     $noteXML = array_shift($xml->updated[0]->items->xpath("//item[@key = '{$noteKeys[3]}']"));
     $keys = split(' ', $noteXML->related);
     $this->assertCount(4, $keys);
     $this->assertContains($parentKey, $keys);
     $this->assertContains((string) $noteKeys[0], $keys);
     $this->assertContains((string) $noteKeys[1], $keys);
     $this->assertContains((string) $noteKeys[2], $keys);
 }
Example #20
0
 public function testCollectionItemUpdate()
 {
     $collectionKey = Sync::createCollection(self::$sessionID, self::$config['libraryID'], "Test", null, $this);
     $itemKey = Sync::createItem(self::$sessionID, self::$config['libraryID'], "book", null, $this);
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     // Get the item version
     $itemXML = API::getItemXML($itemKey);
     $data = API::parseDataFromAtomEntry($itemXML);
     $json = json_decode($data['content'], true);
     $itemVersion = $json['itemVersion'];
     $this->assertNotNull($itemVersion);
     // Add via sync
     $collectionXML = $xml->updated[0]->collections[0]->collection[0];
     $collectionXML['libraryID'] = self::$config['libraryID'];
     $collectionXML->addChild("items", $itemKey);
     $data = '<data version="9"><collections>' . $collectionXML->asXML() . '</collections>' . '</data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data, true);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Make sure item was updated
     $itemXML = API::getItemXML($itemKey);
     $data = API::parseDataFromAtomEntry($itemXML);
     $json = json_decode($data['content'], true);
     $this->assertGreaterThan($itemVersion, $json['itemVersion']);
     $itemVersion = $json['itemVersion'];
     $this->assertCount(1, $json['collections']);
     $this->assertContains($collectionKey, $json['collections']);
     // Remove via sync
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     $collectionXML = $xml->updated[0]->collections[0]->collection[0];
     $collectionXML['libraryID'] = self::$config['libraryID'];
     unset($collectionXML->items);
     $data = '<data version="9"><collections>' . $collectionXML->asXML() . '</collections>' . '</data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data, true);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Make sure item was removed
     $itemXML = API::getItemXML($itemKey);
     $data = API::parseDataFromAtomEntry($itemXML);
     $json = json_decode($data['content'], true);
     $this->assertGreaterThan($itemVersion, $json['itemVersion']);
     $this->assertCount(0, $json['collections']);
 }
Example #21
0
 public function testSyncUploadUnchanged()
 {
     $data1 = API::createItem("audioRecording", array("title" => "Test", "relations" => array('owl:sameAs' => 'http://zotero.org/groups/1/items/AAAAAAAA')), null, 'data');
     // dc:relation already exists, so item shouldn't change
     $data2 = API::createItem("interview", array("relations" => array('dc:relation' => 'http://zotero.org/users/' . self::$config['userID'] . '/items/' . $data1['key'])), null, 'data');
     // Upload unchanged via sync
     $xml = Sync::updated(self::$sessionID);
     $updateKey = $xml['updateKey'];
     $lastSyncTimestamp = $xml['timestamp'];
     $itemXML1 = array_shift($xml->updated[0]->items[0]->xpath("item[@key='{$data1['key']}']"));
     $itemXML2 = array_shift($xml->updated[0]->items[0]->xpath("item[@key='{$data2['key']}']"));
     $itemXML1['libraryID'] = self::$config['libraryID'];
     $itemXML2['libraryID'] = self::$config['libraryID'];
     $xmlstr = '<data version="9">' . '<items>' . $itemXML1->asXML() . $itemXML2->asXML() . '</items>' . '</data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Check via API to make sure they're the same
     $response = API::userGet(self::$config['userID'], "items?key=" . self::$config['apiKey'] . "&format=versions");
     $json = API::getJSONFromResponse($response);
     $this->assertEquals($data1['version'], $json[$data1['key']]);
     $this->assertEquals($data2['version'], $json[$data2['key']]);
 }
Example #22
0
 /**
  * Handles the Sync command
  * Performs the synchronization of messages
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     // Contains all requested folders (containers)
     $sc = new SyncCollections();
     $status = SYNC_STATUS_SUCCESS;
     $wbxmlproblem = false;
     $emptysync = false;
     // check if the hierarchySync was fully completed
     if (USE_PARTIAL_FOLDERSYNC) {
         if (self::$deviceManager->GetFolderSyncComplete() === false) {
             ZLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): Sync request aborted, as exporting of folders has not yet completed");
             self::$topCollector->AnnounceInformation("Aborted due incomplete folder sync", true);
             $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
         } else {
             ZLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): FolderSync marked as complete");
         }
     }
     // Start Synchronize
     if (self::$decoder->getElementStartTag(SYNC_SYNCHRONIZE)) {
         // AS 1.0 sends version information in WBXML
         if (self::$decoder->getElementStartTag(SYNC_VERSION)) {
             $sync_version = self::$decoder->getElementContent();
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("WBXML sync version: '%s'", $sync_version));
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         // Synching specified folders
         // Android still sends heartbeat sync even if all syncfolders are disabled.
         // Check if Folders tag is empty (<Folders/>) and only sync if there are
         // some folders in the request. See ZP-172
         $startTag = self::$decoder->getElementStartTag(SYNC_FOLDERS);
         if (isset($startTag[EN_FLAGS]) && $startTag[EN_FLAGS]) {
             while (self::$decoder->getElementStartTag(SYNC_FOLDER)) {
                 $actiondata = array();
                 $actiondata["requested"] = true;
                 $actiondata["clientids"] = array();
                 $actiondata["modifyids"] = array();
                 $actiondata["removeids"] = array();
                 $actiondata["fetchids"] = array();
                 $actiondata["statusids"] = array();
                 // read class, synckey and folderid without SyncParameters Object for now
                 $class = $synckey = $folderid = false;
                 //for AS versions < 2.5
                 if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                     $class = self::$decoder->getElementContent();
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync folder: '%s'", $class));
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 // SyncKey
                 if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
                     $synckey = "0";
                     if (($synckey = self::$decoder->getElementContent()) !== false) {
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 } else {
                     return false;
                 }
                 // FolderId
                 if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
                     $folderid = self::$decoder->getElementContent();
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
                 if (!$folderid && $class) {
                     $folderid = self::$deviceManager->GetFolderIdFromCacheByClass($class);
                 }
                 // folderid HAS TO BE known by now, so we retrieve the correct SyncParameters object for an update
                 try {
                     $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($folderid);
                     // TODO remove resync of folders for < Z-Push 2 beta4 users
                     // this forces a resync of all states previous to Z-Push 2 beta4
                     if (!$spa instanceof SyncParameters) {
                         throw new StateInvalidException("Saved state are not of type SyncParameters");
                     }
                     // new/resync requested
                     if ($synckey == "0") {
                         $spa->RemoveSyncKey();
                     } else {
                         if ($synckey !== false) {
                             $spa->SetSyncKey($synckey);
                         }
                     }
                 } catch (StateInvalidException $stie) {
                     $spa = new SyncParameters();
                     $status = SYNC_STATUS_INVALIDSYNCKEY;
                     self::$topCollector->AnnounceInformation("State invalid - Resync folder", true);
                     self::$deviceManager->ForceFolderResync($folderid);
                 }
                 // update folderid.. this might be a new object
                 $spa->SetFolderId($folderid);
                 if ($class !== false) {
                     $spa->SetContentClass($class);
                 }
                 // Get class for as versions >= 12.0
                 if (!$spa->HasContentClass()) {
                     try {
                         $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("GetFolderClassFromCacheByID from Device Manager: '%s' for id:'%s'", $spa->GetContentClass(), $spa->GetFolderId()));
                     } catch (NoHierarchyCacheAvailableException $nhca) {
                         $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                         self::$deviceManager->ForceFullResync();
                     }
                 }
                 // done basic SPA initialization/loading -> add to SyncCollection
                 $sc->AddCollection($spa);
                 $sc->AddParameter($spa, "requested", true);
                 if ($spa->HasContentClass()) {
                     self::$topCollector->AnnounceInformation(sprintf("%s request", $spa->GetContentClass()), true);
                 } else {
                     ZLog::Write(LOGLEVEL_WARN, "Not possible to determine class of request. Request did not contain class and apparently there is an issue with the HierarchyCache.");
                 }
                 // SUPPORTED properties
                 if (($se = self::$decoder->getElementStartTag(SYNC_SUPPORTED)) !== false) {
                     // ZP-481: LG phones send an empty supported tag, so only read the contents if available here
                     // if <Supported/> is received, it's as no supported fields would have been sent at all.
                     // unsure if this is the correct approach, or if in this case some default list should be used
                     if ($se[EN_FLAGS] & EN_FLAGS_CONTENT) {
                         $supfields = array();
                         while (1) {
                             $el = self::$decoder->getElement();
                             if ($el[EN_TYPE] == EN_TYPE_ENDTAG) {
                                 break;
                             } else {
                                 $supfields[] = $el[EN_TAG];
                             }
                         }
                         self::$deviceManager->SetSupportedFields($spa->GetFolderId(), $supfields);
                     }
                 }
                 // Deletes as moves can be an empty tag as well as have value
                 if (self::$decoder->getElementStartTag(SYNC_DELETESASMOVES)) {
                     $spa->SetDeletesAsMoves(true);
                     if (($dam = self::$decoder->getElementContent()) !== false) {
                         $spa->SetDeletesAsMoves((bool) $dam);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 }
                 // Get changes can be an empty tag as well as have value
                 // code block partly contributed by dw2412
                 if (self::$decoder->getElementStartTag(SYNC_GETCHANGES)) {
                     $sc->AddParameter($spa, "getchanges", true);
                     if (($gc = self::$decoder->getElementContent()) !== false) {
                         $sc->AddParameter($spa, "getchanges", $gc);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 }
                 if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
                     $ws = self::$decoder->getElementContent();
                     // normalize windowsize - see ZP-477
                     if ($ws == 0 || $ws > 512) {
                         $ws = 512;
                     }
                     $spa->SetWindowSize($ws);
                     // also announce the currently requested window size to the DeviceManager
                     self::$deviceManager->SetWindowSize($spa->GetFolderId(), $spa->GetWindowSize());
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 // conversation mode requested
                 if (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
                     $spa->SetConversationMode(true);
                     if (($conversationmode = self::$decoder->getElementContent()) !== false) {
                         $spa->SetConversationMode((bool) $conversationmode);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 }
                 // Do not truncate by default
                 $spa->SetTruncation(SYNC_TRUNCATION_ALL);
                 // use default conflict handling if not specified by the mobile
                 $spa->SetConflict(SYNC_CONFLICT_DEFAULT);
                 while (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
                     $firstOption = true;
                     while (1) {
                         // foldertype definition
                         if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                             $foldertype = self::$decoder->getElementContent();
                             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): specified options block with foldertype '%s'", $foldertype));
                             // switch the foldertype for the next options
                             $spa->UseCPO($foldertype);
                             // set to synchronize all changes. The mobile could overwrite this value
                             $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         } else {
                             if ($firstOption) {
                                 $spa->UseCPO();
                                 // set to synchronize all changes. The mobile could overwrite this value
                                 $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
                             }
                         }
                         $firstOption = false;
                         if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
                             $spa->SetFilterType(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_TRUNCATION)) {
                             $spa->SetTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_RTFTRUNCATION)) {
                             $spa->SetRTFTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
                             $spa->SetMimeSupport(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_MIMETRUNCATION)) {
                             $spa->SetMimeTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_CONFLICT)) {
                             $spa->SetConflict(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) {
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
                                 $bptype = self::$decoder->getElementContent();
                                 $spa->BodyPreference($bptype);
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
                                 $spa->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
                                 $spa->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
                                 $spa->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         $e = self::$decoder->peek();
                         if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
                             self::$decoder->getElementEndTag();
                             break;
                         }
                     }
                 }
                 // limit items to be synchronized to the mobiles if configured
                 if (defined('SYNC_FILTERTIME_MAX') && SYNC_FILTERTIME_MAX > SYNC_FILTERTYPE_ALL && (!$spa->HasFilterType() || $spa->GetFilterType() == SYNC_FILTERTYPE_ALL || $spa->GetFilterType() > SYNC_FILTERTIME_MAX)) {
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("SYNC_FILTERTIME_MAX defined. Filter set to value: %s", SYNC_FILTERTIME_MAX));
                     $spa->SetFilterType(SYNC_FILTERTIME_MAX);
                 }
                 // Check if the hierarchycache is available. If not, trigger a HierarchySync
                 if (self::$deviceManager->IsHierarchySyncRequired()) {
                     $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                     ZLog::Write(LOGLEVEL_DEBUG, "HierarchyCache is also not available. Triggering HierarchySync to device");
                 }
                 if (($el = self::$decoder->getElementStartTag(SYNC_PERFORM)) && $el[EN_FLAGS] & EN_FLAGS_CONTENT) {
                     // We can not proceed here as the content class is unknown
                     if ($status != SYNC_STATUS_SUCCESS) {
                         ZLog::Write(LOGLEVEL_WARN, "Ignoring all incoming actions as global status indicates problem.");
                         $wbxmlproblem = true;
                         break;
                     }
                     $performaction = true;
                     // unset the importer
                     $this->importer = false;
                     $nchanges = 0;
                     while (1) {
                         // ADD, MODIFY, REMOVE or FETCH
                         $element = self::$decoder->getElement();
                         if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
                             self::$decoder->ungetElement($element);
                             break;
                         }
                         if ($status == SYNC_STATUS_SUCCESS) {
                             $nchanges++;
                         }
                         // Foldertype sent when synching SMS
                         if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                             $foldertype = self::$decoder->getElementContent();
                             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): incoming data with foldertype '%s'", $foldertype));
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         } else {
                             $foldertype = false;
                         }
                         $serverid = false;
                         if (self::$decoder->getElementStartTag(SYNC_SERVERENTRYID)) {
                             if (($serverid = self::$decoder->getElementContent()) !== false) {
                                 if (!self::$decoder->getElementEndTag()) {
                                     // end serverid
                                     return false;
                                 }
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_CLIENTENTRYID)) {
                             $clientid = self::$decoder->getElementContent();
                             if (!self::$decoder->getElementEndTag()) {
                                 // end clientid
                                 return false;
                             }
                         } else {
                             $clientid = false;
                         }
                         // Get the SyncMessage if sent
                         if (self::$decoder->getElementStartTag(SYNC_DATA)) {
                             $message = ZPush::getSyncObjectFromFolderClass($spa->GetContentClass());
                             $message->Decode(self::$decoder);
                             // set Ghosted fields
                             $message->emptySupported(self::$deviceManager->GetSupportedFields($spa->GetFolderId()));
                             if (!self::$decoder->getElementEndTag()) {
                                 // end applicationdata
                                 return false;
                             }
                         } else {
                             $message = false;
                         }
                         switch ($element[EN_TAG]) {
                             case SYNC_FETCH:
                                 array_push($actiondata["fetchids"], $serverid);
                                 break;
                             default:
                                 // get the importer
                                 if ($this->importer == false) {
                                     $status = $this->getImporter($sc, $spa, $actiondata);
                                 }
                                 if ($status == SYNC_STATUS_SUCCESS) {
                                     $this->importMessage($spa, $actiondata, $element[EN_TAG], $message, $clientid, $serverid, $foldertype, $nchanges);
                                 } else {
                                     ZLog::Write(LOGLEVEL_WARN, "Ignored incoming change, global status indicates problem.");
                                 }
                                 break;
                         }
                         if ($actiondata["fetchids"]) {
                             self::$topCollector->AnnounceInformation(sprintf("Fetching %d", $nchanges));
                         } else {
                             self::$topCollector->AnnounceInformation(sprintf("Incoming %d", $nchanges));
                         }
                         if (!self::$decoder->getElementEndTag()) {
                             // end add/change/delete/move
                             return false;
                         }
                     }
                     if ($status == SYNC_STATUS_SUCCESS && $this->importer !== false) {
                         ZLog::Write(LOGLEVEL_INFO, sprintf("Sync->Handle(): Processed %d incoming changes", $nchanges));
                         if (!$actiondata["fetchids"]) {
                             self::$topCollector->AnnounceInformation(sprintf("%d incoming", $nchanges), true);
                         }
                         try {
                             // Save the updated state, which is used for the exporter later
                             $sc->AddParameter($spa, "state", $this->importer->GetState());
                         } catch (StatusException $stex) {
                             $status = $stex->getCode();
                         }
                     }
                     if (!self::$decoder->getElementEndTag()) {
                         // end PERFORM
                         return false;
                     }
                 }
                 // save the failsave state
                 if (!empty($actiondata["statusids"])) {
                     unset($actiondata["failstate"]);
                     $actiondata["failedsyncstate"] = $sc->GetParameter($spa, "state");
                     self::$deviceManager->GetStateManager()->SetSyncFailState($actiondata);
                 }
                 // save actiondata
                 $sc->AddParameter($spa, "actiondata", $actiondata);
                 if (!self::$decoder->getElementEndTag()) {
                     // end collection
                     return false;
                 }
                 // AS14 does not send GetChanges anymore. We should do it if there were no incoming changes
                 if (!isset($performaction) && !$sc->GetParameter($spa, "getchanges") && $spa->HasSyncKey()) {
                     $sc->AddParameter($spa, "getchanges", true);
                 }
             }
             // END FOLDER
             if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) {
                 // end collections
                 return false;
             }
         }
         // end FOLDERS
         if (self::$decoder->getElementStartTag(SYNC_HEARTBEATINTERVAL)) {
             $hbinterval = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_HEARTBEATINTERVAL
                 return false;
             }
         }
         if (self::$decoder->getElementStartTag(SYNC_WAIT)) {
             $wait = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_WAIT
                 return false;
             }
             // internally the heartbeat interval and the wait time are the same
             // heartbeat is in seconds, wait in minutes
             $hbinterval = $wait * 60;
         }
         if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
             $sc->SetGlobalWindowSize(self::$decoder->getElementContent());
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_WINDOWSIZE
                 return false;
             }
         }
         if (self::$decoder->getElementStartTag(SYNC_PARTIAL)) {
             $partial = true;
         } else {
             $partial = false;
         }
         if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) {
             // end sync
             return false;
         }
     } else {
         $emptysync = true;
     }
     // END SYNCHRONIZE
     // check heartbeat/wait time
     if (isset($hbinterval)) {
         if ($hbinterval < 60 || $hbinterval > 3540) {
             $status = SYNC_STATUS_INVALIDWAITORHBVALUE;
             ZLog::Write(LOGLEVEL_WARN, sprintf("HandleSync(): Invalid heartbeat or wait value '%s'", $hbinterval));
         }
     }
     // Partial & Empty Syncs need saved data to proceed with synchronization
     if ($status == SYNC_STATUS_SUCCESS && ($emptysync === true || $partial === true)) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Partial or Empty sync requested. Retrieving data of synchronized folders."));
         // Load all collections - do not overwrite existing (received!), load states and check permissions
         try {
             $sc->LoadAllCollections(false, true, true);
         } catch (StateNotFoundException $snfex) {
             $status = SYNC_STATUS_INVALIDSYNCKEY;
             self::$topCollector->AnnounceInformation("StateNotFoundException", true);
         } catch (StatusException $stex) {
             $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
             self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true);
         }
         // update a few values
         foreach ($sc as $folderid => $spa) {
             // manually set getchanges parameter for this collection
             $sc->AddParameter($spa, "getchanges", true);
             // set new global windowsize without marking the SPA as changed
             if ($sc->GetGlobalWindowSize()) {
                 $spa->SetWindowSize($sc->GetGlobalWindowSize(), false);
             }
             // announce WindowSize to DeviceManager
             self::$deviceManager->SetWindowSize($folderid, $spa->GetWindowSize());
         }
         if (!$sc->HasCollections()) {
             $status = SYNC_STATUS_SYNCREQUESTINCOMPLETE;
         }
     }
     // HEARTBEAT & Empty sync
     if ($status == SYNC_STATUS_SUCCESS && (isset($hbinterval) || $emptysync == true)) {
         $interval = defined('PING_INTERVAL') && PING_INTERVAL > 0 ? PING_INTERVAL : 30;
         if (isset($hbinterval)) {
             $sc->SetLifetime($hbinterval);
         }
         // states are lazy loaded - we have to make sure that they are there!
         $loadstatus = SYNC_STATUS_SUCCESS;
         foreach ($sc as $folderid => $spa) {
             // some androids do heartbeat on the OUTBOX folder, with weird results - ZP-362
             // we do not load the state so we will never get relevant changes on the OUTBOX folder
             if (self::$deviceManager->GetFolderTypeFromCacheById($folderid) == SYNC_FOLDER_TYPE_OUTBOX) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Heartbeat on Outbox folder not allowed"));
                 continue;
             }
             $fad = array();
             // if loading the states fails, we do not enter heartbeat, but we keep $status on SYNC_STATUS_SUCCESS
             // so when the changes are exported the correct folder gets an SYNC_STATUS_INVALIDSYNCKEY
             if ($loadstatus == SYNC_STATUS_SUCCESS) {
                 $loadstatus = $this->loadStates($sc, $spa, $fad);
             }
         }
         if ($loadstatus == SYNC_STATUS_SUCCESS) {
             $foundchanges = false;
             try {
                 // always check for changes
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Entering Heartbeat mode"));
                 $foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval);
             } catch (StatusException $stex) {
                 if ($stex->getCode() == SyncCollections::OBSOLETE_CONNECTION) {
                     $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                 } else {
                     $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                     self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true);
                 }
             }
             // in case there are no changes and no other request has synchronized while we waited, we can reply with an empty response
             if (!$foundchanges && $status == SYNC_STATUS_SUCCESS) {
                 // if there were changes to the SPA or CPOs we need to save this before we terminate
                 // only save if the state was not modified by some other request, if so, return state invalid status
                 foreach ($sc as $folderid => $spa) {
                     if (self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
                         $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                     } else {
                         $sc->SaveCollection($spa);
                     }
                 }
                 if ($status == SYNC_STATUS_SUCCESS) {
                     ZLog::Write(LOGLEVEL_DEBUG, "No changes found and no other process changed states. Replying with empty response and closing connection.");
                     self::$specialHeaders = array();
                     self::$specialHeaders[] = "Connection: close";
                     return true;
                 }
             }
             if ($foundchanges) {
                 foreach ($sc->GetChangedFolderIds() as $folderid => $changecount) {
                     // check if there were other sync requests for a folder during the heartbeat
                     $spa = $sc->GetCollection($folderid);
                     if ($changecount > 0 && $sc->WaitedForChanges() && self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s' which was already synchronized. Heartbeat aborted!", $changecount, $folderid));
                         $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                     } else {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s'", $changecount, $folderid));
                     }
                 }
             }
         }
     }
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Start Output"));
     // Start the output
     self::$encoder->startWBXML();
     self::$encoder->startTag(SYNC_SYNCHRONIZE);
     // global status
     // SYNC_COMMONSTATUS_* start with values from 101
     if ($status != SYNC_COMMONSTATUS_SUCCESS && $status > 100) {
         self::$encoder->startTag(SYNC_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
     } else {
         self::$encoder->startTag(SYNC_FOLDERS);
         foreach ($sc as $folderid => $spa) {
             // get actiondata
             $actiondata = $sc->GetParameter($spa, "actiondata");
             if ($status == SYNC_STATUS_SUCCESS && (!$spa->GetContentClass() || !$spa->GetFolderId())) {
                 ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): no content class or folderid found for collection."));
                 continue;
             }
             if (!$sc->GetParameter($spa, "requested")) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): partial sync for folder class '%s' with id '%s'", $spa->GetContentClass(), $spa->GetFolderId()));
             }
             // initialize exporter to get changecount
             $changecount = false;
             if (isset($exporter)) {
                 unset($exporter);
             }
             // TODO we could check against $sc->GetChangedFolderIds() on heartbeat so we do not need to configure all exporter again
             if ($status == SYNC_STATUS_SUCCESS && ($sc->GetParameter($spa, "getchanges") || !$spa->HasSyncKey())) {
                 //make sure the states are loaded
                 $status = $this->loadStates($sc, $spa, $actiondata);
                 if ($status == SYNC_STATUS_SUCCESS) {
                     try {
                         // if this is an additional folder the backend has to be setup correctly
                         if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) {
                             throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
                         }
                         // Use the state from the importer, as changes may have already happened
                         $exporter = self::$backend->GetExporter($spa->GetFolderId());
                         if ($exporter === false) {
                             throw new StatusException(sprintf("HandleSync() could not get an exporter for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
                         }
                     } catch (StatusException $stex) {
                         $status = $stex->getCode();
                     }
                     try {
                         // Stream the messages directly to the PDA
                         $streamimporter = new ImportChangesStream(self::$encoder, ZPush::getSyncObjectFromFolderClass($spa->GetContentClass()));
                         if ($exporter !== false) {
                             $exporter->Config($sc->GetParameter($spa, "state"));
                             $exporter->ConfigContentParameters($spa->GetCPO());
                             $exporter->InitializeExporter($streamimporter);
                             $changecount = $exporter->GetChangeCount();
                         }
                     } catch (StatusException $stex) {
                         if ($stex->getCode() === SYNC_FSSTATUS_CODEUNKNOWN && $spa->HasSyncKey()) {
                             $status = SYNC_STATUS_INVALIDSYNCKEY;
                         } else {
                             $status = $stex->getCode();
                         }
                     }
                     if (!$spa->HasSyncKey()) {
                         self::$topCollector->AnnounceInformation(sprintf("Exporter registered. %d objects queued.", $changecount), true);
                         // update folder status as initialized
                         $spa->SetFolderSyncTotal($changecount);
                         $spa->SetFolderSyncRemaining($changecount);
                         if ($changecount > 0) {
                             self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INITIALIZED);
                         }
                     } else {
                         if ($status != SYNC_STATUS_SUCCESS) {
                             self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true);
                         }
                     }
                 }
             }
             if (isset($hbinterval) && $changecount == 0 && $status == SYNC_STATUS_SUCCESS) {
                 ZLog::Write(LOGLEVEL_DEBUG, "No changes found for heartbeat folder. Omitting empty output.");
                 continue;
             }
             // Get a new sync key to output to the client if any changes have been send or will are available
             if (!empty($actiondata["modifyids"]) || !empty($actiondata["clientids"]) || !empty($actiondata["removeids"]) || $changecount > 0 || !$spa->HasSyncKey() && $status == SYNC_STATUS_SUCCESS) {
                 $spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey()));
             }
             self::$encoder->startTag(SYNC_FOLDER);
             if ($spa->HasContentClass()) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("Folder type: %s", $spa->GetContentClass()));
                 // AS 12.0 devices require content class
                 if (Request::GetProtocolVersion() < 12.1) {
                     self::$encoder->startTag(SYNC_FOLDERTYPE);
                     self::$encoder->content($spa->GetContentClass());
                     self::$encoder->endTag();
                 }
             }
             self::$encoder->startTag(SYNC_SYNCKEY);
             if ($status == SYNC_STATUS_SUCCESS && $spa->HasNewSyncKey()) {
                 self::$encoder->content($spa->GetNewSyncKey());
             } else {
                 self::$encoder->content($spa->GetSyncKey());
             }
             self::$encoder->endTag();
             self::$encoder->startTag(SYNC_FOLDERID);
             self::$encoder->content($spa->GetFolderId());
             self::$encoder->endTag();
             self::$encoder->startTag(SYNC_STATUS);
             self::$encoder->content($status);
             self::$encoder->endTag();
             // announce failing status to the process loop detection
             if ($status !== SYNC_STATUS_SUCCESS) {
                 self::$deviceManager->AnnounceProcessStatus($spa->GetFolderId(), $status);
             }
             // Output IDs and status for incoming items & requests
             if ($status == SYNC_STATUS_SUCCESS && (!empty($actiondata["clientids"]) || !empty($actiondata["modifyids"]) || !empty($actiondata["removeids"]) || !empty($actiondata["fetchids"]))) {
                 self::$encoder->startTag(SYNC_REPLIES);
                 // output result of all new incoming items
                 foreach ($actiondata["clientids"] as $clientid => $serverid) {
                     self::$encoder->startTag(SYNC_ADD);
                     self::$encoder->startTag(SYNC_CLIENTENTRYID);
                     self::$encoder->content($clientid);
                     self::$encoder->endTag();
                     if ($serverid) {
                         self::$encoder->startTag(SYNC_SERVERENTRYID);
                         self::$encoder->content($serverid);
                         self::$encoder->endTag();
                     }
                     self::$encoder->startTag(SYNC_STATUS);
                     self::$encoder->content(isset($actiondata["statusids"][$clientid]) ? $actiondata["statusids"][$clientid] : SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR);
                     self::$encoder->endTag();
                     self::$encoder->endTag();
                 }
                 // loop through modify operations which were not a success, send status
                 foreach ($actiondata["modifyids"] as $serverid) {
                     if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
                         self::$encoder->startTag(SYNC_MODIFY);
                         self::$encoder->startTag(SYNC_SERVERENTRYID);
                         self::$encoder->content($serverid);
                         self::$encoder->endTag();
                         self::$encoder->startTag(SYNC_STATUS);
                         self::$encoder->content($actiondata["statusids"][$serverid]);
                         self::$encoder->endTag();
                         self::$encoder->endTag();
                     }
                 }
                 // loop through remove operations which were not a success, send status
                 foreach ($actiondata["removeids"] as $serverid) {
                     if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
                         self::$encoder->startTag(SYNC_REMOVE);
                         self::$encoder->startTag(SYNC_SERVERENTRYID);
                         self::$encoder->content($serverid);
                         self::$encoder->endTag();
                         self::$encoder->startTag(SYNC_STATUS);
                         self::$encoder->content($actiondata["statusids"][$serverid]);
                         self::$encoder->endTag();
                         self::$encoder->endTag();
                     }
                 }
                 if (!empty($actiondata["fetchids"])) {
                     self::$topCollector->AnnounceInformation(sprintf("Fetching %d objects ", count($actiondata["fetchids"])), true);
                 }
                 foreach ($actiondata["fetchids"] as $id) {
                     $data = false;
                     try {
                         $fetchstatus = SYNC_STATUS_SUCCESS;
                         // if this is an additional folder the backend has to be setup correctly
                         if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) {
                             throw new StatusException(sprintf("HandleSync(): could not Setup() the backend to fetch in folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_OBJECTNOTFOUND);
                         }
                         $data = self::$backend->Fetch($spa->GetFolderId(), $id, $spa->GetCPO());
                         // check if the message is broken
                         if (ZPush::GetDeviceManager(false) && ZPush::GetDeviceManager()->DoNotStreamMessage($id, $data)) {
                             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): message not to be streamed as requested by DeviceManager, id = %s", $id));
                             $fetchstatus = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
                         }
                     } catch (StatusException $stex) {
                         $fetchstatus = $stex->getCode();
                     }
                     self::$encoder->startTag(SYNC_FETCH);
                     self::$encoder->startTag(SYNC_SERVERENTRYID);
                     self::$encoder->content($id);
                     self::$encoder->endTag();
                     self::$encoder->startTag(SYNC_STATUS);
                     self::$encoder->content($fetchstatus);
                     self::$encoder->endTag();
                     if ($data !== false && $status == SYNC_STATUS_SUCCESS) {
                         self::$encoder->startTag(SYNC_DATA);
                         $data->Encode(self::$encoder);
                         self::$encoder->endTag();
                     } else {
                         ZLog::Write(LOGLEVEL_WARN, sprintf("Unable to Fetch '%s'", $id));
                     }
                     self::$encoder->endTag();
                 }
                 self::$encoder->endTag();
             }
             if ($sc->GetParameter($spa, "getchanges") && $spa->HasFolderId() && $spa->HasContentClass() && $spa->HasSyncKey()) {
                 $windowSize = self::$deviceManager->GetWindowSize($spa->GetFolderId(), $spa->GetContentClass(), $spa->GetUuid(), $spa->GetUuidCounter(), $changecount);
                 if ($changecount > $windowSize) {
                     self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
                 }
             }
             // Stream outgoing changes
             if ($status == SYNC_STATUS_SUCCESS && $sc->GetParameter($spa, "getchanges") == true && $windowSize > 0) {
                 self::$topCollector->AnnounceInformation(sprintf("Streaming data of %d objects", $changecount > $windowSize ? $windowSize : $changecount));
                 // Output message changes per folder
                 self::$encoder->startTag(SYNC_PERFORM);
                 $n = 0;
                 while (1) {
                     try {
                         $progress = $exporter->Synchronize();
                         if (!is_array($progress)) {
                             break;
                         }
                         $n++;
                     } catch (SyncObjectBrokenException $mbe) {
                         $brokenSO = $mbe->GetSyncObject();
                         if (!$brokenSO) {
                             ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Catched SyncObjectBrokenException but broken SyncObject not available. This should be fixed in the backend."));
                         } else {
                             if (!isset($brokenSO->id)) {
                                 $brokenSO->id = "Unknown ID";
                                 ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Catched SyncObjectBrokenException but no ID of object set. This should be fixed in the backend."));
                             }
                             self::$deviceManager->AnnounceIgnoredMessage($spa->GetFolderId(), $brokenSO->id, $brokenSO);
                         }
                     } catch (StatusException $stex) {
                         $status = $stex->getCode();
                         // during export we found out that the states should be thrown away (ZP-623)
                         if ($status == SYNC_STATUS_INVALIDSYNCKEY) {
                             self::$deviceManager->ForceFolderResync($spa->GetFolderId());
                             break;
                         }
                     }
                     if ($n >= $windowSize) {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Exported maxItems of messages: %d / %d", $n, $changecount));
                         break;
                     }
                 }
                 // $progress is not an array when exporting the last message
                 // so we get the number to display from the streamimporter
                 if (isset($streamimporter)) {
                     $n = $streamimporter->GetImportedMessages();
                 }
                 self::$encoder->endTag();
                 self::$topCollector->AnnounceInformation(sprintf("Outgoing %d objects%s", $n, $n >= $windowSize ? " of " . $changecount : ""), true);
                 // update folder status, if there is something set
                 if ($spa->GetFolderSyncRemaining() && $changecount > 0) {
                     $spa->SetFolderSyncRemaining($changecount);
                 }
                 // changecount is initialized with 'false', so 0 means no changes!
                 if ($changecount === 0 || $changecount !== false && $changecount <= $windowSize) {
                     self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_COMPLETED);
                 } else {
                     self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INPROGRESS);
                 }
             }
             self::$encoder->endTag();
             // Save the sync state for the next time
             if ($spa->HasNewSyncKey()) {
                 self::$topCollector->AnnounceInformation("Saving state");
                 try {
                     if (isset($exporter) && $exporter) {
                         $state = $exporter->GetState();
                     } else {
                         if ($sc->GetParameter($spa, "state") !== null) {
                             $state = $sc->GetParameter($spa, "state");
                         } else {
                             if (!$spa->HasSyncKey()) {
                                 $state = "";
                             }
                         }
                     }
                 } catch (StatusException $stex) {
                     $status = $stex->getCode();
                 }
                 if (isset($state) && $status == SYNC_STATUS_SUCCESS) {
                     self::$deviceManager->GetStateManager()->SetSyncState($spa->GetNewSyncKey(), $state, $spa->GetFolderId());
                 } else {
                     ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): error saving '%s' - no state information available", $spa->GetNewSyncKey()));
                 }
             }
             // save SyncParameters
             if ($status == SYNC_STATUS_SUCCESS && empty($actiondata["fetchids"])) {
                 $sc->SaveCollection($spa);
             }
             // reset status for the next folder
             $status = SYNC_STATUS_SUCCESS;
         }
         // END foreach collection
         self::$encoder->endTag();
         //SYNC_FOLDERS
     }
     self::$encoder->endTag();
     //SYNC_SYNCHRONIZE
     return true;
 }
Example #23
0
 public function testDownloadCache()
 {
     $keys = [];
     $keys[] = API::createItem("book", false, false, 'key');
     $keys[] = API::createItem("journalArticle", false, false, 'key');
     $keys[] = API::createItem("newspaperArticle", false, false, 'key');
     $keys[] = API::createItem("magazineArticle", false, false, 'key');
     $keys[] = API::createItem("bookSection", false, false, 'key');
     $keys[] = API::createItem("audioRecording", false, false, 'key');
     $xml1 = Sync::updated(self::$sessionID);
     $xml2 = Sync::updated(self::$sessionID);
     $this->assertEquals(preg_replace('/timestamp="\\d+"/', 'timestamp="--"', $xml1->asXML()), preg_replace('/timestamp="\\d+"/', 'timestamp="--"', $xml2->asXML()));
 }
Example #24
0
 public static function waitForUpload($sessionID, $response, $context, $allowError = false)
 {
     $xml = Sync::getXMLFromResponse($response);
     if (isset($xml->uploaded) || isset($xml->error) && $allowError) {
         return $xml;
     }
     $context->assertTrue(isset($xml->queued));
     $max = 5;
     do {
         $wait = (int) $xml->queued['wait'];
         sleep($wait / 1000);
         $response = Sync::uploadStatus($sessionID, $allowError);
         $xml = Sync::getXMLFromResponse($response);
         $max--;
     } while (isset($xml->queued) && $max > 0);
     if (!$max) {
         $context->fail("Upload did not finish after {$max} attempts");
     }
     if (!$allowError) {
         $context->assertTrue(isset($xml->uploaded));
     }
     return $xml;
 }
Example #25
0
 /**
  * Handles the Sync command
  * Performs the synchronization of messages
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     // Contains all requested folders (containers)
     $sc = new SyncCollections();
     $status = SYNC_STATUS_SUCCESS;
     $wbxmlproblem = false;
     $emptysync = false;
     $this->singleFolder = true;
     $this->multiFolderInfo = array();
     $this->globallyExportedItems = 0;
     // check if the hierarchySync was fully completed
     if (USE_PARTIAL_FOLDERSYNC) {
         if (self::$deviceManager->GetFolderSyncComplete() === false) {
             ZLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): Sync request aborted, as exporting of folders has not yet completed");
             self::$topCollector->AnnounceInformation("Aborted due incomplete folder sync", true);
             $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
         } else {
             ZLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): FolderSync marked as complete");
         }
     }
     // Start Synchronize
     if (self::$decoder->getElementStartTag(SYNC_SYNCHRONIZE)) {
         // AS 1.0 sends version information in WBXML
         if (self::$decoder->getElementStartTag(SYNC_VERSION)) {
             $sync_version = self::$decoder->getElementContent();
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("WBXML sync version: '%s'", $sync_version));
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         // Synching specified folders
         // Android still sends heartbeat sync even if all syncfolders are disabled.
         // Check if Folders tag is empty (<Folders/>) and only sync if there are
         // some folders in the request. See ZP-172
         $startTag = self::$decoder->getElementStartTag(SYNC_FOLDERS);
         if (isset($startTag[EN_FLAGS]) && $startTag[EN_FLAGS]) {
             while (self::$decoder->getElementStartTag(SYNC_FOLDER)) {
                 $actiondata = array();
                 $actiondata["requested"] = true;
                 $actiondata["clientids"] = array();
                 $actiondata["modifyids"] = array();
                 $actiondata["removeids"] = array();
                 $actiondata["fetchids"] = array();
                 $actiondata["statusids"] = array();
                 // read class, synckey and folderid without SyncParameters Object for now
                 $class = $synckey = $folderid = false;
                 // if there are already collections in SyncCollections, this is min. the second folder
                 if ($sc->HasCollections()) {
                     $this->singleFolder = false;
                 }
                 //for AS versions < 2.5
                 if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                     $class = self::$decoder->getElementContent();
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync folder: '%s'", $class));
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 // SyncKey
                 if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
                     $synckey = "0";
                     if (($synckey = self::$decoder->getElementContent()) !== false) {
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 } else {
                     return false;
                 }
                 // FolderId
                 if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
                     $folderid = self::$decoder->getElementContent();
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
                 if (!$folderid && $class) {
                     $folderid = self::$deviceManager->GetFolderIdFromCacheByClass($class);
                 }
                 // folderid HAS TO BE known by now, so we retrieve the correct SyncParameters object for an update
                 try {
                     $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($folderid);
                     // TODO remove resync of folders for < Z-Push 2 beta4 users
                     // this forces a resync of all states previous to Z-Push 2 beta4
                     if (!$spa instanceof SyncParameters) {
                         throw new StateInvalidException("Saved state are not of type SyncParameters");
                     }
                     // new/resync requested
                     if ($synckey == "0") {
                         $spa->RemoveSyncKey();
                         $spa->DelFolderStat();
                         $spa->SetMoveState(false);
                     } else {
                         if ($synckey !== false) {
                             if ($synckey !== $spa->GetSyncKey() && $synckey !== $spa->GetNewSyncKey() || !!$spa->GetMoveState()) {
                                 ZLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Synckey does not match latest saved for this folder or there is a move state, removing folderstat to force Exporter setup");
                                 $spa->DelFolderStat();
                             }
                             $spa->SetSyncKey($synckey);
                         }
                     }
                 } catch (StateInvalidException $stie) {
                     $spa = new SyncParameters();
                     $status = SYNC_STATUS_INVALIDSYNCKEY;
                     self::$topCollector->AnnounceInformation("State invalid - Resync folder", $this->singleFolder);
                     self::$deviceManager->ForceFolderResync($folderid);
                     $this->saveMultiFolderInfo("exception", "StateInvalidException");
                 }
                 // update folderid.. this might be a new object
                 $spa->SetFolderId($folderid);
                 $spa->SetBackendFolderId(self::$deviceManager->GetBackendIdForFolderId($folderid));
                 if ($class !== false) {
                     $spa->SetContentClass($class);
                 }
                 // Get class for as versions >= 12.0
                 if (!$spa->HasContentClass()) {
                     try {
                         $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("GetFolderClassFromCacheByID from Device Manager: '%s' for id:'%s'", $spa->GetContentClass(), $spa->GetFolderId()));
                     } catch (NoHierarchyCacheAvailableException $nhca) {
                         $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                         self::$deviceManager->ForceFullResync();
                     }
                 }
                 // done basic SPA initialization/loading -> add to SyncCollection
                 $sc->AddCollection($spa);
                 $sc->AddParameter($spa, "requested", true);
                 if ($spa->HasContentClass()) {
                     self::$topCollector->AnnounceInformation(sprintf("%s request", $spa->GetContentClass()), $this->singleFolder);
                 } else {
                     ZLog::Write(LOGLEVEL_WARN, "Not possible to determine class of request. Request did not contain class and apparently there is an issue with the HierarchyCache.");
                 }
                 // SUPPORTED properties
                 if (($se = self::$decoder->getElementStartTag(SYNC_SUPPORTED)) !== false) {
                     // ZP-481: LG phones send an empty supported tag, so only read the contents if available here
                     // if <Supported/> is received, it's as no supported fields would have been sent at all.
                     // unsure if this is the correct approach, or if in this case some default list should be used
                     if ($se[EN_FLAGS] & EN_FLAGS_CONTENT) {
                         $supfields = array();
                         WBXMLDecoder::ResetInWhile("syncSupported");
                         while (WBXMLDecoder::InWhile("syncSupported")) {
                             $el = self::$decoder->getElement();
                             if ($el[EN_TYPE] == EN_TYPE_ENDTAG) {
                                 break;
                             } else {
                                 $supfields[] = $el[EN_TAG];
                             }
                         }
                         self::$deviceManager->SetSupportedFields($spa->GetFolderId(), $supfields);
                     }
                 }
                 // Deletes as moves can be an empty tag as well as have value
                 if (self::$decoder->getElementStartTag(SYNC_DELETESASMOVES)) {
                     $spa->SetDeletesAsMoves(true);
                     if (($dam = self::$decoder->getElementContent()) !== false) {
                         $spa->SetDeletesAsMoves((bool) $dam);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 }
                 // Get changes can be an empty tag as well as have value
                 // code block partly contributed by dw2412
                 if (self::$decoder->getElementStartTag(SYNC_GETCHANGES)) {
                     $sc->AddParameter($spa, "getchanges", true);
                     if (($gc = self::$decoder->getElementContent()) !== false) {
                         $sc->AddParameter($spa, "getchanges", $gc);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 }
                 if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
                     $ws = self::$decoder->getElementContent();
                     // normalize windowsize - see ZP-477
                     if ($ws == 0 || $ws > WINDOW_SIZE_MAX) {
                         $ws = WINDOW_SIZE_MAX;
                     }
                     $spa->SetWindowSize($ws);
                     // also announce the currently requested window size to the DeviceManager
                     self::$deviceManager->SetWindowSize($spa->GetFolderId(), $spa->GetWindowSize());
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 // conversation mode requested
                 if (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
                     $spa->SetConversationMode(true);
                     if (($conversationmode = self::$decoder->getElementContent()) !== false) {
                         $spa->SetConversationMode((bool) $conversationmode);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 }
                 // Do not truncate by default
                 $spa->SetTruncation(SYNC_TRUNCATION_ALL);
                 // use default conflict handling if not specified by the mobile
                 $spa->SetConflict(SYNC_CONFLICT_DEFAULT);
                 // save the current filtertype because it might have been changed on the mobile
                 $currentFilterType = $spa->GetFilterType();
                 while (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
                     $firstOption = true;
                     WBXMLDecoder::ResetInWhile("syncOptions");
                     while (WBXMLDecoder::InWhile("syncOptions")) {
                         // foldertype definition
                         if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                             $foldertype = self::$decoder->getElementContent();
                             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): specified options block with foldertype '%s'", $foldertype));
                             // switch the foldertype for the next options
                             $spa->UseCPO($foldertype);
                             // save the current filtertype because it might have been changed on the mobile
                             $currentFilterType = $spa->GetFilterType();
                             // set to synchronize all changes. The mobile could overwrite this value
                             $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         } else {
                             if ($firstOption) {
                                 $spa->UseCPO();
                                 // save the current filtertype because it might have been changed on the mobile
                                 $currentFilterType = $spa->GetFilterType();
                                 // set to synchronize all changes. The mobile could overwrite this value
                                 $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
                             }
                         }
                         $firstOption = false;
                         if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
                             $spa->SetFilterType(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_TRUNCATION)) {
                             $spa->SetTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_RTFTRUNCATION)) {
                             $spa->SetRTFTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
                             $spa->SetMimeSupport(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_MIMETRUNCATION)) {
                             $spa->SetMimeTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_CONFLICT)) {
                             $spa->SetConflict(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) {
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
                                 $bptype = self::$decoder->getElementContent();
                                 $spa->BodyPreference($bptype);
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
                                 $spa->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
                                 $spa->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
                                 $spa->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         $e = self::$decoder->peek();
                         if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
                             self::$decoder->getElementEndTag();
                             break;
                         }
                     }
                 }
                 // limit items to be synchronized to the mobiles if configured
                 if (defined('SYNC_FILTERTIME_MAX') && SYNC_FILTERTIME_MAX > SYNC_FILTERTYPE_ALL && (!$spa->HasFilterType() || $spa->GetFilterType() == SYNC_FILTERTYPE_ALL || $spa->GetFilterType() > SYNC_FILTERTIME_MAX)) {
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("SYNC_FILTERTIME_MAX defined. Filter set to value: %s", SYNC_FILTERTIME_MAX));
                     $spa->SetFilterType(SYNC_FILTERTIME_MAX);
                 }
                 // unset filtertype for KOE GAB folder
                 if (KOE_CAPABILITY_GAB && self::$deviceManager->IsKoe() && $spa->GetBackendFolderId() == self::$deviceManager->GetKoeGabBackendFolderId()) {
                     $spa->SetFilterType(SYNC_FILTERTYPE_ALL);
                     ZLog::Write(LOGLEVEL_DEBUG, "HandleSync(): KOE GAB folder - setting filter type to unlimited");
                 }
                 if ($currentFilterType != $spa->GetFilterType()) {
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): filter type has changed (old: '%s', new: '%s'), removing folderstat to force Exporter setup", $currentFilterType, $spa->GetFilterType()));
                     $spa->DelFolderStat();
                 }
                 // Check if the hierarchycache is available. If not, trigger a HierarchySync
                 if (self::$deviceManager->IsHierarchySyncRequired()) {
                     $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                     ZLog::Write(LOGLEVEL_DEBUG, "HierarchyCache is also not available. Triggering HierarchySync to device");
                 }
                 if (($el = self::$decoder->getElementStartTag(SYNC_PERFORM)) && $el[EN_FLAGS] & EN_FLAGS_CONTENT) {
                     // We can not proceed here as the content class is unknown
                     if ($status != SYNC_STATUS_SUCCESS) {
                         ZLog::Write(LOGLEVEL_WARN, "Ignoring all incoming actions as global status indicates problem.");
                         $wbxmlproblem = true;
                         break;
                     }
                     $performaction = true;
                     // unset the importer
                     $this->importer = false;
                     $nchanges = 0;
                     WBXMLDecoder::ResetInWhile("syncActions");
                     while (WBXMLDecoder::InWhile("syncActions")) {
                         // ADD, MODIFY, REMOVE or FETCH
                         $element = self::$decoder->getElement();
                         if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
                             self::$decoder->ungetElement($element);
                             break;
                         }
                         if ($status == SYNC_STATUS_SUCCESS) {
                             $nchanges++;
                         }
                         // Foldertype sent when synching SMS
                         if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                             $foldertype = self::$decoder->getElementContent();
                             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): incoming data with foldertype '%s'", $foldertype));
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         } else {
                             $foldertype = false;
                         }
                         $serverid = false;
                         if (self::$decoder->getElementStartTag(SYNC_SERVERENTRYID)) {
                             if (($serverid = self::$decoder->getElementContent()) !== false) {
                                 if (!self::$decoder->getElementEndTag()) {
                                     // end serverid
                                     return false;
                                 }
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_CLIENTENTRYID)) {
                             $clientid = self::$decoder->getElementContent();
                             if (!self::$decoder->getElementEndTag()) {
                                 // end clientid
                                 return false;
                             }
                         } else {
                             $clientid = false;
                         }
                         // Get the SyncMessage if sent
                         if (($el = self::$decoder->getElementStartTag(SYNC_DATA)) && $el[EN_FLAGS] & EN_FLAGS_CONTENT) {
                             $message = ZPush::getSyncObjectFromFolderClass($spa->GetContentClass());
                             // KOE ZO-42: OL sends Notes as Appointments
                             if ($spa->GetContentClass() == "Notes" && KOE_CAPABILITY_NOTES && self::$deviceManager->IsKoe()) {
                                 ZLog::Write(LOGLEVEL_DEBUG, "HandleSync(): KOE sends Notes as Appointments, read as SyncAppointment and convert it into a SyncNote object.");
                                 $message = new SyncAppointment();
                                 $message->Decode(self::$decoder);
                                 $note = new SyncNote();
                                 if (isset($message->asbody)) {
                                     $note->asbody = $message->asbody;
                                 }
                                 if (isset($message->categories)) {
                                     $note->categories = $message->categories;
                                 }
                                 if (isset($message->subject)) {
                                     $note->subject = $message->subject;
                                 }
                                 if (isset($message->dtstamp)) {
                                     $note->lastmodified = $message->dtstamp;
                                 }
                                 // set SyncNote->Color from a color category
                                 $note->SetColorFromCategory();
                                 $message = $note;
                             } else {
                                 $message->Decode(self::$decoder);
                                 // set Ghosted fields
                                 $message->emptySupported(self::$deviceManager->GetSupportedFields($spa->GetFolderId()));
                             }
                             if (!self::$decoder->getElementEndTag()) {
                                 // end applicationdata
                                 return false;
                             }
                         } else {
                             $message = false;
                         }
                         switch ($element[EN_TAG]) {
                             case SYNC_FETCH:
                                 array_push($actiondata["fetchids"], $serverid);
                                 break;
                             default:
                                 // get the importer
                                 if ($this->importer == false) {
                                     $status = $this->getImporter($sc, $spa, $actiondata);
                                 }
                                 if ($status == SYNC_STATUS_SUCCESS) {
                                     $this->importMessage($spa, $actiondata, $element[EN_TAG], $message, $clientid, $serverid, $foldertype, $nchanges);
                                 } else {
                                     ZLog::Write(LOGLEVEL_WARN, "Ignored incoming change, global status indicates problem.");
                                 }
                                 break;
                         }
                         if ($actiondata["fetchids"]) {
                             self::$topCollector->AnnounceInformation(sprintf("Fetching %d", $nchanges));
                         } else {
                             self::$topCollector->AnnounceInformation(sprintf("Incoming %d", $nchanges));
                         }
                         if (!self::$decoder->getElementEndTag()) {
                             // end add/change/delete/move
                             return false;
                         }
                     }
                     if ($status == SYNC_STATUS_SUCCESS && $this->importer !== false) {
                         ZLog::Write(LOGLEVEL_INFO, sprintf("Processed '%d' incoming changes", $nchanges));
                         if (!$actiondata["fetchids"]) {
                             self::$topCollector->AnnounceInformation(sprintf("%d incoming", $nchanges), $this->singleFolder);
                             $this->saveMultiFolderInfo("incoming", $nchanges);
                         }
                         try {
                             // Save the updated state, which is used for the exporter later
                             $sc->AddParameter($spa, "state", $this->importer->GetState());
                         } catch (StatusException $stex) {
                             $status = $stex->getCode();
                         }
                     }
                     if (!self::$decoder->getElementEndTag()) {
                         // end PERFORM
                         return false;
                     }
                 }
                 // save the failsave state
                 if (!empty($actiondata["statusids"])) {
                     unset($actiondata["failstate"]);
                     $actiondata["failedsyncstate"] = $sc->GetParameter($spa, "state");
                     self::$deviceManager->GetStateManager()->SetSyncFailState($actiondata);
                 }
                 // save actiondata
                 $sc->AddParameter($spa, "actiondata", $actiondata);
                 if (!self::$decoder->getElementEndTag()) {
                     // end collection
                     return false;
                 }
                 // AS14 does not send GetChanges anymore. We should do it if there were no incoming changes
                 if (!isset($performaction) && !$sc->GetParameter($spa, "getchanges") && $spa->HasSyncKey()) {
                     $sc->AddParameter($spa, "getchanges", true);
                 }
             }
             // END FOLDER
             if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) {
                 // end collections
                 return false;
             }
         }
         // end FOLDERS
         if (self::$decoder->getElementStartTag(SYNC_HEARTBEATINTERVAL)) {
             $hbinterval = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_HEARTBEATINTERVAL
                 return false;
             }
         }
         if (self::$decoder->getElementStartTag(SYNC_WAIT)) {
             $wait = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_WAIT
                 return false;
             }
             // internally the heartbeat interval and the wait time are the same
             // heartbeat is in seconds, wait in minutes
             $hbinterval = $wait * 60;
         }
         if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
             $sc->SetGlobalWindowSize(self::$decoder->getElementContent());
             ZLog::Write(LOGLEVEL_DEBUG, "Sync(): Global WindowSize requested: " . $sc->GetGlobalWindowSize());
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_WINDOWSIZE
                 return false;
             }
         }
         if (self::$decoder->getElementStartTag(SYNC_PARTIAL)) {
             $partial = true;
         } else {
             $partial = false;
         }
         if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) {
             // end sync
             return false;
         }
     } else {
         $emptysync = true;
     }
     // END SYNCHRONIZE
     // check heartbeat/wait time
     if (isset($hbinterval)) {
         if ($hbinterval < 60 || $hbinterval > 3540) {
             $status = SYNC_STATUS_INVALIDWAITORHBVALUE;
             ZLog::Write(LOGLEVEL_WARN, sprintf("HandleSync(): Invalid heartbeat or wait value '%s'", $hbinterval));
         }
     }
     // Partial & Empty Syncs need saved data to proceed with synchronization
     if ($status == SYNC_STATUS_SUCCESS && ($emptysync === true || $partial === true)) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Partial or Empty sync requested. Retrieving data of synchronized folders."));
         // Load all collections - do not overwrite existing (received!), load states, check permissions and only load confirmed states!
         try {
             $sc->LoadAllCollections(false, true, true, true, true);
         } catch (StateInvalidException $siex) {
             $status = SYNC_STATUS_INVALIDSYNCKEY;
             self::$topCollector->AnnounceInformation("StateNotFoundException", $this->singleFolder);
             $this->saveMultiFolderInfo("exeption", "StateNotFoundException");
         } catch (StatusException $stex) {
             $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
             self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
             $this->saveMultiFolderInfo("exeption", "StatusException");
         }
         // update a few values
         foreach ($sc as $folderid => $spa) {
             // manually set getchanges parameter for this collection if it is synchronized
             if ($spa->HasSyncKey()) {
                 $sc->AddParameter($spa, "getchanges", true);
                 // announce WindowSize to DeviceManager
                 self::$deviceManager->SetWindowSize($folderid, $spa->GetWindowSize());
             }
         }
         if (!$sc->HasCollections()) {
             $status = SYNC_STATUS_SYNCREQUESTINCOMPLETE;
         }
     } else {
         if (isset($hbinterval)) {
             // load the hierarchy data - there are no permissions to verify so we just set it to false
             if (!$sc->LoadCollection(false, true, false)) {
                 $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                 self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
                 $this->saveMultiFolderInfo("exeption", "StatusException");
             }
         }
     }
     // HEARTBEAT & Empty sync
     if ($status == SYNC_STATUS_SUCCESS && (isset($hbinterval) || $emptysync == true)) {
         $interval = defined('PING_INTERVAL') && PING_INTERVAL > 0 ? PING_INTERVAL : 30;
         if (isset($hbinterval)) {
             $sc->SetLifetime($hbinterval);
         }
         // states are lazy loaded - we have to make sure that they are there!
         $loadstatus = SYNC_STATUS_SUCCESS;
         foreach ($sc as $folderid => $spa) {
             // some androids do heartbeat on the OUTBOX folder, with weird results - ZP-362
             // we do not load the state so we will never get relevant changes on the OUTBOX folder
             if (self::$deviceManager->GetFolderTypeFromCacheById($folderid) == SYNC_FOLDER_TYPE_OUTBOX) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Heartbeat on Outbox folder not allowed"));
                 continue;
             }
             $fad = array();
             // if loading the states fails, we do not enter heartbeat, but we keep $status on SYNC_STATUS_SUCCESS
             // so when the changes are exported the correct folder gets an SYNC_STATUS_INVALIDSYNCKEY
             if ($loadstatus == SYNC_STATUS_SUCCESS) {
                 $loadstatus = $this->loadStates($sc, $spa, $fad);
             }
         }
         if ($loadstatus == SYNC_STATUS_SUCCESS) {
             $foundchanges = false;
             try {
                 // always check for changes
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Entering Heartbeat mode"));
                 $foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval);
             } catch (StatusException $stex) {
                 if ($stex->getCode() == SyncCollections::OBSOLETE_CONNECTION) {
                     $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                 } else {
                     $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                     self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
                     $this->saveMultiFolderInfo("exeption", "StatusException");
                 }
             }
             // in case there are no changes and no other request has synchronized while we waited, we can reply with an empty response
             if (!$foundchanges && $status == SYNC_STATUS_SUCCESS) {
                 // if there were changes to the SPA or CPOs we need to save this before we terminate
                 // only save if the state was not modified by some other request, if so, return state invalid status
                 foreach ($sc as $folderid => $spa) {
                     if (self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
                         $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                     } else {
                         $sc->SaveCollection($spa);
                     }
                 }
                 if ($status == SYNC_STATUS_SUCCESS) {
                     ZLog::Write(LOGLEVEL_DEBUG, "No changes found and no other process changed states. Replying with empty response and closing connection.");
                     self::$specialHeaders = array();
                     self::$specialHeaders[] = "Connection: close";
                     return true;
                 }
             }
             if ($foundchanges) {
                 foreach ($sc->GetChangedFolderIds() as $folderid => $changecount) {
                     // check if there were other sync requests for a folder during the heartbeat
                     $spa = $sc->GetCollection($folderid);
                     if ($changecount > 0 && $sc->WaitedForChanges() && self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s' which was already synchronized. Heartbeat aborted!", $changecount, $folderid));
                         $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                     } else {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s'", $changecount, $folderid));
                     }
                 }
             }
         }
     }
     // Start the output
     ZLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Start Output");
     // global status
     // SYNC_COMMONSTATUS_* start with values from 101
     if ($status != SYNC_COMMONSTATUS_SUCCESS && ($status == SYNC_STATUS_FOLDERHIERARCHYCHANGED || $status > 100)) {
         $this->sendStartTags();
         self::$encoder->startTag(SYNC_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
         self::$encoder->endTag();
         // SYNC_SYNCHRONIZE
         return true;
     }
     // Loop through requested folders
     foreach ($sc as $folderid => $spa) {
         // get actiondata
         $actiondata = $sc->GetParameter($spa, "actiondata");
         if ($status == SYNC_STATUS_SUCCESS && (!$spa->GetContentClass() || !$spa->GetFolderId())) {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): no content class or folderid found for collection."));
             continue;
         }
         if (!$sc->GetParameter($spa, "requested")) {
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): partial sync for folder class '%s' with id '%s'", $spa->GetContentClass(), $spa->GetFolderId()));
         }
         // initialize exporter to get changecount
         $changecount = false;
         $exporter = false;
         $streamimporter = false;
         $newFolderStat = false;
         $setupExporter = true;
         // TODO we could check against $sc->GetChangedFolderIds() on heartbeat so we do not need to configure all exporter again
         if ($status == SYNC_STATUS_SUCCESS && ($sc->GetParameter($spa, "getchanges") || !$spa->HasSyncKey())) {
             // no need to run the exporter if the globalwindowsize is already full
             if ($sc->GetGlobalWindowSize() == $this->globallyExportedItems) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as GlobalWindowSize is full.", $spa->GetFolderId()));
                 $setupExporter = false;
             }
             // if the maximum request timeout is reached, stop processing other collections
             if (Request::IsRequestTimeoutReached()) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync(): no exporter setup for '%s' as request timeout reached, omitting output for collection.", $spa->GetFolderId()));
                 $setupExporter = false;
             }
             // compare the folder statistics if the backend supports this
             if ($setupExporter && self::$backend->HasFolderStats()) {
                 // check if the folder stats changed -> if not, don't setup the exporter, there are no changes!
                 $newFolderStat = self::$backend->GetFolderStat(ZPush::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()), $spa->GetBackendFolderId());
                 if ($newFolderStat !== false && !$spa->IsExporterRunRequired($newFolderStat, true)) {
                     $changecount = 0;
                     $setupExporter = false;
                 }
             }
             // Do a full Exporter setup if we can't avoid it
             if ($setupExporter) {
                 //make sure the states are loaded
                 $status = $this->loadStates($sc, $spa, $actiondata);
                 if ($status == SYNC_STATUS_SUCCESS) {
                     try {
                         // if this is an additional folder the backend has to be setup correctly
                         if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
                             throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
                         }
                         // Use the state from the importer, as changes may have already happened
                         $exporter = self::$backend->GetExporter($spa->GetBackendFolderId());
                         if ($exporter === false) {
                             throw new StatusException(sprintf("HandleSync() could not get an exporter for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
                         }
                     } catch (StatusException $stex) {
                         $status = $stex->getCode();
                     }
                     try {
                         // Stream the messages directly to the PDA
                         $streamimporter = new ImportChangesStream(self::$encoder, ZPush::getSyncObjectFromFolderClass($spa->GetContentClass()));
                         if ($exporter !== false) {
                             $exporter->SetMoveStates($spa->GetMoveState());
                             $exporter->Config($sc->GetParameter($spa, "state"));
                             $exporter->ConfigContentParameters($spa->GetCPO());
                             $exporter->InitializeExporter($streamimporter);
                             $changecount = $exporter->GetChangeCount();
                         }
                     } catch (StatusException $stex) {
                         if ($stex->getCode() === SYNC_FSSTATUS_CODEUNKNOWN && $spa->HasSyncKey()) {
                             $status = SYNC_STATUS_INVALIDSYNCKEY;
                         } else {
                             $status = $stex->getCode();
                         }
                     }
                     if (!$spa->HasSyncKey()) {
                         self::$topCollector->AnnounceInformation(sprintf("Exporter registered. %d objects queued.", $changecount), $this->singleFolder);
                         $this->saveMultiFolderInfo("queued", $changecount);
                         // update folder status as initialized
                         $spa->SetFolderSyncTotal($changecount);
                         $spa->SetFolderSyncRemaining($changecount);
                         if ($changecount > 0) {
                             self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INITIALIZED);
                         }
                     } else {
                         if ($status != SYNC_STATUS_SUCCESS) {
                             self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), $this->singleFolder);
                             $this->saveMultiFolderInfo("exception", "StatusException");
                         }
                     }
                 }
             }
         }
         // Get a new sync key to output to the client if any changes have been send by the mobile or a new synckey is to be sent
         if (!empty($actiondata["modifyids"]) || !empty($actiondata["clientids"]) || !empty($actiondata["removeids"]) || !$spa->HasSyncKey() && $status == SYNC_STATUS_SUCCESS) {
             $spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey()));
         } else {
             // when reaching the global limit for changes of all collections, stop processing other collections (ZP-697)
             if ($sc->GetGlobalWindowSize() <= $this->globallyExportedItems) {
                 ZLog::Write(LOGLEVEL_DEBUG, "Global WindowSize for amount of exported changes reached, omitting output for collection.");
                 continue;
             }
             // get a new synckey if there are changes are we did not reach the limit yet
             if ($changecount > 0) {
                 $spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey()));
             }
         }
         // Fir AS 14.0+ omit output for folder, if there were no incoming or outgoing changes and no Fetch
         if (Request::GetProtocolVersion() >= 14.0 && !$spa->HasNewSyncKey() && $changecount == 0 && empty($actiondata["fetchids"]) && $status == SYNC_STATUS_SUCCESS && ($newFolderStat === false || !$spa->IsExporterRunRequired($newFolderStat))) {
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync: No changes found for %s folder id '%s'. Omitting output.", $spa->GetContentClass(), $spa->GetFolderId()));
             continue;
         }
         // if there are no other responses sent, we should end with a global status
         if ($status == SYNC_STATUS_FOLDERHIERARCHYCHANGED && $this->startTagsSent === false) {
             $this->sendStartTags();
             self::$encoder->startTag(SYNC_STATUS);
             self::$encoder->content($status);
             self::$encoder->endTag();
             self::$encoder->endTag();
             // SYNC_SYNCHRONIZE
             return true;
         }
         // there is something to send here, sync folder to output
         $this->syncFolder($sc, $spa, $exporter, $changecount, $streamimporter, $status, $newFolderStat);
         // reset status for the next folder
         $status = SYNC_STATUS_SUCCESS;
     }
     // END foreach collection
     //SYNC_FOLDERS - only if the starttag was sent
     if ($this->startFolderTagSent) {
         self::$encoder->endTag();
     }
     //SYNC_SYNCHRONIZE - only if the starttag was sent
     if ($this->startTagsSent) {
         self::$encoder->endTag();
     }
     // final top announcement for a multi-folder sync
     if ($sc->GetCollectionCount() > 1) {
         self::$topCollector->AnnounceInformation($this->getMultiFolderInfoLine($sc->GetCollectionCount()), true);
     }
     return true;
 }
Example #26
0
 public function testTagDeleteUnmodifiedItemChange()
 {
     $key = 'AAAAAAAA';
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     // Create item via sync
     $data = '<data version="9"><items><item libraryID="' . self::$config['libraryID'] . '" itemType="book" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="' . $key . '"/></items>' . '<tags><tag libraryID="' . self::$config['libraryID'] . '" name="Test" ' . 'dateAdded="2009-03-07 04:54:56" ' . 'dateModified="2009-03-07 04:54:56" ' . 'key="BBBBBBBB">' . '<items>' . $key . '</items>' . '</tag></tags></data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Get item via API
     $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=json");
     $xml = API::getXMLFromResponse($response);
     $data = API::parseDataFromAtomEntry($xml);
     $json = json_decode($data['content']);
     $originalVersion = $data['version'];
     $this->assertCount(1, $json->tags);
     $this->assertTrue(isset($json->tags[0]->tag));
     $this->assertEquals("Test", $json->tags[0]->tag);
     // Get item via sync
     $xml = Sync::updated(self::$sessionID);
     $this->assertEquals(1, sizeOf($xml->updated->items->item));
     $this->assertEquals(1, sizeOf($xml->updated->tags->tag));
     $this->assertEquals(1, sizeOf($xml->updated->tags->tag[0]->items));
     $lastsync = (int) $xml['timestamp'];
     usleep(1500000);
     // Increment the library version, since we're testing the
     // version below
     API::createItem('newspaperArticle', false, false, 'key');
     $libraryVersion = API::getLibraryVersion();
     $xml = Sync::updated(self::$sessionID);
     $updateKey = (string) $xml['updateKey'];
     // Delete tag via sync, with unmodified item
     $data = '<data version="9"><items><item libraryID="' . self::$config['libraryID'] . '" itemType="book" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="' . $key . '"/></items>' . '<deleted><tags><tag libraryID="' . self::$config['libraryID'] . '" key="BBBBBBBB"/>' . '</tags></deleted></data>';
     $response = Sync::upload(self::$sessionID, $updateKey, $data);
     Sync::waitForUpload(self::$sessionID, $response, $this);
     // Get item via sync
     $xml = Sync::updated(self::$sessionID);
     $this->assertEquals(1, sizeOf(isset($xml->updated->tags->tag)));
     $this->assertFalse(isset($xml->updated->tags->tag[0]->items));
     // Get item version via API
     $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=json");
     $xml = API::getXMLFromResponse($response);
     $data = API::parseDataFromAtomEntry($xml);
     $json = json_decode($data['content']);
     $this->assertEquals(0, (int) array_shift($xml->xpath('/atom:entry/zapi:numTags')));
     $this->assertCount(0, $json->tags);
     // New item version should be greater than before
     $this->assertGreaterThan($originalVersion, $data['version']);
     // And should be one more than previous version
     $this->assertEquals($libraryVersion + 1, $data['version']);
     // Only the newspaperArticle should be updated
     $xml = Sync::updated(self::$sessionID, $lastsync);
     $this->assertEquals(1, $xml->updated[0]->items[0]->count());
 }
Example #27
0
<?php

// Load module and PHP classes.
include "example.php";
echo "Got new object\n";
echo "Got string {$s} and value {$x} \n";
$s = new Sync();
echo "Got new object\n";
$s->printer();
?>

Example #28
0
 /**
  * @expectedException InvalidArgumentException
  */
 function testException()
 {
     Sync::call(function () {
         throw new \InvalidArgumentException('Oops');
     });
 }
Example #29
0
<?php

/**
* This small piece of php code calls the syncdata() function of Sync class.
* @author Daan Janssens, mentored by Matthew Lagoe
*/
require '../../config.php';
require_once $AMS_LIB . '/libinclude.php';
Sync::syncdata();
Example #30
0
 session_cache_limiter('nocache');
 session_start();
 header("Expires: Mon, 01 May 2000 06:00:00 GMT");
 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
 header("Cache-Control: max-age=1");
 header("Cache-Control: no-store, no-cache, must-revalidate");
 header("Cache-Control: post-check=0, pre-check=0", false);
 header("Pragma: no-cache");
 // Running Cron
 if (isset($_GET["cron"])) {
     if ($_GET["cron"] == "true") {
         Sync::syncdata(false);
     }
 }
 // Always try to sync on page load, ie "lazy" cron
 Sync::syncdata(false);
 // Decide what page to load
 if (!isset($_GET["page"])) {
     if (isset($_SESSION['user'])) {
         if (Ticket_User::isMod(unserialize($_SESSION['ticket_user']))) {
             $page = 'dashboard';
         } else {
             $page = 'show_user';
         }
     } else {
         // default page
         $page = 'login';
     }
 } else {
     // if the session exists load page with $_GET requests
     if (isset($_SESSION['user'])) {