/** * Tears down the fixture * This method is called after a test is executed. * * @access public */ public function tearDown() { Syncroton_Registry::set(Syncroton_Registry::PING_TIMEOUT, 60); Syncroton_Registry::set(Syncroton_Registry::QUIET_TIME, 120); Syncroton_Registry::getTransactionManager()->rollBack(); Syncroton_Registry::getDatabase()->query('delete from syncroton_device'); }
/** * @param unknown_type $_class * @param Syncroton_Model_IDevice $_device * @param DateTime $_timeStamp * @throws InvalidArgumentException * @return Syncroton_Data_IData */ public static function factory($_classFactory, Syncroton_Model_IDevice $_device, DateTime $_timeStamp) { switch ($_classFactory) { case self::CLASS_CALENDAR: $className = Syncroton_Registry::get(Syncroton_Registry::CALENDAR_DATA_CLASS); break; case self::CLASS_CONTACTS: $className = Syncroton_Registry::get(Syncroton_Registry::CONTACTS_DATA_CLASS); break; case self::STORE_EMAIL: case self::CLASS_EMAIL: $className = Syncroton_Registry::get(Syncroton_Registry::EMAIL_DATA_CLASS); break; case self::CLASS_NOTES: $className = Syncroton_Registry::get(Syncroton_Registry::NOTES_DATA_CLASS); break; case self::CLASS_TASKS: $className = Syncroton_Registry::get(Syncroton_Registry::TASKS_DATA_CLASS); break; case self::STORE_GAL: $className = Syncroton_Registry::get(Syncroton_Registry::GAL_DATA_CLASS); break; default: throw new Syncroton_Exception_UnexpectedValue('invalid class type provided'); breeak; } $class = new $className($_device, $_timeStamp); if (!$class instanceof Syncroton_Data_IData) { throw new RuntimeException('class must be instanceof Syncroton_Data_IData'); } return $class; }
/** * Returns TRUE if the $index is a named value in the registry, * or FALSE if $index was not found in the registry. * * @param string $index * @return boolean */ public static function isRegistered($index) { if (self::$_registry === null) { return false; } return self::$_registry->offsetExists($index); }
/** * the constructor * * @param Syncroton_Model_IDevice $_device * @param DateTime $_timeStamp */ public function __construct(Syncroton_Model_IDevice $_device, DateTime $_timeStamp) { $this->_device = $_device; $this->_timeStamp = $_timeStamp; $this->_db = Syncroton_Registry::getDatabase(); $this->_tablePrefix = 'Syncroton_'; $this->_ownerId = '1234'; }
/** * the constructor * * @param Syncroton_Model_IDevice $_device * @param DateTime $_timeStamp */ public function __construct(Syncroton_Model_IDevice $_device, DateTime $_timeStamp) { App::uses('LilCurrentUser', 'Lil.Lib'); $currentUser = LilCurrentUser::getInstance(); $this->_device = $_device; $this->_timeStamp = $_timeStamp; $this->_db = Syncroton_Registry::getDatabase(); $this->_tablePrefix = 'Syncroton_'; $this->_ownerId = $currentUser->get('id'); }
/** * test xml generation for IPhone */ public function testSetDeviceInformation() { // delete folder created above $doc = new DOMDocument(); $doc->loadXML('<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> <Settings xmlns="uri:Settings"><DeviceInformation><Set><Model>iPhone</Model><IMEI>086465r87697</IMEI></Set></DeviceInformation></Settings>'); $folderDelete = new Syncroton_Command_Settings($doc, $this->_device, null); $folderDelete->handle(); $responseDoc = $folderDelete->getResponse(); #$responseDoc->formatOutput = true; echo $responseDoc->saveXML(); $xpath = new DomXPath($responseDoc); $xpath->registerNamespace('Settings', 'uri:Settings'); $nodes = $xpath->query('//Settings:Settings/Settings:Status'); $this->assertEquals(1, $nodes->length, $responseDoc->saveXML()); $this->assertEquals(Syncroton_Command_Settings::STATUS_SUCCESS, $nodes->item(0)->nodeValue, $responseDoc->saveXML()); $updatedDevice = Syncroton_Registry::getDeviceBackend()->get($this->_device->id); $this->assertEquals('086465r87697', $updatedDevice->imei); }
/** * (non-PHPdoc) * @see ActiveSync/ActiveSync_TestCase::setUp() */ protected function setUp() { parent::setUp(); // speed up tests Syncroton_Registry::set(Syncroton_Registry::PING_TIMEOUT, 1); Syncroton_Registry::set(Syncroton_Registry::QUIET_TIME, 1); $this->_setGeoData = Addressbook_Controller_Contact::getInstance()->setGeoDataForContacts(FALSE); Syncroton_Registry::setDatabase(Tinebase_Core::getDb()); Syncroton_Registry::setTransactionManager(Tinebase_TransactionManager::getInstance()); Syncroton_Registry::set(Syncroton_Registry::DEVICEBACKEND, new Syncroton_Backend_Device(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set(Syncroton_Registry::FOLDERBACKEND, new Syncroton_Backend_Folder(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set(Syncroton_Registry::SYNCSTATEBACKEND, new Syncroton_Backend_SyncState(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set(Syncroton_Registry::CONTENTSTATEBACKEND, new Syncroton_Backend_Content(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set('loggerBackend', Tinebase_Core::getLogger()); Syncroton_Registry::setContactsDataClass('Addressbook_Frontend_ActiveSync'); Syncroton_Registry::setCalendarDataClass('Calendar_Frontend_ActiveSync'); Syncroton_Registry::setEmailDataClass('Felamimail_Frontend_ActiveSync'); Syncroton_Registry::setTasksDataClass('Tasks_Frontend_ActiveSync'); $this->_device = Syncroton_Registry::getDeviceBackend()->create(ActiveSync_TestCase::getTestDevice()); }
/** * constructor */ private function __construct() { if (Syncroton_Registry::isRegistered('loggerBackend')) { $this->_logger = Syncroton_Registry::get('loggerBackend'); } }
/** * return active device * * @param string $_deviceType * @return ActiveSync_Model_Device */ protected function _getDevice($_deviceType) { if (!isset($this->objects['devices'][$_deviceType])) { $this->objects['devices'][$_deviceType] = Syncroton_Registry::getDeviceBackend()->create(ActiveSync_TestCase::getTestDevice($_deviceType)); } return $this->objects['devices'][$_deviceType]; }
/** * the constructor * * @param mixed $requestBody * @param Syncroton_Model_Device $device * @param array $requestParameters */ public function __construct($requestBody, Syncroton_Model_IDevice $device, $requestParameters) { $this->_requestBody = $requestBody; $this->_device = $device; $this->_requestParameters = $requestParameters; $this->_policyKey = $requestParameters['policyKey']; $this->_deviceBackend = Syncroton_Registry::getDeviceBackend(); $this->_folderBackend = Syncroton_Registry::getFolderBackend(); $this->_syncStateBackend = Syncroton_Registry::getSyncStateBackend(); $this->_contentStateBackend = Syncroton_Registry::getContentStateBackend(); $this->_policyBackend = Syncroton_Registry::getPolicyBackend(); if (Syncroton_Registry::isRegistered('loggerBackend')) { $this->_logger = Syncroton_Registry::get('loggerBackend'); } $this->_syncTimeStamp = new DateTime(null, new DateTimeZone('UTC')); // set default content type $this->_headers['Content-Type'] = 'application/vnd.ms-sync.wbxml'; if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " sync timestamp: " . $this->_syncTimeStamp->format('Y-m-d H:i:s')); } if (isset($this->_defaultNameSpace) && isset($this->_documentElement)) { // Creates an instance of the DOMImplementation class $imp = new DOMImplementation(); // Creates a DOMDocumentType instance $dtd = $imp->createDocumentType('AirSync', "-//AIRSYNC//DTD AirSync//EN", "http://www.microsoft.com/"); // Creates a DOMDocument instance $this->_outputDom = $imp->createDocument($this->_defaultNameSpace, $this->_documentElement, $dtd); $this->_outputDom->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:Syncroton', 'uri:Syncroton'); $this->_outputDom->formatOutput = false; $this->_outputDom->encoding = 'utf-8'; } if ($this->_skipValidatePolicyKey != true) { if (!empty($this->_device->policyId)) { $policy = $this->_policyBackend->get($this->_device->policyId); if ((int) $policy->policyKey != (int) $this->_policyKey) { $this->_outputDom->documentElement->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', 142)); $sepn = new Syncroton_Exception_ProvisioningNeeded(); $sepn->domDocument = $this->_outputDom; throw $sepn; } // should we wipe the mobile phone? if ($this->_device->remotewipe >= Syncroton_Command_Provision::REMOTEWIPE_REQUESTED) { $this->_outputDom->documentElement->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', 140)); $sepn = new Syncroton_Exception_ProvisioningNeeded(); $sepn->domDocument = $this->_outputDom; throw $sepn; } } } }
/** * (non-PHPdoc) * @see Syncroton_Command_Wbxml::getResponse() */ public function getResponse() { $sync = $this->_outputDom->documentElement; $collections = $this->_outputDom->createElementNS('uri:AirSync', 'Collections'); $totalChanges = 0; // Detect devices that do not support empty Sync reponse $emptySyncSupported = !preg_match('/(meego|nokian800)/i', $this->_device->useragent); // continue only if there are changes or no time is left if ($this->_heartbeatInterval > 0) { $intervalStart = time(); do { // take a break to save battery lifetime sleep(Syncroton_Registry::getPingTimeout()); $now = new DateTime(null, new DateTimeZone('utc')); foreach ($this->_collections as $collectionData) { // continue immediately if folder does not exist if (!$collectionData->folder instanceof Syncroton_Model_IFolder) { break 2; // countinue immediately if syncstate is invalid } elseif (!$collectionData->syncState instanceof Syncroton_Model_ISyncState) { break 2; } else { if ($collectionData->getChanges !== true) { continue; } try { // just check if the folder still exists $this->_folderBackend->get($collectionData->folder); } catch (Syncroton_Exception_NotFound $senf) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " collection does not exist anymore: " . $collectionData->collectionId); } $collectionData->getChanges = false; // make sure this is the last while loop // no break 2 here, as we like to check the other folders too $intervalStart -= $this->_heartbeatInterval; } // check that the syncstate still exists and is still valid try { $syncState = $this->_syncStateBackend->getSyncState($this->_device, $collectionData->folder); // another process synchronized data of this folder already. let's skip it if ($syncState->id !== $collectionData->syncState->id) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " syncstate changed during heartbeat interval for collection: " . $collectionData->folder->serverId); } $collectionData->getChanges = false; // make sure this is the last while loop // no break 2 here, as we like to check the other folders too $intervalStart -= $this->_heartbeatInterval; } } catch (Syncroton_Exception_NotFound $senf) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " no syncstate found anymore for collection: " . $collectionData->folder->serverId); } $collectionData->syncState = null; // make sure this is the last while loop // no break 2 here, as we like to check the other folders too $intervalStart -= $this->_heartbeatInterval; } // safe battery time by skipping folders which got synchronied less than Syncroton_Command_Ping::$quietTime seconds ago if (!$collectionData->syncState instanceof Syncroton_Model_SyncState || $now->getTimestamp() - $collectionData->syncState->lastsync->getTimestamp() < Syncroton_Registry::getQuietTime()) { continue; } $dataController = Syncroton_Data_Factory::factory($collectionData->folder->class, $this->_device, $this->_syncTimeStamp); // countinue immediately if there are any changes available if ($dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState)) { break 2; } } } // See: http://www.tine20.org/forum/viewtopic.php?f=12&t=12146 // // break if there are less than PingTimeout + 10 seconds left for the next loop // otherwise the response will be returned after the client has finished his Ping // request already maybe } while (time() - $intervalStart < $this->_heartbeatInterval - (Syncroton_Registry::getPingTimeout() + 10)); } foreach ($this->_collections as $collectionData) { $collectionChanges = 0; /** * keep track of entries added on server side */ $newContentStates = array(); /** * keep track of entries deleted on server side */ $deletedContentStates = array(); // invalid collectionid provided if (!$collectionData->folder instanceof Syncroton_Model_IFolder) { $collection = $collections->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collection')); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'SyncKey', 0)); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $collectionData->collectionId)); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_FOLDER_HIERARCHY_HAS_CHANGED)); // invalid synckey provided } elseif (!$collectionData->syncState instanceof Syncroton_Model_ISyncState) { // set synckey to 0 $collection = $collections->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collection')); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'SyncKey', 0)); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $collectionData->collectionId)); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_INVALID_SYNC_KEY)); // initial sync } elseif ($collectionData->syncState->counter === 0) { $collectionData->syncState->counter++; // initial sync // send back a new SyncKey only $collection = $collections->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collection')); if (!empty($collectionData->folder->class)) { $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Class', $collectionData->folder->class)); } $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'SyncKey', $collectionData->syncState->counter)); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $collectionData->collectionId)); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_SUCCESS)); } else { $dataController = Syncroton_Data_Factory::factory($collectionData->folder->class, $this->_device, $this->_syncTimeStamp); $clientModifications = $this->_modifications[$collectionData->collectionId]; $serverModifications = array('added' => array(), 'changed' => array(), 'deleted' => array()); if ($collectionData->getChanges === true) { // continue sync session? if (is_array($collectionData->syncState->pendingdata)) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " restored from sync state "); } $serverModifications = $collectionData->syncState->pendingdata; } elseif ($dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState)) { // update _syncTimeStamp as $dataController->hasChanges might have spent some time $this->_syncTimeStamp = new DateTime(null, new DateTimeZone('utc')); try { // fetch entries added since last sync $allClientEntries = $this->_contentStateBackend->getFolderState($this->_device, $collectionData->folder); $allServerEntries = $dataController->getServerEntries($collectionData->collectionId, $collectionData->options['filterType']); // add entries $serverDiff = array_diff($allServerEntries, $allClientEntries); // add entries which produced problems during delete from client $serverModifications['added'] = $clientModifications['forceAdd']; // add entries not yet sent to client $serverModifications['added'] = array_unique(array_merge($serverModifications['added'], $serverDiff)); // @todo still needed? foreach ($serverModifications['added'] as $id => $serverId) { // skip entries added by client during this sync session if (isset($clientModifications['added'][$serverId]) && !isset($clientModifications['forceAdd'][$serverId])) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped added entry: " . $serverId); } unset($serverModifications['added'][$id]); } } // entries to be deleted $serverModifications['deleted'] = array_diff($allClientEntries, $allServerEntries); // fetch entries changed since last sync $serverModifications['changed'] = $dataController->getChangedEntries($collectionData->collectionId, $collectionData->syncState->lastsync, $this->_syncTimeStamp, $collectionData->options['filterType']); $serverModifications['changed'] = array_merge($serverModifications['changed'], $clientModifications['forceChange']); foreach ($serverModifications['changed'] as $id => $serverId) { // skip entry, if it got changed by client during current sync if (isset($clientModifications['changed'][$serverId]) && !isset($clientModifications['forceChange'][$serverId])) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped changed entry: " . $serverId); } unset($serverModifications['changed'][$id]); } else { if (isset($clientModifications['added'][$serverId]) && !isset($clientModifications['forceAdd'][$serverId])) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped change for added entry: " . $serverId); } unset($serverModifications['changed'][$id]); } } } // entries comeing in scope are already in $serverModifications['added'] and do not need to // be send with $serverCanges $serverModifications['changed'] = array_diff($serverModifications['changed'], $serverModifications['added']); } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->crit(__METHOD__ . '::' . __LINE__ . " Folder state checking failed: " . $e->getMessage()); } if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " Folder state checking failed: " . $e->getTraceAsString()); } // Prevent from removing client entries when getServerEntries() fails // @todo: should we set Status and break the loop here? $serverModifications = array('added' => array(), 'changed' => array(), 'deleted' => array()); } } if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found (added/changed/deleted) " . count($serverModifications['added']) . '/' . count($serverModifications['changed']) . '/' . count($serverModifications['deleted']) . ' entries for sync from server to client'); } } // collection header $collection = $this->_outputDom->createElementNS('uri:AirSync', 'Collection'); if (!empty($collectionData->folder->class)) { $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Class', $collectionData->folder->class)); } $syncKeyElement = $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'SyncKey')); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $collectionData->collectionId)); $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_SUCCESS)); $responses = $this->_outputDom->createElementNS('uri:AirSync', 'Responses'); // send reponse for newly added entries if (!empty($clientModifications['added'])) { foreach ($clientModifications['added'] as $entryData) { $add = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Add')); $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ClientId', $entryData['clientId'])); // we have no serverId is the add failed if (isset($entryData['serverId'])) { $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $entryData['serverId'])); } $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', $entryData['status'])); } } // send reponse for changed entries if (!empty($clientModifications['changed'])) { foreach ($clientModifications['changed'] as $serverId => $status) { if ($status !== Syncroton_Command_Sync::STATUS_SUCCESS) { $change = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Change')); $change->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId)); $change->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', $status)); } } } // send response for to be fetched entries if (!empty($collectionData->toBeFetched)) { // unset all truncation settings as entries are not allowed to be truncated during fetch $fetchCollectionData = clone $collectionData; // unset truncationSize if (isset($fetchCollectionData->options['bodyPreferences']) && is_array($fetchCollectionData->options['bodyPreferences'])) { foreach ($fetchCollectionData->options['bodyPreferences'] as $key => $bodyPreference) { unset($fetchCollectionData->options['bodyPreferences'][$key]['truncationSize']); } } $fetchCollectionData->options['mimeTruncation'] = Syncroton_Command_Sync::TRUNCATE_NOTHING; foreach ($collectionData->toBeFetched as $serverId) { $fetch = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Fetch')); $fetch->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId)); try { $applicationData = $this->_outputDom->createElementNS('uri:AirSync', 'ApplicationData'); $dataController->getEntry($fetchCollectionData, $serverId)->appendXML($applicationData, $this->_device); $fetch->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_SUCCESS)); $fetch->appendChild($applicationData); } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage()); } if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getTraceAsString()); } $fetch->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_OBJECT_NOT_FOUND)); } } } if ($responses->hasChildNodes() === true) { $collection->appendChild($responses); } $commands = $this->_outputDom->createElementNS('uri:AirSync', 'Commands'); foreach ($serverModifications['added'] as $id => $serverId) { if ($collectionChanges == $collectionData->windowSize || $totalChanges + $collectionChanges >= $this->_globalWindowSize) { break; } #/** # * somewhere is a problem in the logic for handling moreAvailable # * # * it can happen, that we have a contentstate (which means we sent the entry to the client # * and that this entry is yet in $collectionData->syncState->pendingdata['serverAdds'] # * I have no idea how this can happen, but the next lines of code work around this problem # */ try { $this->_contentStateBackend->getContentState($this->_device, $collectionData->folder, $serverId); if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped an entry({$serverId}) which is already on the client"); } unset($serverModifications['added'][$id]); continue; } catch (Syncroton_Exception_NotFound $senf) { // do nothing => content state should not exist yet } try { $add = $this->_outputDom->createElementNS('uri:AirSync', 'Add'); $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId)); $applicationData = $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ApplicationData')); $dataController->getEntry($collectionData, $serverId)->appendXML($applicationData, $this->_device); $commands->appendChild($add); $collectionChanges++; } catch (Syncroton_Exception_MemoryExhausted $seme) { // continue to next entry, as there is not enough memory left for the current entry // this will lead to MoreAvailable at the end and the entry will be synced during the next Sync command if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " memory exhausted for entry: " . $serverId); } continue; } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage()); } if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getTraceAsString()); } } // mark as sent to the client, even the conversion to xml might have failed $newContentStates[] = new Syncroton_Model_Content(array('device_id' => $this->_device, 'folder_id' => $collectionData->folder, 'contentid' => $serverId, 'creation_time' => $this->_syncTimeStamp, 'creation_synckey' => $collectionData->syncState->counter + 1)); unset($serverModifications['added'][$id]); } /** * process entries changed on server side */ foreach ($serverModifications['changed'] as $id => $serverId) { if ($collectionChanges == $collectionData->windowSize || $totalChanges + $collectionChanges >= $this->_globalWindowSize) { break; } try { $change = $this->_outputDom->createElementNS('uri:AirSync', 'Change'); $change->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId)); $applicationData = $change->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ApplicationData')); $dataController->getEntry($collectionData, $serverId)->appendXML($applicationData, $this->_device); $commands->appendChild($change); $collectionChanges++; } catch (Syncroton_Exception_MemoryExhausted $seme) { // continue to next entry, as there is not enough memory left for the current entry // this will lead to MoreAvailable at the end and the entry will be synced during the next Sync command if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " memory exhausted for entry: " . $serverId); } continue; } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage()); } } unset($serverModifications['changed'][$id]); } foreach ($serverModifications['deleted'] as $id => $serverId) { if ($collectionChanges == $collectionData->windowSize || $totalChanges + $collectionChanges >= $this->_globalWindowSize) { break; } try { // check if we have sent this entry to the phone $state = $this->_contentStateBackend->getContentState($this->_device, $collectionData->folder, $serverId); $delete = $this->_outputDom->createElementNS('uri:AirSync', 'Delete'); $delete->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId)); $deletedContentStates[] = $state; $commands->appendChild($delete); $collectionChanges++; } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage()); } } unset($serverModifications['deleted'][$id]); } $countOfPendingChanges = count($serverModifications['added']) + count($serverModifications['changed']) + count($serverModifications['deleted']); if ($countOfPendingChanges > 0) { $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'MoreAvailable')); } else { $serverModifications = null; } if ($commands->hasChildNodes() === true) { $collection->appendChild($commands); } $totalChanges += $collectionChanges; // increase SyncKey if needed if (!empty($clientModifications['added']) || !empty($clientModifications['changed']) || !empty($clientModifications['deleted']) || $commands->hasChildNodes() === true || $collectionData->syncState->pendingdata != $serverModifications) { // ...then increase SyncKey $collectionData->syncState->counter++; } $syncKeyElement->appendChild($this->_outputDom->createTextNode($collectionData->syncState->counter)); if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " current synckey is " . $collectionData->syncState->counter); } if (!$emptySyncSupported || $collection->childNodes->length > 4 || $collectionData->syncState->counter != $collectionData->syncKey) { $collections->appendChild($collection); } } if (isset($collectionData->syncState) && $collectionData->syncState instanceof Syncroton_Model_ISyncState && $collectionData->syncState->counter != $collectionData->syncKey) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " update syncState for collection: " . $collectionData->collectionId); } // store pending data in sync state when needed if (isset($countOfPendingChanges) && $countOfPendingChanges > 0) { $collectionData->syncState->pendingdata = array('added' => (array) $serverModifications['added'], 'changed' => (array) $serverModifications['changed'], 'deleted' => (array) $serverModifications['deleted']); } else { $collectionData->syncState->pendingdata = null; } if (!empty($clientModifications['added'])) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " remove previous synckey as client added new entries"); } $keepPreviousSyncKey = false; } else { $keepPreviousSyncKey = true; } $collectionData->syncState->lastsync = clone $this->_syncTimeStamp; // increment sync timestamp by 1 second $collectionData->syncState->lastsync->modify('+1 sec'); try { $transactionId = Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase()); // store new synckey $this->_syncStateBackend->create($collectionData->syncState, $keepPreviousSyncKey); // store contentstates for new entries added to client foreach ($newContentStates as $state) { $this->_contentStateBackend->create($state); } // remove contentstates for entries to be deleted on client foreach ($deletedContentStates as $state) { $this->_contentStateBackend->delete($state); } Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId); //} catch (Zend_Db_Statement_Exception $zdse) { } catch (Exception $zdse) { // something went wrong // maybe another parallel request added a new synckey // we must remove data added from client if (!empty($clientModifications['added'])) { foreach ($clientModifications['added'] as $added) { $this->_contentStateBackend->delete($added['contentState']); $dataController->deleteEntry($collectionData->collectionId, $added['serverId'], array()); } } Syncroton_Registry::getTransactionManager()->rollBack(); throw $zdse; } } // store current filter type try { $folderState = $this->_folderBackend->get($collectionData->folder); $folderState->lastfiltertype = $collectionData->options['filterType']; if ($folderState->isDirty()) { $this->_folderBackend->update($folderState); } } catch (Syncroton_Exception_NotFound $senf) { // failed to get folderstate => should not happen but is also no problem in this state if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . ' failed to get folder state for: ' . $collectionData->collectionId); } } } if ($collections->hasChildNodes() === true) { $sync->appendChild($collections); } if ($sync->hasChildNodes()) { return $this->_outputDom; } return null; }
/** * init registry */ protected function _initializeRegistry() { ActiveSync_Controller::initSyncrotonRegistry(); $applications = is_object(Tinebase_Core::getUser()) ? Tinebase_Core::getUser()->getApplications() : new Tinebase_Record_RecordSet('Tinebase_Model_Application'); if ($applications->find('name', 'Addressbook')) { Syncroton_Registry::setContactsDataClass('Addressbook_Frontend_ActiveSync'); Syncroton_Registry::setGALDataClass('Addressbook_Frontend_ActiveSync'); } if ($applications->find('name', 'Calendar')) { Syncroton_Registry::setCalendarDataClass('Calendar_Frontend_ActiveSync'); } if ($applications->find('name', 'Felamimail')) { Syncroton_Registry::setEmailDataClass('Felamimail_Frontend_ActiveSync'); } if ($applications->find('name', 'Tasks')) { Syncroton_Registry::setTasksDataClass('Tasks_Frontend_ActiveSync'); } Syncroton_Registry::set(Syncroton_Registry::DEFAULT_POLICY, ActiveSync_Config::getInstance()->get(ActiveSync_Config::DEFAULT_POLICY)); }
/** * testGetCountOfChanges for fake folder (outbox) */ public function testGetCountOfChangesFakeFolder() { $controller = $this->_getController($this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE)); $numberOfChanges = $controller->getCountOfChanges(Syncroton_Registry::getContentStateBackend(), new Syncroton_Model_Folder(array('id' => Tinebase_Record_Abstract::generateUID(), 'serverId' => 'fake-' . Syncroton_Command_FolderSync::FOLDERTYPE_OUTBOX, 'lastfiltertype' => Syncroton_Command_Sync::FILTER_NOTHING)), new Syncroton_Model_SyncState(array('lastsync' => Tinebase_DateTime::now()->subHour(1)))); $this->assertEquals(0, $numberOfChanges); }
/** * test xml generation for IPhone */ public function testRemoteWipeStep2() { $this->testRemoteWipeStep1(); $doc = new DOMDocument(); $doc->loadXML('<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> <Provision xmlns="uri:Provision"><RemoteWipe><Status>1</Status></RemoteWipe></Provision>'); $provision = new Syncroton_Command_Provision($doc, $this->_device, $this->_device->policykey); $provision->handle(); $responseDoc = $provision->getResponse(); #$responseDoc->formatOutput = true; echo $responseDoc->saveXML(); $this->_device = Syncroton_Registry::getDeviceBackend()->get($this->_device); $this->assertEquals(Syncroton_Command_Provision::REMOTEWIPE_CONFIRMED, $this->_device->remotewipe); $xpath = new DomXPath($responseDoc); $xpath->registerNamespace('Provision', 'uri:Provision'); $nodes = $xpath->query('//Provision:Provision/Provision:Status'); $this->assertEquals(1, $nodes->length, $responseDoc->saveXML()); $this->assertEquals(Syncroton_Command_FolderSync::STATUS_SUCCESS, $nodes->item(0)->nodeValue, $responseDoc->saveXML()); $nodes = $xpath->query('//Provision:Provision/Provision:RemoteWipe'); $this->assertEquals(1, $nodes->length, $responseDoc->saveXML()); }
public function testDeleteEntry() { try { $device = Syncroton_Registry::getDeviceBackend()->getUserDevice('1234', 'iphone-abcd'); Syncroton_Registry::getDeviceBackend()->delete($device); } catch (Syncroton_Exception_NotFound $e) { // do nothing => it's ok } require_once dirname(dirname(__FILE__)) . DS . 'Backend' . DS . 'DeviceTest.php'; $device = Syncroton_Registry::getDeviceBackend()->create(DeviceTest::getTestDevice(Syncroton_Model_Device::TYPE_IPHONE)); $dataController = Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_CONTACTS, $device, new DateTime(null, new DateTimeZone('UTC'))); $entries = $dataController->getServerEntries('addressbookFolderId', null); $dataController->deleteEntry('addressbookFolderId', $entries[0], array()); $newEntries = $dataController->getServerEntries('addressbookFolderId', null); $this->assertArrayNotHasKey($entries[0], $newEntries); }
* * Example server file * * @package doc * @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3 * @copyright Copyright (c) 2012-2012 Metaways Infosystems GmbH (http://www.metaways.de) * @author Lars Kneschke <*****@*****.**> */ if (!isset($_SERVER['PHP_AUTH_USER'])) { header('WWW-Authenticate: Basic realm="Syncroton"'); header('HTTP/1.0 401 Unauthorized'); echo 'Please authenticate!'; exit; } // http://localhost/Microsoft-Server-ActiveSync $paths = array(realpath(dirname(__FILE__) . '/../forbidden/zend1/library/'), realpath(dirname(__FILE__)), realpath(dirname(__FILE__) . '/lib'), get_include_path()); set_include_path(implode(PATH_SEPARATOR, $paths)); require_once 'Zend\\Loader\\Autoloader.php'; $autoloader = Zend_Loader_Autoloader::getInstance(); $autoloader->setFallbackAutoloader(true); $db = Zend_Db::factory('PDO_MYSQL', array('host' => '127.0.0.1', 'username' => 'root', 'password' => '', 'dbname' => 'syncro')); Syncroton_Registry::setDatabase($db); $writer = new Zend_Log_Writer_Stream(dirname(__FILE__) . '\\syncroton.log'); $writer->addFilter(new Zend_Log_Filter_Priority(Zend_Log::DEBUG)); Syncroton_Registry::set('loggerBackend', new Zend_Log($writer)); Syncroton_Registry::setContactsDataClass('Syncroton_Data_Contacts'); Syncroton_Registry::setCalendarDataClass('Syncroton_Data_Calendar'); Syncroton_Registry::setTasksDataClass('Syncroton_Data_Tasks'); #Syncroton_Registry::setEmailDataClass('Syncroton_Data_Email'); $server = new Syncroton_Server($_SERVER['PHP_AUTH_USER']); $server->handle();
if (!defined('WEBROOT_DIR')) { define('WEBROOT_DIR', basename(dirname(__FILE__))); } if (!defined('WWW_ROOT')) { define('WWW_ROOT', dirname(__FILE__) . DS); } if (!defined('CAKE_CORE_INCLUDE_PATH')) { if (function_exists('ini_set')) { ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path')); } if (!(include 'Cake' . DS . 'bootstrap.php')) { $failed = true; } } else { if (!(include CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php')) { $failed = true; } } if (!empty($failed)) { trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); } App::uses('ConnectionManager', 'Model'); Syncroton_Registry::setDatabase(ConnectionManager::getDataSource('default')); Syncroton_Registry::set('loggerBackend', new Syncroton_Log()); Syncroton_Registry::setContactsDataClass('Syncroton_Data_Contacts'); Syncroton_Registry::setCalendarDataClass('Syncroton_Data_Calendar'); Syncroton_Registry::setTasksDataClass('Syncroton_Data_Tasks'); Syncroton_Registry::setEmailDataClass('Syncroton_Data_Email'); Syncroton_Registry::setNotesDataClass('Syncroton_Data_Notes'); $server = new Syncroton_Server($_SERVER['PHP_AUTH_USER']); $server->handle();
/** * Get Expressomail message id from source structure * * @param array|string $source * @return string */ public function getMessageIdFromSource($source) { $serverId = is_array($source) ? $source['itemId'] : $source; $folderId = is_array($source) ? $source['collectionId'] : NULL; // is $serverId a LongId? if (strpos($serverId, ActiveSync_Frontend_Abstract::LONGID_DELIMITER) !== false) { list($folderId, $serverId) = explode(ActiveSync_Frontend_Abstract::LONGID_DELIMITER, $serverId, 2); } $folderBackend = Syncroton_Registry::get(Syncroton_Registry::FOLDERBACKEND); $folder = $folderBackend->getFolder($this->_device, $folderId); return $this->getBigContentId($folder->id, $serverId); }
/** * addnote method * * @return void */ public function addnote() { App::uses('ConnectionManager', 'Model'); Syncroton_Registry::setDatabase(ConnectionManager::getDataSource('default')); Syncroton_Registry::setNotesDataClass('Syncroton_Data_Notes'); Syncroton_Registry::set('loggerBackend', new Syncroton_Log('lil_active_sync2')); $device = Syncroton_Registry::getDeviceBackend()->getUserDevice('miha', 'ApplC33JKLWDDTWD'); Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_NOTES, $device, new DateTime(null, new DateTimeZone('utc')))->createEntry('', new Syncroton_Model_Note(array('subject' => 'Second note'))); return new CakeResponse(array('body' => 'success')); }
/** * testResetSync * * @see 0010584: CLI function for resetting sync on devices */ public function testResetSync() { $class = 'Calendar'; $this->testSyncOfEvents(); $result = ActiveSync_Controller::getInstance()->resetSyncForUser(Tinebase_Core::getUser()->accountLoginName, array($class)); $this->assertTrue($result); // check if synckey = 0 $folderState = Syncroton_Registry::getFolderBackend()->getFolderState($this->_device->id, $class); $this->assertTrue(count($folderState) > 0); foreach ($folderState as $folder) { try { Syncroton_Registry::getSyncStateBackend()->getSyncState($this->_device->id, $folder->id); $this->fail('should not find sync state for folder'); } catch (Exception $e) { $this->assertEquals('id not found', $e->getMessage()); } } }
/** * process the XML file and add, change, delete or fetches data * * @todo can we get rid of LIBXML_NOWARNING * @todo we need to stored the initial data for folders and lifetime as the phone is sending them only when they change * @return resource */ public function handle() { $intervalStart = time(); $status = self::STATUS_NO_CHANGES_FOUND; // the client does not send a wbxml document, if the Ping parameters did not change compared with the last request if ($this->_requestBody instanceof DOMDocument) { $xml = simplexml_import_dom($this->_requestBody); $xml->registerXPathNamespace('Ping', 'Ping'); if (isset($xml->HeartbeatInterval)) { $this->_device->pinglifetime = (int) $xml->HeartbeatInterval; } if (isset($xml->Folders->Folder)) { $folders = array(); foreach ($xml->Folders->Folder as $folderXml) { try { // does the folder exist? $folder = $this->_folderBackend->getFolder($this->_device, (string) $folderXml->Id); $folders[$folder->id] = $folder; } catch (Syncroton_Exception_NotFound $senf) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $senf->getMessage()); } $status = self::STATUS_FOLDER_NOT_FOUND; break; } } $this->_device->pingfolder = serialize(array_keys($folders)); } } $this->_device->lastping = new DateTime('now', new DateTimeZone('utc')); if ($status == self::STATUS_NO_CHANGES_FOUND) { $this->_device = $this->_deviceBackend->update($this->_device); } $lifeTime = $this->_device->pinglifetime; $maxLifeTime = Syncroton_Registry::getMaxPingInterval(); if ($maxLifeTime > 0 && $lifeTime > $maxLifeTime) { $ping = $this->_outputDom->documentElement; $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Status', self::STATUS_INTERVAL_TO_GREAT_OR_SMALL)); $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'HeartbeatInterval', $maxLifeTime)); return; } $intervalEnd = $intervalStart + $lifeTime; $secondsLeft = $intervalEnd; $folders = unserialize($this->_device->pingfolder); if ($status === self::STATUS_NO_CHANGES_FOUND && (!is_array($folders) || count($folders) == 0)) { $status = self::STATUS_MISSING_PARAMETERS; } if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " Folders to monitor({$lifeTime} / {$intervalStart} / {$intervalEnd} / {$status}): " . print_r($folders, true)); } if ($status === self::STATUS_NO_CHANGES_FOUND) { do { // take a break to save battery lifetime sleep(Syncroton_Registry::getPingTimeout()); try { $device = $this->_deviceBackend->get($this->_device->id); } catch (Syncroton_Exception_NotFound $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); } $status = self::STATUS_FOLDER_NOT_FOUND; break; } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->err(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); } // do nothing, maybe temporal issue, should we stop? continue; } // if another Ping command updated lastping property, we can stop processing this Ping command request if (isset($device->lastping) && $device->lastping instanceof DateTime && $device->pingfolder === $this->_device->pingfolder && $device->lastping->getTimestamp() > $this->_device->lastping->getTimestamp()) { break; } $now = new DateTime('now', new DateTimeZone('utc')); foreach ($folders as $folderId) { try { $folder = $this->_folderBackend->get($folderId); $dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp); } catch (Syncroton_Exception_NotFound $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); } $status = self::STATUS_FOLDER_NOT_FOUND; break; } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->error(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); } // do nothing, maybe temporal issue, should we stop? continue; } try { $syncState = $this->_syncStateBackend->getSyncState($this->_device, $folder); // another process synchronized data of this folder already. let's skip it if ($syncState->lastsync > $this->_syncTimeStamp) { continue; } // safe battery time by skipping folders which got synchronied less than Syncroton_Registry::getQuietTime() seconds ago if ($now->getTimestamp() - $syncState->lastsync->getTimestamp() < Syncroton_Registry::getQuietTime()) { continue; } $foundChanges = $dataController->hasChanges($this->_contentStateBackend, $folder, $syncState); } catch (Syncroton_Exception_NotFound $e) { // folder got never synchronized to client if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage()); } if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . ' syncstate not found. enforce sync for folder: ' . $folder->serverId); } $foundChanges = true; } if ($foundChanges == true) { $this->_foldersWithChanges[] = $folder; $status = self::STATUS_CHANGES_FOUND; } } if ($status != self::STATUS_NO_CHANGES_FOUND) { break; } $secondsLeft = $intervalEnd - time(); if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " seconds left: " . $secondsLeft); } // See: http://www.tine20.org/forum/viewtopic.php?f=12&t=12146 // // break if there are less than PingTimeout + 10 seconds left for the next loop // otherwise the response will be returned after the client has finished his Ping // request already maybe } while ($secondsLeft > Syncroton_Registry::getPingTimeout() + 10); } if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " Lifetime: {$lifeTime} SecondsLeft: {$secondsLeft} Status: {$status})"); } $ping = $this->_outputDom->documentElement; $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Status', $status)); if ($status === self::STATUS_CHANGES_FOUND) { $folders = $ping->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Folders')); foreach ($this->_foldersWithChanges as $changedFolder) { $folder = $folders->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Folder', $changedFolder->serverId)); if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " changes in folder: " . $changedFolder->serverId); } } } }
/** * test that the last filterType got updated, even no changed entries were found */ public function testUpdateOfLastFilterType() { $this->testSyncOfContacts(); $dataController = Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_CONTACTS, $this->_device, new DateTime(null, new DateTimeZone('UTC'))); $entries = $dataController->getServerEntries('addressbookFolderId', null); // lets add one contact $doc = new DOMDocument(); $doc->loadXML('<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections> <Collection> <Class>Contacts</Class> <SyncKey>4</SyncKey> <CollectionId>addressbookFolderId</CollectionId> <DeletesAsMoves/> <GetChanges/> <WindowSize>100</WindowSize> <Options> <FilterType>2</FilterType> <AirSyncBase:BodyPreference> <AirSyncBase:Type>1</AirSyncBase:Type> <AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize> </AirSyncBase:BodyPreference> <Conflict>1</Conflict> </Options> </Collection> </Collections></Sync>'); $sync = new Syncroton_Command_Sync($doc, $this->_device, $this->_device->policykey); $sync->handle(); $syncDoc = $sync->getResponse(); $folder = Syncroton_Registry::getFolderBackend()->getFolder($this->_device, 'addressbookFolderId'); $this->assertEquals(2, $folder->lastfiltertype); }
/** * */ public function testPingContacts() { // first do a foldersync $doc = new DOMDocument(); $doc->loadXML('<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> <FolderSync xmlns="uri:FolderHierarchy"><SyncKey>0</SyncKey></FolderSync>'); $folderSync = new Syncroton_Command_FolderSync($doc, $this->_device, $this->_device->policykey); $folderSync->handle(); $folderSync->getResponse(); // request initial synckey $doc = new DOMDocument(); $doc->loadXML('<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections><Collection><Class>Contacts</Class><SyncKey>0</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>100</WindowSize><Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options></Collection></Collections></Sync>'); $sync = new Syncroton_Command_Sync($doc, $this->_device, $this->_device->policykey); $sync->handle(); $syncDoc = $sync->getResponse(); #$syncDoc->formatOutput = true; echo $syncDoc->saveXML(); // now do the first sync $doc = new DOMDocument(); $doc->loadXML('<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections><Collection><Class>Contacts</Class><SyncKey>1</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>100</WindowSize><Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options></Collection></Collections></Sync>'); $sync = new Syncroton_Command_Sync($doc, $this->_device, $this->_device->policykey); $sync->handle(); $syncDoc = $sync->getResponse(); #$syncDoc->formatOutput = true; echo $syncDoc->saveXML(); $folder = Syncroton_Registry::getFolderBackend()->getFolder($this->_device, 'addressbookFolderId'); $oneSecondAgo = new DateTime(null, new DateTimeZone('utc')); $oneSecondAgo->modify('-1 second'); $tenSecondsAgo = new DateTime(null, new DateTimeZone('utc')); $tenSecondsAgo->modify('-10 second'); // update modify timeStamp of contact $dataController = Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_CONTACTS, $this->_device, $oneSecondAgo); $contact = $dataController->getEntry(new Syncroton_Model_SyncCollection(array('folder' => 'addressbookFolderId')), 'contact1'); $dataController->updateEntry('addressbookFolderId', 'contact1', $contact); // turn back last sync time $syncState = Syncroton_Registry::getSyncStateBackend()->getSyncState($this->_device, $folder); $syncState->lastsync = $tenSecondsAgo; $syncState = Syncroton_Registry::getSyncStateBackend()->update($syncState); // and now we can start the ping request $doc = new DOMDocument(); $doc->loadXML('<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/"> <Ping xmlns="uri:Ping"><HeartBeatInterval>10</HeartBeatInterval><Folders><Folder><Id>addressbookFolderId</Id><Class>Contacts</Class></Folder></Folders></Ping>'); $search = new Syncroton_Command_Ping($doc, $this->_device, null); $search->handle(); $responseDoc = $search->getResponse(); #$responseDoc->formatOutput = true; echo $responseDoc->saveXML(); $xpath = new DomXPath($responseDoc); $xpath->registerNamespace('Ping', 'uri:Ping'); $nodes = $xpath->query('//Ping:Ping/Ping:Status'); $this->assertEquals(1, $nodes->length, $responseDoc->saveXML()); $this->assertEquals(Syncroton_Command_Ping::STATUS_CHANGES_FOUND, $nodes->item(0)->nodeValue, $responseDoc->saveXML()); $nodes = $xpath->query('//Ping:Ping/Ping:Folders/Ping:Folder'); $this->assertEquals(1, $nodes->length, $responseDoc->saveXML()); $this->assertEquals('addressbookFolderId', $nodes->item(0)->nodeValue, $responseDoc->saveXML()); }
public static function initSyncrotonRegistry() { Syncroton_Registry::setDatabase(Tinebase_Core::getDb()); Syncroton_Registry::setTransactionManager(Tinebase_TransactionManager::getInstance()); Syncroton_Registry::set(Syncroton_Registry::DEVICEBACKEND, new Syncroton_Backend_Device(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set(Syncroton_Registry::FOLDERBACKEND, new Syncroton_Backend_Folder(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set(Syncroton_Registry::SYNCSTATEBACKEND, new Syncroton_Backend_SyncState(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set(Syncroton_Registry::CONTENTSTATEBACKEND, new Syncroton_Backend_Content(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set(Syncroton_Registry::POLICYBACKEND, new Syncroton_Backend_Policy(Tinebase_Core::getDb(), SQL_TABLE_PREFIX . 'acsync_')); Syncroton_Registry::set(Syncroton_Registry::LOGGERBACKEND, Tinebase_Core::getLogger()); Syncroton_Registry::set(Syncroton_Registry::SESSION_VALIDATOR, function () { return !Tinebase_Core::inMaintenanceMode(); }); }
/** * get existing device of owner or create new device for owner * * @param unknown_type $ownerId * @param unknown_type $deviceId * @param unknown_type $deviceType * @param unknown_type $userAgent * @param unknown_type $protocolVersion * @return Syncroton_Model_Device */ protected function _getUserDevice($ownerId, $requestParameters) { try { $device = $this->_deviceBackend->getUserDevice($ownerId, $requestParameters['deviceId']); $device->useragent = $requestParameters['userAgent']; $device->acsversion = $requestParameters['protocolVersion']; if ($device->isDirty()) { $device = $this->_deviceBackend->update($device); } } catch (Syncroton_Exception_NotFound $senf) { $device = $this->_deviceBackend->create(new Syncroton_Model_Device(array('owner_id' => $ownerId, 'deviceid' => $requestParameters['deviceId'], 'devicetype' => $requestParameters['deviceType'], 'useragent' => $requestParameters['userAgent'], 'acsversion' => $requestParameters['protocolVersion'], 'policyId' => Syncroton_Registry::isRegistered(Syncroton_Registry::DEFAULT_POLICY) ? Syncroton_Registry::get(Syncroton_Registry::DEFAULT_POLICY) : null))); } return $device; }
/** * this test should throw no error if the foldersync got restarted after an invalid synckey */ public function testFolderSyncAfterInvalidSyncKey() { $this->testGetFoldersSyncKey0(); $clientFolders1 = Syncroton_Registry::getFolderBackend()->getFolderState($this->_device, 'Contacts'); $testFolderIds = array_keys($clientFolders1); $this->testGetFoldersInvalidSyncKey(); $this->testGetFoldersSyncKey0(); $clientFolders2 = Syncroton_Registry::getFolderBackend()->getFolderState($this->_device, 'Contacts'); $this->assertEquals($clientFolders1[$testFolderIds[0]], $clientFolders2[$testFolderIds[0]]); }