made Ping command working
authorLars Kneschke <l.kneschke@metaways.de>
Mon, 23 Jan 2012 02:56:23 +0000 (03:56 +0100)
committerLars Kneschke <l.kneschke@metaways.de>
Mon, 23 Jan 2012 02:56:23 +0000 (03:56 +0100)
12 files changed:
docs/syncope.sql
lib/Syncope/Backend/Device.php
lib/Syncope/Backend/Folder.php
lib/Syncope/Backend/IFolder.php
lib/Syncope/Backend/SyncState.php
lib/Syncope/Command/Ping.php
lib/Syncope/Command/Sync.php
lib/Syncope/Model/IDevice.php
tests/Syncope/Command/ATestCase.php
tests/Syncope/Command/AllTests.php
tests/Syncope/Command/PingTests.php [new file with mode: 0644]
tests/bootstrap.php

index 51879d0..f2a9c09 100644 (file)
@@ -7,6 +7,7 @@ CREATE TABLE IF NOT EXISTS `syncope_devices` (
     `acsversion` varchar(40) NOT NULL,
     `pinglifetime` int(11) DEFAULT NULL,
     `remotewipe` int(11) DEFAULT '0',
+    `pingfolder` longblob,
     PRIMARY KEY (`id`)
 );
 
index b45948e..1e20375 100644 (file)
@@ -88,9 +88,11 @@ class Syncope_Backend_Device implements Syncope_Backend_IDevice
     public function update(Syncope_Model_IDevice $_device)
     {
         $this->_db->update('syncope_devices', array(
-               'acsversion' => $_device->acsversion,
-               'policykey'  => $_device->policykey,
-               'remotewipe' => $_device->remotewipe
+               'acsversion'   => $_device->acsversion,
+               'policykey'    => $_device->policykey,
+               'pingfolder'   => $_device->pingfolder,
+               'pinglifetime' => $_device->pinglifetime,
+               'remotewipe'   => $_device->remotewipe
         ), array(
                'id = ?' => $_device->id
         ));
index 97d5475..52ebb69 100644 (file)
@@ -141,11 +141,11 @@ class Syncope_Backend_Folder implements Syncope_Backend_IFolder
     }
     
     /**
-     * get array of ids which got send to the client for a given class
+     * get folder indentified by $_folderId
      *
-     * @param Syncope_Model_Device|string $_deviceId
-     * @param string $_class
-     * @return array
+     * @param  Syncope_Model_Device|string  $_deviceId
+     * @param  string                       $_folderId
+     * @return Syncope_Model_IFolder
      */
     public function getFolder($_deviceId, $_folderId)
     {
@@ -160,7 +160,7 @@ class Syncope_Backend_Folder implements Syncope_Backend_IFolder
         $state = $stmt->fetchObject('Syncope_Model_Folder');
         
         if (! $state instanceof Syncope_Model_IFolder) {
-            throw new Syncope_Exception_NotFound('id not found');
+            throw new Syncope_Exception_NotFound('folder not found');
         }
         
         if (!empty($state->creation_time)) {
index 3f3d225..a5d118e 100755 (executable)
@@ -36,6 +36,15 @@ interface Syncope_Backend_IFolder
     public function resetState($_deviceId);
     
     /**
+     * get folder indentified by $_folderId
+     *
+     * @param  Syncope_Model_Device|string  $_deviceId
+     * @param  string                       $_folderId
+     * @return Syncope_Model_IFolder
+     */
+    public function getFolder($_deviceId, $_folderId);
+    
+    /**
      * get array of ids which got send to the client for a given class
      *
      * @param Syncope_Model_Device $_deviceId
index c9ffd73..ee6fe28 100644 (file)
@@ -113,6 +113,38 @@ class Syncope_Backend_SyncState implements Syncope_Backend_ISyncState
     }
     
     /**
+     * @param  Syncope_Model_IDevice|string  $_deviceId
+     * @param  Syncope_Model_IFolder|string  $_folderId
+     * @return Syncope_Model_ISyncState
+     */
+    public function getSyncState($_deviceId, $_folderId)
+    {
+        $deviceId = $_deviceId instanceof Syncope_Model_IDevice ? $_deviceId->id : $_deviceId;
+        $folderId = $_folderId instanceof Syncope_Model_IFolder ? $_folderId->id : $_folderId;
+    
+        $select = $this->_db->select()
+            ->from('syncope_synckeys')
+            ->where($this->_db->quoteIdentifier('device_id') . ' = ?', $deviceId)
+            ->where($this->_db->quoteIdentifier('type')      . ' = ?', $folderId);
+    
+        $stmt = $this->_db->query($select);
+        $state = $stmt->fetchObject('Syncope_Model_SyncState');
+    
+        if (! $state instanceof Syncope_Model_ISyncState) {
+            throw new Syncope_Exception_NotFound('id not found');
+        }
+        
+        if (!empty($state->lastsync)) {
+            $state->lastsync = new DateTime($state->lastsync, new DateTimeZone('utc'));
+        }
+        if (!empty($state->pendingdata)) {
+            $state->pendingdata = Zend_Json::encode($state->pendingdata);
+        }
+        
+        return $state;
+    }
+    
+    /**
      * delete all stored synckeys for given type
      *
      * @param  Syncope_Model_IDevice|string  $_deviceId
index 6b9b719..1d00dff 100644 (file)
@@ -45,6 +45,7 @@ class Syncope_Command_Ping extends Syncope_Command_Wbxml
     protected $_defaultNameSpace = 'uri:Ping';
     protected $_documentElement = 'Ping';
     
+    protected $_foldersWithChanges = array();
     
     /**
      * process the XML file and add, change, delete or fetches data 
@@ -55,54 +56,47 @@ class Syncope_Command_Ping extends Syncope_Command_Wbxml
      */
     public function handle()
     {
-        $controller = Syncope_Controller::getInstance();
-        
         $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->_inputDom instanceof DOMDocument) {
-            #$xml = simplexml_load_string($this->_inputDom->saveXML());
-            $xml = new SimpleXMLElement($this->_inputDom->saveXML(), LIBXML_NOWARNING);
+            $xml = simplexml_import_dom($this->_inputDom);
             $xml->registerXPathNamespace('Ping', 'Ping');    
 
             if(isset($xml->HeartBeatInterval)) {
-                $this->_device->pinglifetime = $xml->HeartBeatInterval;
+                $this->_device->pinglifetime = (int)$xml->HeartBeatInterval;
             }
             
             if(isset($xml->Folders->Folder)) {
+                $folders = array();
                 foreach ($xml->Folders->Folder as $folderXml) {
-                    #Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " folderType: " . print_r($folderXml, true));
-                    #$folderBackend = $this->_backend->factory((string)$folderXml->Class);
                     try {
                         // does the folder exist?
-            #            $folderBackend->getFolder($folderXml->Id);
-                        
-                        $folder = array(
-                            'serverEntryId' => (string)$folderXml->Id,
-                            'folderType'    => (string)$folderXml->Class
-                        );
+                        $folder = $this->_folderBackend->getFolder($this->_device, (string)$folderXml->Id);
                         
                         $folders[] = $folder;                
                     } catch (Exception $e) {
-                        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
+                        if ($this->_logger instanceof Zend_Log) 
+                            $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
                         $status = self::STATUS_FOLDER_NOT_FOUND;
                         break;
                     }
                 }
                 $this->_device->pingfolder = serialize($folders);
             }
-            $this->_device = $controller->updateDevice($this->_device);
+            $this->_device = $this->_deviceBackend->update($this->_device);
         }
         
         $lifeTime = $this->_device->pinglifetime;
-        Tinebase_Core::setExecutionLifeTime($lifeTime);
+        #Tinebase_Core::setExecutionLifeTime($lifeTime);
         
         $intervalEnd = $intervalStart + $lifeTime;
         $secondsLeft = $intervalEnd;
         $folders = unserialize($this->_device->pingfolder);
         
-        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Folders to monitor($lifeTime / $intervalStart / $intervalEnd / $status): " . print_r($folders, true));
+        if ($this->_logger instanceof Zend_Log) 
+            $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " Folders to monitor($lifeTime / $intervalStart / $intervalEnd / $status): " . print_r($folders, true));
         
         if($status === self::STATUS_NO_CHANGES_FOUND) {
 
@@ -110,29 +104,28 @@ class Syncope_Command_Ping extends Syncope_Command_Wbxml
             
             do {
                 foreach((array) $folders as $folder) {
-                    $dataController = Syncope_Controller::dataFactory($folder['folderType'], $this->_device, $this->_syncTimeStamp);
-                    #if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . print_r($folder, true));
+                    $dataController = Syncope_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp);
+                    
                     try {
-                        $syncState = $controller->getSyncState($this->_device, $folder['folderType'], $folder['serverEntryId']);
+                        $syncState = $this->_syncStateBackend->getSyncState($this->_device, $folder);
                         
                         $count = $this->_getItemEstimate(
                             $dataController,
                             $folder,
                             $syncState->lastsync
                         );
-                    } catch (Syncope_Exception_SyncStateNotFound $e) {
+                    } catch (Syncope_Exception_NotFound $e) {
                         // folder got never synchronized to client
-                        if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
-                        if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' syncstate not found. enforce sync for folder: ' . $folder['serverEntryId']);
+                        if ($this->_logger instanceof Zend_Log) 
+                            $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getMessage());
+                        if ($this->_logger instanceof Zend_Log) 
+                            $this->_logger->info(__METHOD__ . '::' . __LINE__ . ' syncstate not found. enforce sync for folder: ' . $folder['serverFolderId']);
                         
                         $count = 1;
                     }
                         
                     if($count > 0) {
-                        $folderWithChanges[] = array(
-                            'serverEntryId' => $folder['serverEntryId'],
-                            'folderType'    => $folder['folderType']
-                        );
+                        $this->_foldersWithChanges[] = $folder;
                         $status = self::STATUS_CHANGES_FOUND;
                     }
                 }
