예제 #1
0
 /**
  * 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');
 }
예제 #2
0
 /**
  * @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;
 }
예제 #3
0
 /**
  * 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);
 }
예제 #4
0
 /**
  * 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';
 }
예제 #5
0
 /**
  * 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');
 }
예제 #6
0
    /**
     * 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];
 }
예제 #10
0
 /**
  * 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;
             }
         }
     }
 }
예제 #11
0
 /**
  * (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);
 }
예제 #14
0
    /**
     * 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());
    }
예제 #15
0
 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);
 }
예제 #16
0
 *
 * 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();
예제 #17
0
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());
         }
     }
 }
예제 #21
0
 /**
  * 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);
             }
         }
     }
 }
예제 #22
0
    /**
     * 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);
    }
예제 #23
0
    /**
     *
     */
    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();
     });
 }
예제 #25
0
 /**
  * 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;
 }
예제 #26
0
 /**
  * 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]]);
 }