/** * tear down tests */ protected function tearDown() { parent::tearDown(); if (!$this->_transactionId) { Syncroton_Registry::getDeviceBackend()->delete($this->_device->id); } }
public function __construct($userId, CakeRequest $request = null, $body = null) { if (Syncroton_Registry::isRegistered('loggerBackend')) { $this->_logger = Syncroton_Registry::get('loggerBackend'); } $this->_userId = $userId; $this->_request = $request instanceof CakeRequest ? $request : new CakeRequest(); $this->_body = $body !== null ? $body : fopen('php://input', 'r'); $this->_deviceBackend = Syncroton_Registry::getDeviceBackend(); }
/** * 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()); }
/** * (non-PHPdoc) * @see Syncroton/Syncroton_TestCase::setUp() */ public function setUp() { Syncroton_Registry::setDatabase(ConnectionManager::getDataSource('test')); Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase()); Syncroton_Registry::set('loggerBackend', new Syncroton_Log('lil_active_sync')); 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'; $this->_device = Syncroton_Registry::getDeviceBackend()->create(DeviceTest::getTestDevice()); Syncroton_Registry::setContactsDataClass('Syncroton_Data_Contacts'); Syncroton_Registry::setCalendarDataClass('Syncroton_Data_Calendar'); Syncroton_Registry::setEmailDataClass('Syncroton_Data_Email'); Syncroton_Registry::setTasksDataClass('Syncroton_Data_Tasks'); Syncroton_Registry::setGALDataClass('Syncroton_Data_Contacts'); // speed up tests Syncroton_Registry::set(Syncroton_Registry::PING_TIMEOUT, 1); Syncroton_Registry::set(Syncroton_Registry::QUIET_TIME, 1); }
/** * process the XML file and add, change, delete or fetches data */ public function handle() { // input xml $requestXML = simplexml_import_dom($this->_mergeSyncRequest($this->_requestBody, $this->_device)); if (!isset($requestXML->Collections)) { $this->_outputDom->documentElement->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_RESEND_FULL_XML)); return $this->_outputDom; } if (isset($requestXML->HeartbeatInterval)) { $this->_heartbeatInterval = (int) $requestXML->HeartbeatInterval; } elseif (isset($requestXML->Wait)) { $this->_heartbeatInterval = (int) $requestXML->Wait * 60; } $this->_globalWindowSize = isset($requestXML->WindowSize) ? (int) $requestXML->WindowSize : 100; if (!$this->_globalWindowSize || $this->_globalWindowSize > 512) { $this->_globalWindowSize = 512; } if ($this->_globalWindowSize > $this->_maxWindowSize) { $this->_globalWindowSize = $this->_maxWindowSize; } // load options from lastsynccollection $lastSyncCollection = array('options' => array()); if (!empty($this->_device->lastsynccollection)) { $lastSyncCollection = json_decode($this->_device->lastsynccollection, true); if (!array_key_exists('options', $lastSyncCollection) || !is_array($lastSyncCollection['options'])) { $lastSyncCollection['options'] = array(); } } $collections = array(); foreach ($requestXML->Collections->Collection as $xmlCollection) { $collectionId = (string) $xmlCollection->CollectionId; $collections[$collectionId] = new Syncroton_Model_SyncCollection($xmlCollection); // do we have to reuse the options from the previous request? if (!isset($xmlCollection->Options) && array_key_exists($collectionId, $lastSyncCollection['options'])) { $collections[$collectionId]->options = $lastSyncCollection['options'][$collectionId]; if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " restored options to " . print_r($collections[$collectionId]->options, TRUE)); } } // store current options for next Sync command request (sticky options) $lastSyncCollection['options'][$collectionId] = $collections[$collectionId]->options; } $this->_device->lastsynccollection = json_encode($lastSyncCollection); if ($this->_device->isDirty()) { Syncroton_Registry::getDeviceBackend()->update($this->_device); } foreach ($collections as $collectionData) { // has the folder been synchronised to the device already try { $collectionData->folder = $this->_folderBackend->getFolder($this->_device, $collectionData->collectionId); } catch (Syncroton_Exception_NotFound $senf) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " folder {$collectionData->collectionId} not found"); } // trigger INVALID_SYNCKEY instead of OBJECT_NOTFOUND when synckey is higher than 0 // to avoid a syncloop for the iPhone if ($collectionData->syncKey > 0) { $collectionData->folder = new Syncroton_Model_Folder(array('deviceId' => $this->_device, 'serverId' => $collectionData->collectionId)); } $this->_collections[$collectionData->collectionId] = $collectionData; continue; } if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " SyncKey is {$collectionData->syncKey} Class: {$collectionData->folder->class} CollectionId: {$collectionData->collectionId}"); } // initial synckey if ($collectionData->syncKey === 0) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " initial client synckey 0 provided"); } // reset sync state for this folder $this->_syncStateBackend->resetState($this->_device, $collectionData->folder); $this->_contentStateBackend->resetState($this->_device, $collectionData->folder); $collectionData->syncState = new Syncroton_Model_SyncState(array('device_id' => $this->_device, 'counter' => 0, 'type' => $collectionData->folder, 'lastsync' => $this->_syncTimeStamp)); $this->_collections[$collectionData->collectionId] = $collectionData; continue; } // check for invalid sycnkey if (($collectionData->syncState = $this->_syncStateBackend->validate($this->_device, $collectionData->folder, $collectionData->syncKey)) === false) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " invalid synckey {$collectionData->syncKey} provided"); } // reset sync state for this folder $this->_syncStateBackend->resetState($this->_device, $collectionData->folder); $this->_contentStateBackend->resetState($this->_device, $collectionData->folder); $this->_collections[$collectionData->collectionId] = $collectionData; continue; } $dataController = Syncroton_Data_Factory::factory($collectionData->folder->class, $this->_device, $this->_syncTimeStamp); switch ($collectionData->folder->class) { case Syncroton_Data_Factory::CLASS_CALENDAR: $dataClass = 'Syncroton_Model_Event'; break; case Syncroton_Data_Factory::CLASS_CONTACTS: $dataClass = 'Syncroton_Model_Contact'; break; case Syncroton_Data_Factory::CLASS_EMAIL: $dataClass = 'Syncroton_Model_Email'; break; case Syncroton_Data_Factory::CLASS_NOTES: $dataClass = 'Syncroton_Model_Note'; break; case Syncroton_Data_Factory::CLASS_TASKS: $dataClass = 'Syncroton_Model_Task'; break; default: throw new Syncroton_Exception_UnexpectedValue('invalid class provided'); break; } $clientModifications = array('added' => array(), 'changed' => array(), 'deleted' => array(), 'forceAdd' => array(), 'forceChange' => array(), 'toBeFetched' => array()); // handle incoming data if ($collectionData->hasClientAdds()) { $adds = $collectionData->getClientAdds(); if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($adds) . " entries to be added to server"); } foreach ($adds as $add) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " add entry with clientId " . (string) $add->ClientId); } try { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " adding entry as new"); } $serverId = $dataController->createEntry($collectionData->collectionId, new $dataClass($add->ApplicationData)); $clientModifications['added'][$serverId] = array('clientId' => (string) $add->ClientId, 'serverId' => $serverId, 'status' => self::STATUS_SUCCESS, 'contentState' => $this->_contentStateBackend->create(new Syncroton_Model_Content(array('device_id' => $this->_device, 'folder_id' => $collectionData->folder, 'contentid' => $serverId, 'creation_time' => $this->_syncTimeStamp, 'creation_synckey' => $collectionData->syncKey + 1)))); } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " failed to add entry " . $e->getMessage()); } $clientModifications['added'][] = array('clientId' => (string) $add->ClientId, 'status' => self::STATUS_SERVER_ERROR); } } } // handle changes, but only if not first sync if ($collectionData->syncKey > 1 && $collectionData->hasClientChanges()) { $changes = $collectionData->getClientChanges(); if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($changes) . " entries to be updated on server"); } foreach ($changes as $change) { $serverId = (string) $change->ServerId; try { $dataController->updateEntry($collectionData->collectionId, $serverId, new $dataClass($change->ApplicationData)); $clientModifications['changed'][$serverId] = self::STATUS_SUCCESS; } catch (Syncroton_Exception_AccessDenied $e) { $clientModifications['changed'][$serverId] = self::STATUS_CONFLICT_MATCHING_THE_CLIENT_AND_SERVER_OBJECT; $clientModifications['forceChange'][$serverId] = $serverId; } catch (Syncroton_Exception_NotFound $e) { // entry does not exist anymore, will get deleted automaticaly $clientModifications['changed'][$serverId] = self::STATUS_OBJECT_NOT_FOUND; } catch (Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->warning(__METHOD__ . '::' . __LINE__ . " failed to update entry " . $e); } // something went wrong while trying to update the entry $clientModifications['changed'][$serverId] = self::STATUS_SERVER_ERROR; } } } // handle deletes, but only if not first sync if ($collectionData->hasClientDeletes()) { $deletes = $collectionData->getClientDeletes(); if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($deletes) . " entries to be deleted on server"); } foreach ($deletes as $delete) { $serverId = (string) $delete->ServerId; try { // check if we have sent this entry to the phone $state = $this->_contentStateBackend->getContentState($this->_device, $collectionData->folder, $serverId); try { $dataController->deleteEntry($collectionData->collectionId, $serverId, $collectionData); } catch (Syncroton_Exception_NotFound $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->crit(__METHOD__ . '::' . __LINE__ . ' tried to delete entry ' . $serverId . ' but entry was not found'); } } catch (Syncroton_Exception $e) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . ' tried to delete entry ' . $serverId . ' but a error occured: ' . $e->getMessage()); } $clientModifications['forceAdd'][$serverId] = $serverId; } $this->_contentStateBackend->delete($state); } catch (Syncroton_Exception_NotFound $senf) { if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . ' ' . $serverId . ' should have been removed from client already'); } // should we send a special status??? //$collectionData->deleted[$serverId] = self::STATUS_SUCCESS; } $clientModifications['deleted'][$serverId] = self::STATUS_SUCCESS; } } // handle fetches, but only if not first sync if ($collectionData->syncKey > 1 && $collectionData->hasClientFetches()) { // the default value for GetChanges is 1. If the phone don't want the changes it must set GetChanges to 0 // some prevoius versions of iOS did not set GetChanges to 0 for fetches. Let's enforce getChanges to false here. $collectionData->getChanges = false; $fetches = $collectionData->getClientFetches(); if ($this->_logger instanceof Syncroton_Log) { $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($fetches) . " entries to be fetched from server"); } $toBeFecthed = array(); foreach ($fetches as $fetch) { $serverId = (string) $fetch->ServerId; $toBeFetched[$serverId] = $serverId; } $collectionData->toBeFetched = $toBeFetched; } $this->_collections[$collectionData->collectionId] = $collectionData; $this->_modifications[$collectionData->collectionId] = $clientModifications; } }
/** * 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]; }
/** * 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')); }
/** * 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); }
/** * 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; } } } }