@@ -143,7 +136,8 @@ class Syncope_Command_Ping extends Syncope_Command_Wbxml
                 
                 // another process synchronized data already
                 if(isset($syncState) && $syncState->lastsync > $this->_syncTimeStamp) {
-                    Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " terminate ping process. Some other process updated data already.");
+                    if ($this->_logger instanceof Zend_Log) 
+                        $this->_logger->info(__METHOD__ . '::' . __LINE__ . " terminate ping process. Some other process updated data already.");
                     break;
                 }
                 
@@ -153,16 +147,18 @@ class Syncope_Command_Ping extends Syncope_Command_Wbxml
             } while($secondsLeft > 0);
         }
         
-        Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " Lifetime: $lifeTime SecondsLeft: $secondsLeft Status: $status)");
+        if ($this->_logger instanceof Zend_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($folderWithChanges as $changedFolder) {
-                $folder = $folders->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Folder', $changedFolder['serverEntryId']));
-                #$folder->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Id', $changedFolder['serverEntryId']));
-                Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " changes in folder: " . $changedFolder['serverEntryId']);
+            
+            foreach($this->_foldersWithChanges as $changedFolder) {
+                $folder = $folders->appendChild($this->_outputDom->createElementNS('uri:Ping', 'Folder', $changedFolder->folderid));
+                if ($this->_logger instanceof Zend_Log) 
+                    $this->_logger->info(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " changes in folder: " . $changedFolder->folderid);
             }
         }                
     }
@@ -173,8 +169,8 @@ class Syncope_Command_Ping extends Syncope_Command_Wbxml
      */
     public function getResponse()
     {
-        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
-            Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . $this->_outputDom->saveXML());
+        if ($this->_logger instanceof Zend_Log) 
+            $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $this->_outputDom->saveXML());
     
         return $this->_outputDom;
     }
@@ -187,11 +183,13 @@ class Syncope_Command_Ping extends Syncope_Command_Wbxml
      * @param $_lastSyncTimeStamp
      * @return int number of changed entries
      */
-    private function _getItemEstimate($_dataController, $_collectionData, $_lastSyncTimeStamp)
+    protected function _getItemEstimate($_dataController, $_collectionData, $_lastSyncTimeStamp)
     {
+        return 1;
+        
         // hack to have the same variable names all over the place
-        $_collectionData['class']        = $_collectionData['folderType'];
-        $_collectionData['collectionId'] = $_collectionData['serverEntryId'];
+        $_collectionData['class']        = $_collectionData['class'];
+        $_collectionData['collectionId'] = $_collectionData['serverFolderId'];
         
         $contentStateBackend  = new Syncope_Backend_ContentState();
         $folderStateBackend   = new Syncope_Backend_FolderState();
index 9f8d20b..17e5eb2 100644 (file)
@@ -109,7 +109,6 @@ class Syncope_Command_Sync extends Syncope_Command_Wbxml
     public function handle()
     {
         // input xml
-        #$xml = new SimpleXMLElement($this->_inputDom->saveXML());
         $xml = simplexml_import_dom($this->_inputDom);
         
         foreach ($xml->Collections->Collection as $xmlCollection) {
index 47c028d..807fae4 100644 (file)
@@ -22,6 +22,7 @@
  * @property    string   policykey
  * @property    string   owner_id
  * @property    string   acsversion
+ * @property    string   pingfolder
  * @property    string   pinglifetime
  * @property    string   remotewipe
  */
index 27e5271..882dede 100644 (file)
@@ -45,6 +45,8 @@ abstract class Syncope_Command_ATestCase extends PHPUnit_Framework_TestCase
      */
     protected $_db;
     
+    protected $_logPriority = Zend_Log::ALERT;
+    
     /**
      * (non-PHPdoc)
      * @see Syncope/Syncope_TestCase::setUp()
@@ -57,7 +59,7 @@ abstract class Syncope_Command_ATestCase extends PHPUnit_Framework_TestCase
 
         #$writer = new Zend_Log_Writer_Null();
         
-        $filter = new Zend_Log_Filter_Priority(Zend_Log::CRIT);
+        $filter = new Zend_Log_Filter_Priority($this->_logPriority);
         $writer = new Zend_Log_Writer_Stream('php://output');
         $writer->addFilter($filter);
         
index ba943a8..90a7536 100755 (executable)
@@ -21,6 +21,7 @@ class Syncope_Command_AllTests
         
         $suite->addTestSuite('Syncope_Command_FolderCreateTests');
         $suite->addTestSuite('Syncope_Command_FolderSyncTests');
+        $suite->addTestSuite('Syncope_Command_PingTests');
         $suite->addTestSuite('Syncope_Command_ProvisionTests');
         $suite->addTestSuite('Syncope_Command_SearchTests');
         $suite->addTestSuite('Syncope_Command_SyncTests');
diff --git a/tests/Syncope/Command/PingTests.php b/tests/Syncope/Command/PingTests.php
new file mode 100644 (file)
index 0000000..a99225d
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tests
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright   Copyright (c) 2010-2011 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Cornelius Weiss <c.weiss@metaways.de>
+ */
+
+/**
+ * Test class for FolderSync_Controller_Event
+ * 
+ * @package     Tests
+ */
+class Syncope_Command_PingTests extends Syncope_Command_ATestCase
+{
+    #protected $_logPriority = Zend_Log::DEBUG;
+    
+    /**
+     * Runs the test methods of this class.
+     *
+     * @access public
+     * @static
+     */
+    public static function main()
+    {
+        $suite  = new PHPUnit_Framework_TestSuite('ActiveSync FolderCreate command tests');
+        PHPUnit_TextUI_TestRunner::run($suite);
+    }
+    
+    /**
+     * 
+     */
+    public function testPingWithNoSyncRunningBefore()
+    {
+        $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>14</Id><Class>Contacts</Class></Folder></Folders></Ping>'
+        );
+        
+        $search = new Syncope_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(Syncope_Command_Ping::STATUS_FOLDER_NOT_FOUND, $nodes->item(0)->nodeValue, $responseDoc->saveXML());
+    }
+        
+    /**
+     * 
+     */
+    public function testPing()
+    {
+        // 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 Syncope_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 Syncope_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 Syncope_Command_Sync($doc, $this->_device, $this->_device->policykey);
+        $sync->handle();
+        $syncDoc = $sync->getResponse();
+        #$syncDoc->formatOutput = true; echo $syncDoc->saveXML();
+
+        // sleep one second; otherwise we are to fast
+        sleep(1);
+        
+        // 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 Syncope_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(Syncope_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());
+        
+    }    
+}
index 18faed2..594805e 100644 (file)
@@ -37,6 +37,7 @@ function getTestDatabase()
         `acsversion` varchar(40) NOT NULL,
         `pinglifetime` int(11) DEFAULT NULL,
         `remotewipe` int(11) DEFAULT '0',
+        `pingfolder` longblob,
         PRIMARY KEY (`id`)
        )");