Page MenuHomePhorge

No OneTemporary

Size
142 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/ext/Syncroton/Command/FolderCreate.php b/lib/ext/Syncroton/Command/FolderCreate.php
index 98a1e7f..47aefcb 100644
--- a/lib/ext/Syncroton/Command/FolderCreate.php
+++ b/lib/ext/Syncroton/Command/FolderCreate.php
@@ -1,120 +1,120 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync FolderSync command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_FolderCreate extends Syncroton_Command_Wbxml
{
protected $_defaultNameSpace = 'uri:FolderHierarchy';
protected $_documentElement = 'FolderCreate';
protected $_classes = array(
Syncroton_Data_Factory::CLASS_CALENDAR,
Syncroton_Data_Factory::CLASS_CONTACTS,
Syncroton_Data_Factory::CLASS_EMAIL,
Syncroton_Data_Factory::CLASS_TASKS
);
/**
* synckey sent from client
*
* @var string
*/
protected $_syncKey;
protected $_parentId;
protected $_displayName;
protected $_type;
/**
* parse FolderCreate request
*
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
$this->_syncKey = (int)$xml->SyncKey;
$this->_parentId = (string)$xml->ParentId;
$this->_displayName = (string)$xml->DisplayName;
$this->_type = (int)$xml->Type;
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " synckey is $this->_syncKey");
switch((int)$xml->Type) {
case Syncroton_Command_FolderSync::FOLDERTYPE_CALENDAR_USER_CREATED:
$this->_class = Syncroton_Data_Factory::CLASS_CALENDAR;
break;
case Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT_USER_CREATED:
$this->_class = Syncroton_Data_Factory::CLASS_CONTACTS;
break;
case Syncroton_Command_FolderSync::FOLDERTYPE_MAIL_USER_CREATED:
$this->_class = Syncroton_Data_Factory::CLASS_EMAIL;
break;
case Syncroton_Command_FolderSync::FOLDERTYPE_TASK_USER_CREATED:
$this->_class = Syncroton_Data_Factory::CLASS_TASKS;
break;
default:
throw new Syncroton_Exception_UnexpectedValue('invalid type defined');
break;
}
$this->_syncState = $this->_syncStateBackend->validate($this->_device, 'FolderSync', $this->_syncKey);
}
/**
* generate FolderCreate response
*/
public function getResponse()
{
$folderCreate = $this->_outputDom->documentElement;
if($this->_syncState == false) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " INVALID synckey");
$folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_INVALID_SYNC_KEY));
} else {
$this->_syncState->counter++;
$dataController = Syncroton_Data_Factory::factory($this->_class, $this->_device, $this->_syncTimeStamp);
$folder = $dataController->createFolder(new Syncroton_Model_Folder(array(
'device_id' => $this->_device,
'class' => $this->_class,
'parentid' => $this->_parentId,
'displayname' => $this->_displayName,
'type' => $this->_type,
'creation_time' => $this->_syncTimeStamp,
'lastfiltertype' => null
)));
// create xml output
$folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_SUCCESS));
$folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $this->_syncState->counter));
$folderCreate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ServerId', $folder->folderid));
// store folder in state backend
$this->_folderBackend->create($folder);
$this->_syncStateBackend->update($this->_syncState);
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/FolderDelete.php b/lib/ext/Syncroton/Command/FolderDelete.php
index 0eb1d34..e390105 100644
--- a/lib/ext/Syncroton/Command/FolderDelete.php
+++ b/lib/ext/Syncroton/Command/FolderDelete.php
@@ -1,98 +1,98 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync FolderDelete command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_FolderDelete extends Syncroton_Command_Wbxml
{
protected $_defaultNameSpace = 'uri:FolderHierarchy';
protected $_documentElement = 'FolderDelete';
protected $_classes = array(
Syncroton_Data_Factory::CLASS_CALENDAR,
Syncroton_Data_Factory::CLASS_CONTACTS,
Syncroton_Data_Factory::CLASS_EMAIL,
Syncroton_Data_Factory::CLASS_TASKS
);
protected $_serverId;
/**
* @var Syncroton_Model_ISyncState
*/
protected $_syncState;
/**
* parse FolderDelete request
*
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
$syncKey = (int)$xml->SyncKey;
$folderId = (string)$xml->ServerId;
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " synckey is $syncKey");
$this->_syncState = $this->_syncStateBackend->validate($this->_device, 'FolderSync', $syncKey);
try {
$this->_folder = $this->_folderBackend->getFolder($this->_device, $folderId);
$dataController = Syncroton_Data_Factory::factory($this->_folder->class, $this->_device, $this->_syncTimeStamp);
$dataController->deleteFolder($this->_folder);
$this->_folderBackend->delete($this->_folder);
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $senf->getMessage());
}
}
/**
* generate FolderDelete response
*
* @todo currently we support only the main folder which contains all contacts/tasks/events/notes per class
*/
public function getResponse()
{
$folderDelete = $this->_outputDom->documentElement;
if($this->_syncState == false) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " INVALID synckey");
$folderDelete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_INVALID_SYNC_KEY));
} else {
if ($this->_folder instanceof Syncroton_Model_IFolder) {
$this->_syncState->counter++;
// create xml output
$folderDelete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_SUCCESS));
$folderDelete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $this->_syncState->counter));
$this->_syncStateBackend->update($this->_syncState);
} else {
// create xml output
$folderDelete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_FOLDER_NOT_FOUND));
$folderDelete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $this->_syncState->counter));
}
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/FolderSync.php b/lib/ext/Syncroton/Command/FolderSync.php
index d42ecd7..df82ca4 100644
--- a/lib/ext/Syncroton/Command/FolderSync.php
+++ b/lib/ext/Syncroton/Command/FolderSync.php
@@ -1,216 +1,235 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync FolderSync command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_FolderSync extends Syncroton_Command_Wbxml
{
const STATUS_SUCCESS = 1;
const STATUS_FOLDER_EXISTS = 2;
const STATUS_IS_SPECIAL_FOLDER = 3;
const STATUS_FOLDER_NOT_FOUND = 4;
const STATUS_PARENT_FOLDER_NOT_FOUND = 5;
const STATUS_SERVER_ERROR = 6;
const STATUS_ACCESS_DENIED = 7;
const STATUS_REQUEST_TIMED_OUT = 8;
const STATUS_INVALID_SYNC_KEY = 9;
const STATUS_MISFORMATTED = 10;
const STATUS_UNKNOWN_ERROR = 11;
/**
* some usefull constants for working with the xml files
*
*/
const FOLDERTYPE_GENERIC_USER_CREATED = 1;
const FOLDERTYPE_INBOX = 2;
const FOLDERTYPE_DRAFTS = 3;
const FOLDERTYPE_DELETEDITEMS = 4;
const FOLDERTYPE_SENTMAIL = 5;
const FOLDERTYPE_OUTBOX = 6;
const FOLDERTYPE_TASK = 7;
const FOLDERTYPE_CALENDAR = 8;
const FOLDERTYPE_CONTACT = 9;
const FOLDERTYPE_NOTE = 10;
const FOLDERTYPE_JOURNAL = 11;
const FOLDERTYPE_MAIL_USER_CREATED = 12;
const FOLDERTYPE_CALENDAR_USER_CREATED = 13;
const FOLDERTYPE_CONTACT_USER_CREATED = 14;
const FOLDERTYPE_TASK_USER_CREATED = 15;
const FOLDERTYPE_JOURNAL_USER_CREATED = 16;
const FOLDERTYPE_NOTES_USER_CREATED = 17;
const FOLDERTYPE_UNKOWN = 18;
protected $_defaultNameSpace = 'uri:FolderHierarchy';
protected $_documentElement = 'FolderSync';
protected $_classes = array(
Syncroton_Data_Factory::CLASS_CALENDAR,
Syncroton_Data_Factory::CLASS_CONTACTS,
Syncroton_Data_Factory::CLASS_EMAIL,
Syncroton_Data_Factory::CLASS_TASKS
);
/**
* @var string
*/
protected $_syncKey;
/**
* parse FolderSync request
*
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ #if ($this->_statusProvisioning = $this->_checkProvisioningNeeded() !== false) {
+ # if (version_compare($this->_device->acsversion, '14.0', '<')) {
+ # throw new Syncroton_Exception_ProvisioningNeeded();
+ # } else {
+ # return;
+ # }
+ #}
+
+ $xml = simplexml_import_dom($this->_requestBody);
$syncKey = (int)$xml->SyncKey;
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " synckey is $syncKey");
if ($syncKey == 0) {
$this->_syncState = new Syncroton_Model_SyncState(array(
'device_id' => $this->_device,
'counter' => 0,
'type' => 'FolderSync',
'lastsync' => $this->_syncTimeStamp
));
// reset state of foldersync
$this->_syncStateBackend->resetState($this->_device, 'FolderSync');
} else {
if (($this->_syncState = $this->_syncStateBackend->validate($this->_device, 'FolderSync', $syncKey)) instanceof Syncroton_Model_SyncState) {
$this->_syncState->lastsync = $this->_syncTimeStamp;
} else {
$this->_syncStateBackend->resetState($this->_device, 'FolderSync');
}
}
}
/**
* generate FolderSync response
*
* @todo changes are missing in response (folder got renamed for example)
*/
public function getResponse()
{
$folderSync = $this->_outputDom->documentElement;
+ // provioning needed?
+ #if ($this->_statusProvisioning !== false) {
+ # if ($this->_logger instanceof Zend_Log)
+ # $this->_logger->info(__METHOD__ . '::' . __LINE__ . " provisiong needed or remote wipe requested");
+ # $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', $this->_statusProvisioning));
+ #
+ # return $this->_outputDom;
+ #}
+
+ // invalid synckey provided
if($this->_syncState === false) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " INVALID synckey provided");
$folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', self::STATUS_INVALID_SYNC_KEY));
- } else {
- $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', self::STATUS_SUCCESS));
+ return $this->_outputDom;
+ }
+
+ $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', self::STATUS_SUCCESS));
+
+ $adds = array();
+ $deletes = array();
+
+ foreach($this->_classes as $class) {
+ try {
+ $dataController = Syncroton_Data_Factory::factory($class, $this->_device, $this->_syncTimeStamp);
+ } catch (Zend_Exception $ze) {
+ // backend not defined
+ if ($this->_logger instanceof Zend_Log)
+ $this->_logger->info(__METHOD__ . '::' . __LINE__ . " no data backend defined for class: " . $class);
+ continue;
+ }
- $adds = array();
- $deletes = array();
+ // retrieve all folders available in data backend
+ $serverFolders = $dataController->getAllFolders();
+ // retrieve all folders sent to client
+ $clientFolders = $this->_folderBackend->getFolderState($this->_device, $class);
- foreach($this->_classes as $class) {
- try {
- $dataController = Syncroton_Data_Factory::factory($class, $this->_device, $this->_syncTimeStamp);
- } catch (Zend_Exception $ze) {
- // backend not defined
- if ($this->_logger instanceof Zend_Log)
- $this->_logger->info(__METHOD__ . '::' . __LINE__ . " no data backend defined for class: " . $class);
- continue;
- }
-
- // retrieve all folders available in data backend
- $serverFolders = $dataController->getAllFolders();
- // retrieve all folders sent to client
- $clientFolders = $this->_folderBackend->getFolderState($this->_device, $class);
-
- $serverFoldersIds = array_keys($serverFolders);
-
- // is this the first sync?
- if($this->_syncState->counter == 0) {
- $clientFoldersIds = array();
+ $serverFoldersIds = array_keys($serverFolders);
+
+ // is this the first sync?
+ if($this->_syncState->counter == 0) {
+ $clientFoldersIds = array();
+ } else {
+ $clientFoldersIds = array_keys($clientFolders);
+ }
+
+ // calculate added entries
+ $serverDiff = array_diff($serverFoldersIds, $clientFoldersIds);
+ foreach($serverDiff as $serverFolderId) {
+ if (isset($clientFolders[$serverFolderId])) {
+ $adds[] = $clientFolders[$serverFolderId];
} else {
- $clientFoldersIds = array_keys($clientFolders);
- }
-
- // calculate added entries
- $serverDiff = array_diff($serverFoldersIds, $clientFoldersIds);
- foreach($serverDiff as $serverFolderId) {
- if (isset($clientFolders[$serverFolderId])) {
- $adds[] = $clientFolders[$serverFolderId];
- } else {
- $adds[] = new Syncroton_Model_Folder(array(
- 'device_id' => $this->_device,
- 'class' => $class,
- 'folderid' => $serverFolders[$serverFolderId]->folderid,
- 'parentid' => $serverFolders[$serverFolderId]->parentid,
- 'displayname' => $serverFolders[$serverFolderId]->displayname,
- 'type' => $serverFolders[$serverFolderId]->type,
- 'creation_time' => $this->_syncTimeStamp,
- 'lastfiltertype' => null
- ));
- }
- }
-
- // calculate deleted entries
- $serverDiff = array_diff($clientFoldersIds, $serverFoldersIds);
- foreach($serverDiff as $serverFolderId) {
- $deletes[] = $clientFolders[$serverFolderId];
+ $adds[] = new Syncroton_Model_Folder(array(
+ 'device_id' => $this->_device,
+ 'class' => $class,
+ 'folderid' => $serverFolders[$serverFolderId]->folderid,
+ 'parentid' => $serverFolders[$serverFolderId]->parentid,
+ 'displayname' => $serverFolders[$serverFolderId]->displayname,
+ 'type' => $serverFolders[$serverFolderId]->type,
+ 'creation_time' => $this->_syncTimeStamp,
+ 'lastfiltertype' => null
+ ));
}
}
- $count = count($adds) + /*count($changes) + */count($deletes);
- if($count > 0) {
- $this->_syncState->counter++;
+ // calculate deleted entries
+ $serverDiff = array_diff($clientFoldersIds, $serverFoldersIds);
+ foreach($serverDiff as $serverFolderId) {
+ $deletes[] = $clientFolders[$serverFolderId];
}
+ }
+
+ $count = count($adds) + /*count($changes) + */count($deletes);
+ if($count > 0) {
+ $this->_syncState->counter++;
+ }
+
+ // create xml output
+ $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $this->_syncState->counter));
+
+ $changes = $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Changes'));
+ $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Count', $count));
+
+ foreach($adds as $folder) {
- // create xml output
- $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $this->_syncState->counter));
+ $add = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Add'));
+ $add->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ServerId', $folder->folderid));
+ $add->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ParentId', $folder->parentid));
- $changes = $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Changes'));
- $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Count', $count));
+ $displayName = $this->_outputDom->createElementNS('uri:FolderHierarchy', 'DisplayName');
+ $displayName->appendChild($this->_outputDom->createTextNode($folder->displayname));
+ $add->appendChild($displayName);
- foreach($adds as $folder) {
-
- $add = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Add'));
- $add->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ServerId', $folder->folderid));
- $add->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ParentId', $folder->parentid));
-
- $displayName = $this->_outputDom->createElementNS('uri:FolderHierarchy', 'DisplayName');
- $displayName->appendChild($this->_outputDom->createTextNode($folder->displayname));
- $add->appendChild($displayName);
-
- $add->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Type', $folder->type));
-
- // store folder in backend
- if (empty($folder->id)) {
- $this->_folderBackend->create($folder);
- }
- }
+ $add->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Type', $folder->type));
- foreach($deletes as $folder) {
- $delete = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Delete'));
- $delete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ServerId', $folder->folderid));
-
- $this->_folderBackend->delete($folder);
+ // store folder in backend
+ if (empty($folder->id)) {
+ $this->_folderBackend->create($folder);
}
+ }
+
+ foreach($deletes as $folder) {
+ $delete = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Delete'));
+ $delete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ServerId', $folder->folderid));
- if (empty($this->_syncState->id)) {
- $this->_syncStateBackend->create($this->_syncState);
- } else {
- $this->_syncStateBackend->update($this->_syncState);
- }
+ $this->_folderBackend->delete($folder);
+ }
+
+ if (empty($this->_syncState->id)) {
+ $this->_syncStateBackend->create($this->_syncState);
+ } else {
+ $this->_syncStateBackend->update($this->_syncState);
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/FolderUpdate.php b/lib/ext/Syncroton/Command/FolderUpdate.php
index 36d764d..5089bd7 100644
--- a/lib/ext/Syncroton/Command/FolderUpdate.php
+++ b/lib/ext/Syncroton/Command/FolderUpdate.php
@@ -1,93 +1,93 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync FolderUpdate command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_FolderUpdate extends Syncroton_Command_Wbxml
{
protected $_defaultNameSpace = 'uri:FolderHierarchy';
protected $_documentElement = 'FolderUpdate';
protected $_classes = array('Contacts', 'Tasks', 'Email');
/**
* synckey sent from client
*
* @var string
*/
protected $_syncKey;
protected $_parentId;
protected $_displayName;
protected $_serverId;
/**
* parse FolderUpdate request
*
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
$this->_syncKey = (int)$xml->SyncKey;
$this->_serverId = (string)$xml->ServerId;
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " synckey is $this->_syncKey parentId $this->_parentId name $this->_displayName");
$this->_syncState = $this->_syncStateBackend->validate($this->_device, 'FolderSync', $this->_syncKey);
try {
$folder = $this->_folderBackend->getFolder($this->_device, $this->_serverId);
$folder->displayname = (string)$xml->DisplayName;
$folder->parentid = (string)$xml->ParentId;
$dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp);
$dataController->updateFolder($folder);
$this->_folderBackend->update($folder);
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $senf->getMessage());
}
}
/**
* generate FolderUpdate response
*
* @todo currently we support only the main folder which contains all contacts/tasks/events/notes per class
*/
public function getResponse($_keepSession = FALSE)
{
$folderUpdate = $this->_outputDom->documentElement;
if($this->_syncState == false) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " INVALID synckey");
$folderUpdate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_INVALID_SYNC_KEY));
} else {
$this->_syncState->counter++;
// create xml output
$folderUpdate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', Syncroton_Command_FolderSync::STATUS_SUCCESS));
$folderUpdate->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'SyncKey', $this->_syncState->counter));
$this->_syncStateBackend->update($this->_syncState);
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/GetItemEstimate.php b/lib/ext/Syncroton/Command/GetItemEstimate.php
index 89d558f..de0de96 100644
--- a/lib/ext/Syncroton/Command/GetItemEstimate.php
+++ b/lib/ext/Syncroton/Command/GetItemEstimate.php
@@ -1,151 +1,151 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync GetItemEstimate command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_GetItemEstimate extends Syncroton_Command_Wbxml
{
const STATUS_SUCCESS = 1;
const STATUS_INVALID_COLLECTION = 2;
const STATUS_SYNC_STATE_NOT_PRIMED = 3;
const STATUS_INVALID_SYNC_KEY = 4;
protected $_defaultNameSpace = 'uri:ItemEstimate';
protected $_documentElement = 'GetItemEstimate';
/**
* list of collections
*
* @var array
*/
protected $_collections = array();
/**
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
foreach ($xml->Collections->Collection as $xmlCollection) {
// fetch values from a different namespace
$airSyncValues = $xmlCollection->children('uri:AirSync');
$collectionData = array(
'syncKey' => (int)$airSyncValues->SyncKey,
'class' => (string) $xmlCollection->Class,
'collectionId' => (string) $xmlCollection->CollectionId,
'filterType' => isset($airSyncValues->FilterType) ? (int)$airSyncValues->FilterType : 0
);
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " synckey is {$collectionData['syncKey']} class: {$collectionData['class']} collectionid: {$collectionData['collectionId']} filtertype: {$collectionData['filterType']}");
try {
// does the folder exist?
$collectionData['folder'] = $this->_folderBackend->getFolder($this->_device, $collectionData['collectionId']);
$collectionData['folder']->lastfiltertype = $collectionData['filterType'];
if($collectionData['syncKey'] === 0) {
$collectionData['syncState'] = new Syncroton_Model_SyncState(array(
'device_id' => $this->_device,
'counter' => 0,
'type' => $collectionData['folder'],
'lastsync' => $this->_syncTimeStamp
));
// reset sync state for this folder
$this->_syncStateBackend->resetState($this->_device, $collectionData['folder']);
$this->_contentStateBackend->resetState($this->_device, $collectionData['folder']);
} else {
$collectionData['syncState'] = $this->_syncStateBackend->validate($this->_device, $collectionData['folder'], $collectionData['syncKey']);
}
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $senf->getMessage());
}
$this->_collections[$collectionData['collectionId']] = $collectionData;
}
}
/**
* (non-PHPdoc)
* @see Syncroton_Command_Wbxml::getResponse()
*/
public function getResponse()
{
$itemEstimate = $this->_outputDom->documentElement;
foreach($this->_collections as $collectionData) {
$response = $itemEstimate->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Response'));
// invalid collectionid provided
if (empty($collectionData['folder'])) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " folder does not exist");
$response->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Status', self::STATUS_INVALID_COLLECTION));
$collection = $response->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Collection'));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Class', $collectionData['class']));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'CollectionId', $collectionData['collectionId']));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Estimate', 0));
} elseif (! ($collectionData['syncState'] instanceof Syncroton_Model_ISyncState)) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " invalid synckey ${collectionData['syncKey']} provided");
/*
* Android phones (and maybe others) don't take care about status 4(INVALID_SYNC_KEY)
* To solve the problem we always return status 1(SUCCESS) even the sync key is invalid with Estimate set to 1.
* This way the phone gets forced to sync. Handling invalid synckeys during sync command works without any problems.
*
$response->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Status', self::STATUS_INVALID_SYNC_KEY));
$collection = $response->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Collection'));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Class', $collectionData['class']));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'CollectionId', $collectionData['collectionId']));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Estimate', 0));
*/
$response->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Status', self::STATUS_SUCCESS));
$collection = $response->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Collection'));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Class', $collectionData['class']));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'CollectionId', $collectionData['collectionId']));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Estimate', 1));
} else {
$dataController = Syncroton_Data_Factory::factory($collectionData['folder']->class, $this->_device, $this->_syncTimeStamp);
$response->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Status', self::STATUS_SUCCESS));
$collection = $response->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Collection'));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Class', $collectionData['class']));
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'CollectionId', $collectionData['collectionId']));
if($collectionData['syncState']->counter === 0) {
// this is the first sync. in most cases there are data on the server.
$count = count($dataController->getServerEntries($collectionData['collectionId'], $collectionData['filterType']));
} else {
$count = $dataController->getCountOfChanges($this->_contentStateBackend, $collectionData['folder'], $collectionData['syncState']);
}
$collection->appendChild($this->_outputDom->createElementNS('uri:ItemEstimate', 'Estimate', $count));
}
// folderState can be NULL in case of not existing folder
if (isset($collectionData['folder'])) {
$this->_folderBackend->update($collectionData['folder']);
}
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/ItemOperations.php b/lib/ext/Syncroton/Command/ItemOperations.php
index 05ef2fa..aef2641 100644
--- a/lib/ext/Syncroton/Command/ItemOperations.php
+++ b/lib/ext/Syncroton/Command/ItemOperations.php
@@ -1,138 +1,138 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync ItemOperations command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_ItemOperations extends Syncroton_Command_Wbxml
{
const STATUS_SUCCESS = 1;
const STATUS_PROTOCOL_ERROR = 2;
const STATUS_SERVER_ERROR = 3;
const STATUS_ITEM_FAILED_CONVERSION = 14;
protected $_defaultNameSpace = 'uri:ItemOperations';
protected $_documentElement = 'ItemOperations';
/**
* list of items to move
*
* @var array
*/
protected $_fetches = array();
/**
* parse MoveItems request
*
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
if (isset($xml->Fetch)) {
foreach ($xml->Fetch as $fetch) {
$fetchArray = array(
'store' => (string)$fetch->Store
);
// try to fetch element from namespace AirSync
$airSync = $fetch->children('uri:AirSync');
if (isset($airSync->CollectionId)) {
$fetchArray['collectionId'] = (string)$airSync->CollectionId;
$fetchArray['serverId'] = (string)$airSync->ServerId;
}
// try to fetch element from namespace AirSyncBase
$airSyncBase = $fetch->children('uri:AirSyncBase');
if (isset($airSyncBase->FileReference)) {
$fetchArray['fileReference'] = (string)$airSyncBase->FileReference;
}
if (isset($fetch->Options)) {
// try to fetch element from namespace AirSyncBase
$airSyncBase = $fetch->Options->children('uri:AirSyncBase');
if (isset($airSyncBase->BodyPreference)) {
// required
$fetchArray['bodyPreferenceType'] = (int) $airSyncBase->BodyPreference->Type;
// optional
if (isset($airSyncBase->BodyPreference->TruncationSize)) {
$fetchArray['truncationSize'] = (int) $airSyncBase->BodyPreference->TruncationSize;
}
}
}
$this->_fetches[] = $fetchArray;
}
}
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " fetches: " . print_r($this->_fetches, true));
}
/**
* generate ItemOperations response
*/
public function getResponse()
{
// add aditional namespaces
$this->_outputDom->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:AirSyncBase' , 'uri:AirSyncBase');
$this->_outputDom->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:AirSync' , 'uri:AirSync');
$itemOperations = $this->_outputDom->documentElement;
$itemOperations->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SUCCESS));
$response = $itemOperations->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Response'));
foreach ($this->_fetches as $fetch) {
$fetchTag = $response->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Fetch'));
try {
$dataController = Syncroton_Data_Factory::factory($fetch['store'], $this->_device, $this->_syncTimeStamp);
if (isset($fetch['collectionId'])) {
$fetchTag->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SUCCESS));
$fetchTag->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $fetch['collectionId']));
$fetchTag->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $fetch['serverId']));
$properties = $this->_outputDom->createElementNS('uri:ItemOperations', 'Properties');
$dataController
->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $fetch['collectionId'])), $fetch['serverId'])
->appendXML($properties);
$fetchTag->appendChild($properties);
} elseif (isset($fetch['fileReference'])) {
$fetchTag->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SUCCESS));
$fetchTag->appendChild($this->_outputDom->createElementNS('uri:AirSyncBase', 'FileReference', $fetch['fileReference']));
$properties = $this->_outputDom->createElementNS('uri:ItemOperations', 'Properties');
$dataController
->getFileReference($fetch['fileReference'])
->appendXML($properties);
$fetchTag->appendChild($properties);
}
} catch (Syncroton_Exception_NotFound $e) {echo __LINE__;
$response->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_ITEM_FAILED_CONVERSION));
} catch (Exception $e) {echo __LINE__; echo $e->getMessage(); echo $e->getTraceAsString();
$response->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SERVER_ERROR));
}
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/MoveItems.php b/lib/ext/Syncroton/Command/MoveItems.php
index f6d31af..974cddf 100644
--- a/lib/ext/Syncroton/Command/MoveItems.php
+++ b/lib/ext/Syncroton/Command/MoveItems.php
@@ -1,93 +1,93 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync MoveItem command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_MoveItems extends Syncroton_Command_Wbxml
{
const STATUS_INVALID_SOURCE = 1;
const STATUS_INVALID_DESTINATION = 2;
const STATUS_SUCCESS = 3;
protected $_defaultNameSpace = 'uri:Move';
protected $_documentElement = 'Moves';
/**
* list of items to move
*
* @var array
*/
protected $_moves = array();
/**
* parse MoveItems request
*
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
foreach ($xml->Move as $move) {
$this->_moves[] = array(
'srcMsgId' => (string)$move->SrcMsgId,
'srcFldId' => (string)$move->SrcFldId,
'dstFldId' => (string)$move->DstFldId
);
}
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " moves: " . print_r($this->_moves, true));
}
/**
* generate MoveItems response
*/
public function getResponse()
{
$moves = $this->_outputDom->documentElement;
foreach ($this->_moves as $move) {
$response = $moves->appendChild($this->_outputDom->createElementNS('uri:Move', 'Response'));
$response->appendChild($this->_outputDom->createElementNS('uri:Move', 'SrcMsgId', $move['srcMsgId']));
try {
$sourceFolder = $this->_folderBackend->getFolder($this->_device, $move['srcFldId']);
} catch (Syncroton_Exception_NotFound $senf) {
$sourceFolder = null;
}
try {
$destinationFolder = $this->_folderBackend->getFolder($this->_device, $move['dstFldId']);
} catch (Syncroton_Exception_NotFound $senf) {
$destinationFolder = null;
}
if ($sourceFolder === null) {
$response->appendChild($this->_outputDom->createElementNS('uri:Move', 'Status', Syncroton_Command_MoveItems::STATUS_INVALID_SOURCE));
} else if ($destinationFolder === null) {
$response->appendChild($this->_outputDom->createElementNS('uri:Move', 'Status', Syncroton_Command_MoveItems::STATUS_INVALID_DESTINATION));
} else {
$dataController = Syncroton_Data_Factory::factory($sourceFolder->class, $this->_device, $this->_syncTimeStamp);
$newId = $dataController->moveItem($move['srcFldId'], $move['srcMsgId'], $move['dstFldId']);
$response->appendChild($this->_outputDom->createElementNS('uri:Move', 'Status', Syncroton_Command_MoveItems::STATUS_SUCCESS));
$response->appendChild($this->_outputDom->createElementNS('uri:Move', 'DstMsgId', $newId));
}
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/Ping.php b/lib/ext/Syncroton/Command/Ping.php
index bc76f46..a2c88b6 100644
--- a/lib/ext/Syncroton/Command/Ping.php
+++ b/lib/ext/Syncroton/Command/Ping.php
@@ -1,169 +1,169 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Ping command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_Ping extends Syncroton_Command_Wbxml
{
const STATUS_NO_CHANGES_FOUND = 1;
const STATUS_CHANGES_FOUND = 2;
const STATUS_MISSING_PARAMETERS = 3;
const STATUS_REQUEST_FORMAT_ERROR = 4;
const STATUS_INTERVAL_TO_GREAT_OR_SMALL = 5;
const STATUS_TO_MUCH_FOLDERS = 6;
const STATUS_FOLDER_NOT_FOUND = 7;
const STATUS_GENERAL_ERROR = 8;
protected $_skipValidatePolicyKey = true;
protected $_changesDetected = false;
const PING_TIMEOUT = 60;
/**
* Enter description here...
*
* @var Syncroton_Backend_StandAlone_Abstract
*/
protected $_dataBackend;
protected $_defaultNameSpace = 'uri:Ping';
protected $_documentElement = 'Ping';
protected $_foldersWithChanges = array();
/**
* 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->_inputDom instanceof DOMDocument) {
- $xml = simplexml_import_dom($this->_inputDom);
+ 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;
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " " . $senf->getMessage());
$status = self::STATUS_FOLDER_NOT_FOUND;
break;
}
}
$this->_device->pingfolder = serialize($folders);
}
$this->_device = $this->_deviceBackend->update($this->_device);
}
$lifeTime = $this->_device->pinglifetime;
#Tinebase_Core::setExecutionLifeTime($lifeTime);
$intervalEnd = $intervalStart + $lifeTime;
$secondsLeft = $intervalEnd;
$folders = unserialize($this->_device->pingfolder);
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) {
$folderWithChanges = array();
do {
foreach((array) $folders as $folder) {
$dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp);
try {
$syncState = $this->_syncStateBackend->getSyncState($this->_device, $folder);
$foundChanges = !!$dataController->getCountOfChanges($this->_contentStateBackend, $folder, $syncState);
} catch (Syncroton_Exception_NotFound $e) {
// folder got never synchronized to client
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->folderid);
$foundChanges = true;
}
if($foundChanges == true) {
$this->_foldersWithChanges[] = $folder;
$status = self::STATUS_CHANGES_FOUND;
}
}
if($status === self::STATUS_CHANGES_FOUND) {
break;
}
// another process synchronized data already
if(isset($syncState) && $syncState->lastsync > $this->_syncTimeStamp) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " terminate ping process. Some other process updated data already.");
break;
}
sleep(self::PING_TIMEOUT);
$secondsLeft = $intervalEnd - time();
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " DeviceId: " . $this->_device->deviceid . " seconds left: " . $secondsLeft);
} while($secondsLeft > 0);
}
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($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);
}
}
}
/**
* generate ping command response
*
*/
public function getResponse()
{
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/Provision.php b/lib/ext/Syncroton/Command/Provision.php
index 53374d1..7cf53a2 100644
--- a/lib/ext/Syncroton/Command/Provision.php
+++ b/lib/ext/Syncroton/Command/Provision.php
@@ -1,184 +1,184 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Provision command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_Provision extends Syncroton_Command_Wbxml
{
protected $_defaultNameSpace = 'uri:Provision';
protected $_documentElement = 'Provision';
const POLICYTYPE_XML = 'MS-WAP-Provisioning-XML';
const POLICYTYPE_WBXML = 'MS-EAS-Provisioning-WBXML';
const STATUS_SUCCESS = 1;
const STATUS_PROTOCOL_ERROR = 2;
const STATUS_GENERAL_SERVER_ERROR = 3;
const STATUS_DEVICE_MANAGED_EXTERNALLY = 4;
const REMOTEWIPE_REQUESTED = 1;
const REMOTEWIPE_CONFIRMED = 2;
protected $_skipValidatePolicyKey = true;
protected $_policyType;
protected $_sendPolicyKey;
/**
* process the XML file and add, change, delete or fetches data
*
* @return resource
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
$this->_policyType = isset($xml->Policies->Policy->PolicyType) ? (string) $xml->Policies->Policy->PolicyType : null;
$this->_sendPolicyKey = isset($xml->Policies->Policy->PolicyKey) ? (int) $xml->Policies->Policy->PolicyKey : null;
if ($this->_device->remotewipe == self::REMOTEWIPE_REQUESTED && isset($xml->RemoteWipe->Status) && (int)$xml->RemoteWipe->Status == self::STATUS_SUCCESS) {
$this->_device->remotewipe = self::REMOTEWIPE_CONFIRMED;
$this->_device = $this->_deviceBackend->update($this->_device);
}
}
/**
* generate search command response
*
*/
public function getResponse()
{
// should we wipe the device
if ($this->_device->remotewipe >= self::REMOTEWIPE_REQUESTED) {
$this->_sendRemoteWipe();
} else {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' PolicyType: ' . $this->_policyType . ' PolicyKey: ' . $this->_sendPolicyKey);
if($this->_sendPolicyKey === NULL) {
$this->_sendPolicy();
} else {
$this->_acknowledgePolicy();
}
}
return $this->_outputDom;
}
/**
* function the send policy to client
*
* 4131 (Enforce password on device) 0: enabled 1: disabled
* 4133 (Unlock from computer) 0: disabled 1: enabled
* AEFrequencyType 0: no inactivity time 1: inactivity time is set
* AEFrequencyValue inactivity time in minutes
* DeviceWipeThreshold after how many worng password to device should get wiped
* CodewordFrequency validate every 3 wrong passwords, that a person is using the device which is able to read and write. should be half of DeviceWipeThreshold
* MinimumPasswordLength minimum password length
* PasswordComplexity 0: Require alphanumeric 1: Require only numeric, 2: anything goes
*
*/
protected function _sendPolicy()
{
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . ' send policy to device');
$policyData = '<wap-provisioningdoc>
<characteristic type="SecurityPolicy">
<parm name="4131" value="0"/>
<parm name="4133" value="0"/>
</characteristic>
<characteristic type="Registry">
<characteristic type="HKLM\Comm\Security\Policy\LASSD\AE\{50C13377-C66D-400C-889E-C316FC4AB374}">
<parm name="AEFrequencyType" value="1"/>
<parm name="AEFrequencyValue" value="3"/>
</characteristic>
<characteristic type="HKLM\Comm\Security\Policy\LASSD">
<parm name="DeviceWipeThreshold" value="6"/>
</characteristic>
<characteristic type="HKLM\Comm\Security\Policy\LASSD">
<parm name="CodewordFrequency" value="3"/>
</characteristic>
<characteristic type="HKLM\Comm\Security\Policy\LASSD\LAP\lap_pw">
<parm name="MinimumPasswordLength" value="5"/>
</characteristic>
<characteristic type="HKLM\Comm\Security\Policy\LASSD\LAP\lap_pw">
<parm name="PasswordComplexity" value="2"/>
</characteristic>
</characteristic>
</wap-provisioningdoc>';
$this->_device->policykey = $this->generatePolicyKey();
$provision = $sync = $this->_outputDom->documentElement;
$provision->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Status', 1));
$policies = $provision->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Policies'));
$policy = $policies->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Policy'));
$policy->appendChild($this->_outputDom->createElementNS('uri:Provision', 'PolicyType', $this->_policyType));
$policy->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Status', 1));
$policy->appendChild($this->_outputDom->createElementNS('uri:Provision', 'PolicyKey', $this->_device->policykey));
if ($this->_policyType == self::POLICYTYPE_XML) {
$data = $policy->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Data', $policyData));
} else {
$data = $policy->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Data'));
$easProvisionDoc = $data->appendChild($this->_outputDom->createElementNS('uri:Provision', 'EASProvisionDoc'));
$easProvisionDoc->appendChild($this->_outputDom->createElementNS('uri:Provision', 'DevicePasswordEnabled', 1));
#$easProvisionDoc->appendChild($this->_outputDom->createElementNS('uri:Provision', 'MinDevicePasswordLength', 4));
#$easProvisionDoc->appendChild($this->_outputDom->createElementNS('uri:Provision', 'MaxDevicePasswordFailedAttempts', 4));
#$easProvisionDoc->appendChild($this->_outputDom->createElementNS('uri:Provision', 'MaxInactivityTimeDeviceLock', 60));
}
$this->_deviceBackend->update($this->_device);
}
/**
* function the send remote wipe command
*/
protected function _sendRemoteWipe()
{
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . ' send remote wipe to device');
$provision = $sync = $this->_outputDom->documentElement;
$provision->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Status', 1));
$provision->appendChild($this->_outputDom->createElementNS('uri:Provision', 'RemoteWipe'));
}
protected function _acknowledgePolicy()
{
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . ' acknowledge policy');
$this->_device->policykey = $this->generatePolicyKey();
$provision = $sync = $this->_outputDom->documentElement;
$provision->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Status', 1));
$policies = $provision->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Policies'));
$policy = $policies->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Policy'));
$policy->appendChild($this->_outputDom->createElementNS('uri:Provision', 'PolicyType', $this->_policyType));
$policy->appendChild($this->_outputDom->createElementNS('uri:Provision', 'Status', 1));
$policy->appendChild($this->_outputDom->createElementNS('uri:Provision', 'PolicyKey', $this->_device->policykey));
$this->_deviceBackend->update($this->_device);
}
public static function generatePolicyKey()
{
$policyKey = mt_rand(1, mt_getrandmax());
return $policyKey;
}
}
diff --git a/lib/ext/Syncroton/Command/Search.php b/lib/ext/Syncroton/Command/Search.php
index 221c078..2c6d956 100644
--- a/lib/ext/Syncroton/Command/Search.php
+++ b/lib/ext/Syncroton/Command/Search.php
@@ -1,68 +1,68 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Search command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_Search extends Syncroton_Command_Wbxml
{
const STATUS_SUCCESS = 1;
const STATUS_SERVER_ERROR = 3;
protected $_defaultNameSpace = 'uri:Search';
protected $_documentElement = 'Search';
/**
* store data
*
* @var array
*/
protected $_store = array();
/**
* parse search command request
*
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
$this->_store = array(
'name' => (string) $xml->Store->Name
);
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " stores: " . print_r($this->_store, true));
}
/**
* generate search command response
*
*/
public function getResponse()
{
$search = $this->_outputDom->documentElement;
$search->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', self::STATUS_SUCCESS));
$response = $search->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Response'));
$store = $response->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Store'));
$store->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', self::STATUS_SUCCESS));
$store->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Total', 0));
$result = $store->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Result', 0));
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/SendMail.php b/lib/ext/Syncroton/Command/SendMail.php
index accf16c..0f19f7a 100644
--- a/lib/ext/Syncroton/Command/SendMail.php
+++ b/lib/ext/Syncroton/Command/SendMail.php
@@ -1,114 +1,69 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Sendmail command
*
* @package Syncroton
* @subpackage Command
*/
-class Syncroton_Command_SendMail
+class Syncroton_Command_SendMail extends Syncroton_Command_Wbxml
{
- /**
- * save copy in sent folder
- *
- * @var boolean
- */
- protected $_saveInSent;
-
- /**
- * @var resource
- */
- protected $_inputStream;
+ protected $_defaultNameSpace = 'uri:ComposeMail';
+ protected $_documentElement = 'Sendmail';
- /**
- * informations about the currently device
- *
- * @var Syncroton_Model_Device
- */
- protected $_device;
-
- /**
- * timestamp to use for all sync requests
- *
- * @var DateTime
- */
- protected $_syncTimeStamp;
-
- /**
- * @var Zend_Log
- */
- protected $_logger;
-
- /**
- * the constructor
- *
- * @param mixed $requestBody
- * @param Syncroton_Model_Device $device
- * @param string $policyKey
- */
- public function __construct($requestBody, Syncroton_Model_IDevice $device, $policyKey)
- {
- if (!is_resource($requestBody)) {
- throw new Syncroton_Exception_UnexpectedValue('$requestBody must be stream');
- }
-
- $this->_inputStream = $requestBody;
-
- $this->_policyKey = $policyKey;
- $this->_device = $device;
- $this->_syncTimeStamp = new DateTime(null, new DateTimeZone('UTC'));
-
- if (Syncroton_Registry::isRegistered('loggerBackend')) {
- $this->_logger = Syncroton_Registry::get('loggerBackend');
- }
- }
+ protected $_saveInSent;
+ protected $_itemId;
+ protected $_collectionId;
+ protected $_mime;
/**
* process the XML file and add, change, delete or fetches data
*
* @return resource
*/
public function handle()
{
- $this->_saveInSent = isset($_GET['SaveInSent']) && (bool)$_GET['SaveInSent'] == 'T';
+ if ($this->_requestParameters['contentType'] == 'message/rfc822') {
+ $this->_mime = $this->_requestBody;
+ $this->_saveInSent = $this->_requestParameters['SaveInSent'] == 'T';
+
+ $this->_collectionId = $this->_requestParameters['CollectionId'];
+ $this->_itemId = $this->_requestParameters['ItemId'];
+
+ } else {
+ $xml = simplexml_import_dom($this->_requestBody);
+
+ $this->_mime = (string) $xml->Mime;
+ $this->_saveInSent = isset($xml->SaveinSentItems);
+
+ if (isset ($xml->Source)) {
+ $this->_collectionId = (string)$xml->Source->FolderId;
+ $this->_itemId = (string)$xml->Source->ItemId;
+ }
+ }
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " saveInSent: " . (int)$this->_saveInSent);
-
- /**
- * uncomment next lines to log email body
- *
- if ($this->_logger instanceof Zend_Log) {
- $debugStream = fopen("php://temp", 'r+');
- stream_copy_to_stream($this->_inputStream, $debugStream);
- rewind($debugStream);
- $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " email to send:" . stream_get_contents($debugStream));
-
- // replace original stream wirh debug stream, as php://input can't be rewinded
- $this->_inputStream = $debugStream;
- rewind($this->_inputStream);
- }
- */
}
/**
* this function generates the response for the client
*
* @return void
*/
public function getResponse()
{
$dataController = Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_EMAIL, $this->_device, $this->_syncTimeStamp);
- $dataController->sendEmail($this->_inputStream, $this->_saveInSent);
+ $dataController->sendEmail($this->_mime, $this->_saveInSent);
}
}
diff --git a/lib/ext/Syncroton/Command/Settings.php b/lib/ext/Syncroton/Command/Settings.php
index a6ead75..6d779e3 100644
--- a/lib/ext/Syncroton/Command/Settings.php
+++ b/lib/ext/Syncroton/Command/Settings.php
@@ -1,96 +1,96 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Settings command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_Settings extends Syncroton_Command_Wbxml
{
const STATUS_SUCCESS = 1;
const STATUS_PROTOCOL_ERROR = 2;
const STATUS_ACCESS_DENIED = 3;
const STATUS_SERVICE_UNAVAILABLE = 4;
const STATUS_INVALID_ARGUMENTS = 5;
const STATUS_CONFLICTING_ARGUMENTS = 6;
const STATUS_DEVICEPASSWORD_TO_LONG = 5;
const STATUS_DEVICEPASSWORD_PASSWORD_RECOVERY_DISABLED = 7;
protected $_defaultNameSpace = 'uri:Settings';
protected $_documentElement = 'Settings';
protected $_deviceInformationSet = false;
protected $_userInformationRequested = false;
/**
* process the XML file and add, change, delete or fetches data
*
*/
public function handle()
{
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
if(isset($xml->DeviceInformation->Set)) {
$this->_deviceInformationSet = true;
$this->_device->model = (string)$xml->DeviceInformation->Set->Model;
$this->_device->imei = (string)$xml->DeviceInformation->Set->IMEI;
$this->_device->friendlyname = (string)$xml->DeviceInformation->Set->FriendlyName;
$this->_device->os = (string)$xml->DeviceInformation->Set->OS;
$this->_device->oslanguage = (string)$xml->DeviceInformation->Set->OSLanguage;
$this->_device->phonenumber = (string)$xml->DeviceInformation->Set->PhoneNumber;
$this->_device = $this->_deviceBackend->update($this->_device);
}
if(isset($xml->UserInformation->Get)) {
$this->_userInformationRequested = true;
}
}
/**
* this function generates the response for the client
*
*/
public function getResponse()
{
$settings = $this->_outputDom->documentElement;
$settings->appendChild($this->_outputDom->createElementNS('uri:Settings', 'Status', self::STATUS_SUCCESS));
if($this->_deviceInformationSet === true) {
$deviceInformation = $settings->appendChild($this->_outputDom->createElementNS('uri:Settings', 'DeviceInformation'));
$set = $deviceInformation->appendChild($this->_outputDom->createElementNS('uri:Settings', 'Set'));
$set->appendChild($this->_outputDom->createElementNS('uri:Settings', 'Status', self::STATUS_SUCCESS));
}
if($this->_userInformationRequested === true) {
$smtpAddresses = array();
$userInformation = $settings->appendChild($this->_outputDom->createElementNS('uri:Settings', 'UserInformation'));
$userInformation->appendChild($this->_outputDom->createElementNS('uri:Settings', 'Status', self::STATUS_SUCCESS));
$get = $userInformation->appendChild($this->_outputDom->createElementNS('uri:Settings', 'Get'));
if(!empty($smtpAddresses)) {
$emailAddresses = $get->appendChild($this->_outputDom->createElementNS('uri:Settings', 'EmailAddresses'));
foreach($smtpAddresses as $smtpAddress) {
$emailAddresses->appendChild($this->_outputDom->createElementNS('uri:Settings', 'SMTPAddress', $smtpAddress));
}
}
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/SmartForward.php b/lib/ext/Syncroton/Command/SmartForward.php
index 1e15eae..3ee31e7 100644
--- a/lib/ext/Syncroton/Command/SmartForward.php
+++ b/lib/ext/Syncroton/Command/SmartForward.php
@@ -1,31 +1,33 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync SmartForward command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_SmartForward extends Syncroton_Command_SmartReply
{
+ protected $_defaultNameSpace = 'uri:ComposeMail';
+ protected $_documentElement = 'SmartForward';
/**
* this function generates the response for the client
*
* @return void
*/
public function getResponse()
{
$dataController = Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_EMAIL, $this->_device, $this->_syncTimeStamp);
- $dataController->forwardEmail($this->_collectionId, $this->_itemId, $this->_inputStream, $this->_saveInSent);
+ $dataController->forwardEmail($this->_collectionId, $this->_itemId, $this->_mime, $this->_saveInSent);
}
}
diff --git a/lib/ext/Syncroton/Command/SmartReply.php b/lib/ext/Syncroton/Command/SmartReply.php
index e033062..0f03341 100644
--- a/lib/ext/Syncroton/Command/SmartReply.php
+++ b/lib/ext/Syncroton/Command/SmartReply.php
@@ -1,54 +1,34 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync SmartReply command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_SmartReply extends Syncroton_Command_SendMail
{
- protected $_itemId;
-
- protected $_collectionId;
-
- /**
- * 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()
- {
- parent::handle();
-
- $this->_collectionId = $_GET['CollectionId'];
- $this->_itemId = $_GET['ItemId'];
-
- if ($this->_logger instanceof Zend_Log)
- $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " collectionId: " . $this->_collectionId . " itemId: " . $this->_itemId);
-
- }
+ protected $_defaultNameSpace = 'uri:ComposeMail';
+ protected $_documentElement = 'SmartReply';
/**
* this function generates the response for the client
*
* @return void
*/
public function getResponse()
{
$dataController = Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_EMAIL, $this->_device, $this->_syncTimeStamp);
- $dataController->replyEmail($this->_collectionId, $this->_itemId, $this->_inputStream, $this->_saveInSent);
+ $dataController->replyEmail($this->_collectionId, $this->_itemId, $this->_mime, $this->_saveInSent);
}
}
diff --git a/lib/ext/Syncroton/Command/Sync.php b/lib/ext/Syncroton/Command/Sync.php
index ec6a12a..4b5ebc5 100644
--- a/lib/ext/Syncroton/Command/Sync.php
+++ b/lib/ext/Syncroton/Command/Sync.php
@@ -1,763 +1,763 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Sync command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_Sync extends Syncroton_Command_Wbxml
{
const STATUS_SUCCESS = 1;
const STATUS_PROTOCOL_VERSION_MISMATCH = 2;
const STATUS_INVALID_SYNC_KEY = 3;
const STATUS_PROTOCOL_ERROR = 4;
const STATUS_SERVER_ERROR = 5;
const STATUS_ERROR_IN_CLIENT_SERVER_CONVERSION = 6;
const STATUS_CONFLICT_MATCHING_THE_CLIENT_AND_SERVER_OBJECT = 7;
const STATUS_OBJECT_NOT_FOUND = 8;
const STATUS_USER_ACCOUNT_MAYBE_OUT_OF_DISK_SPACE = 9;
const STATUS_ERROR_SETTING_NOTIFICATION_GUID = 10;
const STATUS_DEVICE_NOT_PROVISIONED_FOR_NOTIFICATIONS = 11;
const STATUS_FOLDER_HIERARCHY_HAS_CHANGED = 12;
const STATUS_RESEND_FULL_XML = 13;
const STATUS_WAIT_INTERVAL_OUT_OF_RANGE = 14;
const CONFLICT_OVERWRITE_SERVER = 0;
const CONFLICT_OVERWRITE_PIM = 1;
const MIMESUPPORT_DONT_SEND_MIME = 0;
const MIMESUPPORT_SMIME_ONLY = 1;
const MIMESUPPORT_SEND_MIME = 2;
const BODY_TYPE_PLAIN_TEXT = 1;
const BODY_TYPE_HTML = 2;
const BODY_TYPE_RTF = 3;
const BODY_TYPE_MIME = 4;
/**
* truncate types
*/
const TRUNCATE_ALL = 0;
const TRUNCATE_4096 = 1;
const TRUNCATE_5120 = 2;
const TRUNCATE_7168 = 3;
const TRUNCATE_10240 = 4;
const TRUNCATE_20480 = 5;
const TRUNCATE_51200 = 6;
const TRUNCATE_102400 = 7;
const TRUNCATE_NOTHING = 8;
/**
* filter types
*/
const FILTER_NOTHING = 0;
const FILTER_1_DAY_BACK = 1;
const FILTER_3_DAYS_BACK = 2;
const FILTER_1_WEEK_BACK = 3;
const FILTER_2_WEEKS_BACK = 4;
const FILTER_1_MONTH_BACK = 5;
const FILTER_3_MONTHS_BACK = 6;
const FILTER_6_MONTHS_BACK = 7;
const FILTER_INCOMPLETE = 8;
protected $_defaultNameSpace = 'uri:AirSync';
protected $_documentElement = 'Sync';
/**
* list of collections
*
* @var array
*/
protected $_collections = array();
protected $_modifications = array();
/**
* total count of items in all collections
*
* @var integer
*/
protected $_totalCount;
/**
* there are more entries than WindowSize available
* the MoreAvailable tag hot added to the xml output
*
* @var boolean
*/
protected $_moreAvailable = false;
/**
* @var Syncroton_Model_SyncState
*/
protected $_syncState;
/**
* process the XML file and add, change, delete or fetches data
*/
public function handle()
{
#if ($this->_statusProvisioning = $this->_checkProvisioningNeeded() !== false) {
# if (version_compare($this->_device->acsversion, '14.0', '<')) {
# throw new Syncroton_Exception_ProvisioningNeeded();
# } else {
# return;
# }
#}
// input xml
- $xml = simplexml_import_dom($this->_inputDom);
+ $xml = simplexml_import_dom($this->_requestBody);
foreach ($xml->Collections->Collection as $xmlCollection) {
$collectionData = new Syncroton_Model_SyncCollection($xmlCollection);
// got the folder synchronized to the device already
try {
$collectionData->folder = $this->_folderBackend->getFolder($this->_device, $collectionData->collectionId);
} catch (Syncroton_Exception_NotFound $senf) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__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(
'device_id' => $this->_device,
'folderid' => $collectionData->collectionId
));
}
$this->_collections[] = $collectionData;
continue;
}
if ($this->_logger instanceof Zend_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 Zend_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;
continue;
}
// check for invalid sycnkey
if(($collectionData->syncState = $this->_syncStateBackend->validate($this->_device, $collectionData->folder, $collectionData->syncKey)) === false) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__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;
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_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 Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($adds) . " entries to be added to server");
foreach ($adds as $add) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " add entry with clientId " . (string) $add->ClientId);
try {
if ($this->_logger instanceof Zend_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 Zend_Log)
$this->_logger->warn(__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 Zend_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 Zend_Log)
$this->_logger->warn(__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 Zend_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 Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . ' tried to delete entry ' . $serverId . ' but entry was not found');
} catch (Syncroton_Exception $e) {
if ($this->_logger instanceof Zend_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 Zend_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 Zend_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;
$this->_modifications[$collectionData->collectionId] = $clientModifications;
}
}
/**
* (non-PHPdoc)
* @see Syncroton_Command_Wbxml::getResponse()
*/
public function getResponse()
{
$sync = $this->_outputDom->documentElement;
// provioning needed?
#if ($this->_statusProvisioning !== false) {
# if ($this->_logger instanceof Zend_Log)
# $this->_logger->info(__METHOD__ . '::' . __LINE__ . " provisiong needed or remote wipe requested");
# $sync->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', $this->_statusProvisioning));
#
# return $this->_outputDom;
#}
$collections = $sync->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collections'));
foreach($this->_collections as $collectionData) {
// 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(),
);
$moreAvailable = false;
if($collectionData->getChanges === true) {
// continue sync session?
if(is_array($collectionData->syncState->pendingdata)) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " restored from sync state ");
$serverModifications = $collectionData->syncState->pendingdata;
} else {
// 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 Zend_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);
$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 Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped changed 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']);
}
if ($this->_logger instanceof Zend_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');
}
if (!empty($clientModifications['added']) || !empty($clientModifications['changed']) || !empty($clientModifications['deleted']) ||
!empty($serverModifications['added']) || !empty($serverModifications['changed']) || !empty($serverModifications['deleted'])) {
$collectionData->syncState->counter++;
}
// collection header
$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));
$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)) {
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($collectionData, $serverId)
->appendXML($applicationData);
$fetch->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_SUCCESS));
$fetch->appendChild($applicationData);
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage());
$fetch->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_OBJECT_NOT_FOUND));
}
}
}
if ($responses->hasChildNodes() === true) {
$collection->appendChild($responses);
}
if ((count($serverModifications['added']) + count($serverModifications['changed']) + count($serverModifications['deleted'])) > $collectionData->windowSize ) {
$moreAvailable = true;
$collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'MoreAvailable'));
}
$commands = $this->_outputDom->createElementNS('uri:AirSync', 'Commands');
/**
* process entries added on server side
*/
$newContentStates = array();
foreach($serverModifications['added'] as $id => $serverId) {
if($this->_totalCount === $collectionData->windowSize) {
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 Zend_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);
$commands->appendChild($add);
$this->_totalCount++;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage());
}
// mark as send 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
));
unset($serverModifications['added'][$id]);
}
/**
* process entries changed on server side
*/
foreach($serverModifications['changed'] as $id => $serverId) {
if($this->_totalCount === $collectionData->windowSize) {
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);
$commands->appendChild($change);
$this->_totalCount++;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage());
}
unset($serverModifications['changed'][$id]);
}
/**
* process entries deleted on server side
*/
$deletedContentStates = array();
foreach($serverModifications['deleted'] as $id => $serverId) {
if($this->_totalCount === $collectionData->windowSize) {
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);
$this->_totalCount++;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " unable to convert entry to xml: " . $e->getMessage());
}
unset($serverModifications['deleted'][$id]);
}
if ($commands->hasChildNodes() === true) {
$collection->appendChild($commands);
}
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " new synckey is ". $collectionData->syncState->counter);
}
if (isset($collectionData->syncState) && $collectionData->syncState instanceof Syncroton_Model_ISyncState &&
$collectionData->syncState->counter != $collectionData->syncKey) {
// increment sync timestamp by 1 second
$this->_syncTimeStamp->modify('+1 sec');
// store pending data in sync state when needed
if(isset($moreAvailable) && $moreAvailable === true) {
$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 Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " remove previous synckey as client added new entries");
$keepPreviousSyncKey = false;
} else {
$keepPreviousSyncKey = true;
}
$collectionData->syncState->lastsync = $this->_syncTimeStamp;
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
if (isset($newContentStates)) {
foreach($newContentStates as $state) {
$this->_contentStateBackend->create($state);
}
}
// remove contentstates for entries to be deleted on client
if (isset($deletedContentStates)) {
foreach($deletedContentStates as $state) {
$this->_contentStateBackend->delete($state);
}
}
Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId);
} catch (Zend_Db_Statement_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->getFolder($this->_device, $collectionData->collectionId);
$folderState->lastfiltertype = $collectionData->options['filterType'];
$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 Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . ' failed to get content state for: ' . $collectionData->collectionId);
}
}
}
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Command/Wbxml.php b/lib/ext/Syncroton/Command/Wbxml.php
index c500a25..0b69857 100644
--- a/lib/ext/Syncroton/Command/Wbxml.php
+++ b/lib/ext/Syncroton/Command/Wbxml.php
@@ -1,161 +1,167 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* abstract class for all commands using wbxml encoded content
*
* @package Syncroton
* @subpackage Command
*/
abstract class Syncroton_Command_Wbxml implements Syncroton_Command_ICommand
{
/**
* informations about the currently device
*
* @var Syncroton_Model_Device
*/
protected $_device;
/**
* informations about the currently device
*
* @var Syncroton_Backend_IDevice
*/
protected $_deviceBackend;
/**
* informations about the currently device
*
* @var Syncroton_Backend_IFolder
*/
protected $_folderBackend;
/**
* @var Syncroton_Backend_ISyncState
*/
protected $_syncStateBackend;
/**
* @var Syncroton_Backend_IContent
*/
protected $_contentStateBackend;
/**
* the domDocument containing the xml response from the server
*
* @var DOMDocument
*/
protected $_outputDom;
/**
* the domDocucment containing the xml request from the client
*
* @var DOMDocument
*/
- protected $_inputDom;
+ protected $_requestBody;
/**
* the default namespace
*
* @var string
*/
protected $_defaultNameSpace;
/**
* the main xml tag
*
* @var string
*/
protected $_documentElement;
/**
- * @var string
+ * @var array
*/
- protected $_policyKey;
+ protected $_requestParameters;
/**
* @var Syncroton_Model_SyncState
*/
protected $_syncState;
protected $_skipValidatePolicyKey = false;
/**
* timestamp to use for all sync requests
*
* @var DateTime
*/
protected $_syncTimeStamp;
/**
* @var string
*/
protected $_transactionId;
+ /**
+ * @var string
+ */
+ protected $_policyKey;
+
/**
* @var Zend_Log
*/
protected $_logger;
/**
* the constructor
*
- * @param mixed $_requestBody
- * @param Syncroton_Model_Device $_device
- * @param string $_policyKey
+ * @param mixed $requestBody
+ * @param Syncroton_Model_Device $device
+ * @param array $requestParameters
*/
- public function __construct($_requestBody, Syncroton_Model_IDevice $_device, $_policyKey)
+ public function __construct($requestBody, Syncroton_Model_IDevice $device, $requestParameters)
{
- $this->_policyKey = $_policyKey;
- $this->_device = $_device;
+ $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();
if (Syncroton_Registry::isRegistered('loggerBackend')) {
$this->_logger = Syncroton_Registry::get('loggerBackend');
}
if ($this->_skipValidatePolicyKey !== true && $this->_policyKey === null) {
#throw new Syncroton_Exception_PolicyKeyMissing();
}
if ($this->_skipValidatePolicyKey !== true && ($this->_policyKey === 0 || $this->_device->policykey != $this->_policyKey)) {
#throw new Syncroton_Exception_ProvisioningNeeded();
}
// should we wipe the mobile phone?
if ($this->_skipValidatePolicyKey !== true && !empty($this->_policyKey) && $this->_device->remotewipe >= Syncroton_Command_Provision::REMOTEWIPE_REQUESTED) {
throw new Syncroton_Exception_ProvisioningNeeded();
}
- $this->_inputDom = $_requestBody;
$this->_syncTimeStamp = new DateTime(null, new DateTimeZone('UTC'));
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " sync timestamp: " . $this->_syncTimeStamp->format('Y-m-d H:i:s'));
// 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->formatOutput = false;
$this->_outputDom->encoding = 'utf-8';
}
}
diff --git a/lib/ext/Syncroton/Model/Device.php b/lib/ext/Syncroton/Model/Device.php
index 4ff6c44..5e382c6 100644
--- a/lib/ext/Syncroton/Model/Device.php
+++ b/lib/ext/Syncroton/Model/Device.php
@@ -1,58 +1,59 @@
<?php
/**
* Syncroton
*
* @package Model
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Sync command
*
* @package Model
*/
class Syncroton_Model_Device implements Syncroton_Model_IDevice
{
const TYPE_IPHONE = 'iphone';
const TYPE_WEBOS = 'webos';
const TYPE_ANDROID = 'android';
+ const TYPE_ANDROID_40 = 'android40';
const TYPE_SMASUNGGALAXYS2 = 'samsunggti9100'; // Samsung Galaxy S-3
public function __construct(array $_data = array())
{
$this->setFromArray($_data);
}
public function setFromArray(array $_data)
{
foreach($_data as $key => $value) {
$this->$key = $value;
}
}
/**
* Returns major firmware version of this device
*
* @return int/string
*/
public function getMajorVersion()
{
switch ($this->devicetype) {
case Syncroton_Model_Device::TYPE_IPHONE:
if (preg_match('/(.+)\/(\d+)\.(\d+)/', $this->useragent, $matches)) {
list(, $name, $majorVersion, $minorVersion) = $matches;
return $majorVersion;
}
break;
default:
break;
}
return 0;
}
}
diff --git a/lib/ext/Syncroton/Server.php b/lib/ext/Syncroton/Server.php
index cb711c0..94fa4a9 100644
--- a/lib/ext/Syncroton/Server.php
+++ b/lib/ext/Syncroton/Server.php
@@ -1,326 +1,332 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle incoming http ActiveSync requests
*
* @package Syncroton
*/
class Syncroton_Server
{
protected $_body;
/**
* informations about the currently device
*
* @var Syncroton_Backend_IDevice
*/
protected $_deviceBackend;
/**
* @var Zend_Log
*/
protected $_logger;
/**
* @var Zend_Controller_Request_Http
*/
protected $_request;
protected $_userId;
public function __construct($userId, Zend_Controller_Request_Http $request = null, $body = null)
{
if (Syncroton_Registry::isRegistered('loggerBackend')) {
$this->_logger = Syncroton_Registry::get('loggerBackend');
}
$this->_userId = $userId;
$this->_request = $request instanceof Zend_Controller_Request_Http ? $request : new Zend_Controller_Request_Http();
$this->_body = $body !== null ? $body : fopen('php://input', 'r');
$this->_deviceBackend = Syncroton_Registry::getDeviceBackend();
}
public function handle()
{
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST METHOD: ' . $this->_request->getMethod());
switch($this->_request->getMethod()) {
case 'OPTIONS':
$this->_handleOptions();
break;
case 'POST':
$this->_handlePost();
break;
case 'GET':
echo "It works!<br>Your userid is: {$this->_userId} and your IP address is: {$_SERVER['REMOTE_ADDR']}.";
break;
}
}
/**
* handle options request
*
*/
protected function _handleOptions()
{
$command = new Syncroton_Command_Options();
$command->getResponse();
}
protected function _handlePost()
{
$requestParameters = $this->_getRequestParameters($this->_request);
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST ' . print_r($requestParameters, true));
$className = 'Syncroton_Command_' . $requestParameters['command'];
if(!class_exists($className)) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " command not supported: " . $requestParameters['command']);
header("HTTP/1.1 501 not implemented");
return;
}
// get user device
$device = $this->_getUserDevice($this->_userId, $requestParameters);
if ($requestParameters['contentType'] == 'application/vnd.ms-sync.wbxml') {
// decode wbxml request
try {
$decoder = new Syncroton_Wbxml_Decoder($this->_body);
$requestBody = $decoder->decode();
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request: " . $requestBody->saveXML());
} catch(Syncroton_Wbxml_Exception_UnexpectedEndOfFile $e) {
$requestBody = NULL;
}
} else {
$requestBody = $this->_body;
}
if (PHP_SAPI !== 'cli') {
header("MS-Server-ActiveSync: 14.00.0536.000");
}
try {
- $command = new $className($requestBody, $device, $requestParameters['policyKey']);
+ $command = new $className($requestBody, $device, $requestParameters);
$command->handle();
$response = $command->getResponse();
} catch (Syncroton_Exception_PolicyKeyMissing $sepkm) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " X-MS-POLICYKEY missing (" . $_command. ')');
header("HTTP/1.1 400 header X-MS-POLICYKEY not found");
} catch (Syncroton_Exception_ProvisioningNeeded $sepn) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " provisioning needed");
header("HTTP/1.1 449 Retry after sending a PROVISION command");
return;
} catch (Exception $e) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " unexpected exception occured: " . get_class($e));
if ($this->_logger instanceof Zend_Log)
$this->_logger->crit(__METHOD__ . '::' . __LINE__ . " exception message: " . $e->getMessage());
if ($this->_logger instanceof Zend_Log)
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " " . $e->getTraceAsString());
header("HTTP/1.1 500 Internal server error");
return;
}
if ($response instanceof DOMDocument) {
if ($this->_logger instanceof Zend_Log)
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml response: " . $response->saveXML());
$outputStream = fopen("php://temp", 'r+');
$encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
$encoder->encode($response);
// avoid sending headers while running on cli (phpunit)
if (PHP_SAPI !== 'cli') {
header("Content-Type: application/vnd.ms-sync.wbxml");
}
rewind($outputStream);
fpassthru($outputStream);
}
}
/**
* return request params
*
* @return array
*/
protected function _getRequestParameters(Zend_Controller_Request_Http $request)
{
if(count($_GET) == 1) {
$arrayKeys = array_keys($_GET);
$base64Decoded = base64_decode($arrayKeys[0]);
$stream = fopen("php://temp", 'r+');
fwrite($stream, base64_decode($arrayKeys[0]));
rewind($stream);
#fpassthru($stream);rewind($stream);
$protocolVersion = ord(fread($stream, 1));
switch (ord(fread($stream, 1))) {
case 0:
$command = 'Sync';
break;
case 1:
$command = 'SendMail';
break;
case 2:
$command = 'SmartForward';
break;
case 3:
$command = 'SmartReply';
break;
case 4:
$command = 'GetAttachment';
break;
case 9:
$command = 'FolderSync';
break;
case 10:
$command = 'FolderCreate';
break;
case 11:
$command = 'FolderDelete';
break;
case 12:
$command = 'FolderUpdate';
break;
case 13:
$command = 'MoveItems';
break;
case 14:
$command = 'GetItemEstimate';
break;
case 15:
$command = 'MeetingResponse';
break;
case 16:
$command = 'Search';
break;
case 17:
$command = 'Settings';
break;
case 18:
$command = 'Ping';
break;
case 19:
$command = 'ItemOperations';
break;
case 20:
$command = 'Provision';
break;
case 21:
$command = 'ResolveRecipients';
break;
case 22:
$command = 'ValidateCert';
break;
}
$locale = fread($stream, 2);
$deviceIdLength = ord(fread($stream, 1));
if ($deviceIdLength > 0) {
$deviceId = fread($stream, $deviceIdLength);
}
$policyKeyLength = ord(fread($stream, 1));
if ($policyKeyLength > 0) {
$policyKey = fread($stream, 4);
}
$deviceTypeLength = ord(fread($stream, 1));
$deviceType = fread($stream, $deviceTypeLength);
// @todo parse command parameters
$result = array(
'protocolVersion' => $protocolVersion,
'command' => $command,
'deviceId' => $deviceId,
- 'deviceType' => $deviceType
+ 'deviceType' => $deviceType,
+ 'saveInSent' => null,
+ 'collectionId' => null,
+ 'itemId' => null
);
} else {
$result = array(
- 'protocolVersion' => $request->getServer('HTTP_MS_ASPROTOCOLVERSION'),
- 'command' => $request->getQuery('Cmd'),
- 'deviceId' => $request->getQuery('DeviceId'),
- 'deviceType' => $request->getQuery('DeviceType')
+ 'protocolVersion' => $request->getServer('HTTP_MS_ASPROTOCOLVERSION'),
+ 'command' => $request->getQuery('Cmd'),
+ 'deviceId' => $request->getQuery('DeviceId'),
+ 'deviceType' => $request->getQuery('DeviceType'),
+ 'saveInSent' => $request->getQuery('SaveInSent'),
+ 'collectionId' => $request->getQuery('CollectionId'),
+ 'itemId' => $request->getQuery('ItemId')
);
}
$result['userAgent'] = $request->getServer('HTTP_USER_AGENT', $result['deviceType']);
$result['policyKey'] = $request->getServer('HTTP_X_MS_POLICYKEY');
$result['contentType'] = $request->getServer('CONTENT_TYPE');
return $result;
}
/**
* 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'];
$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']
)));
}
return $device;
}
}
\ No newline at end of file
diff --git a/lib/ext/Syncroton/Wbxml/Abstract.php b/lib/ext/Syncroton/Wbxml/Abstract.php
index 2f260d4..e1d0c36 100644
--- a/lib/ext/Syncroton/Wbxml/Abstract.php
+++ b/lib/ext/Syncroton/Wbxml/Abstract.php
@@ -1,210 +1,210 @@
<?php
/**
* Syncroton
*
* @package Wbxml
* @subpackage Wbxml
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2009 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
* @version $Id:Abstract.php 4968 2008-10-17 09:09:33Z l.kneschke@metaways.de $
*/
/**
* class documentation
*
* @package Wbxml
* @subpackage Wbxml
*/
abstract class Syncroton_Wbxml_Abstract
{
/**
* stream containing the wbxml encoded data
*
* @var resource
*/
protected $_stream;
/**
* the wbxml version
*
* @var string
*/
protected $_version;
/**
* the Document Public Identifier
*
* @var string
*/
protected $_dpi;
/**
* the current active dtd
*
* @var Syncroton_Wbxml_Dtd_Syncml_Abstract
*/
protected $_dtd;
/**
* the charSet used in the wbxml file
*
* @var string
*/
protected $_charSet;
/**
* currently active code page
*
* @var array
*/
protected $_codePage;
/**
* see section 5.5
*
*/
const DPI_WELLKNOWN = 'WELLKNOWN';
/**
* see section 5.5
*
*/
const DPI_STRINGTABLE = 'STRINGTABLE';
const SWITCH_PAGE = 0x00;
const END = 0x01;
const ENTITY = 0x02;
const STR_I = 0x03;
const LITERAL = 0x04;
const EXT_I_0 = 0x40;
const EXT_I_1 = 0x41;
const EXT_I_2 = 0x42;
const PI = 0x43;
const LITERAL_C = 0x44;
const EXT_T_0 = 0x80;
const EXT_T_1 = 0x81;
const EXT_T_2 = 0x82;
const STR_T = 0x83;
const LITERAL_A = 0x84;
const EXT_0 = 0xC0;
const EXT_1 = 0xC1;
const EXT_2 = 0xC2;
const OPAQUE = 0xC3;
const LITERAL_AC = 0xC4;
/**
* the real name for this DPI is "unknown"
* But Microsoft is using them for their ActiveSync stuff
* instead defining their own DPI like the sycnml creators did
*
*/
const DPI_1 = '-//AIRSYNC//DTD AirSync//EN';
/**
* return wellknown identifiers
*
* @param integer $_uInt
* @todo add well known identifiers from section 7.2
* @return string
*/
public function getDPI($_uInt = 0)
{
- if(($dpi = constant('Syncroton_Wbxml_Abstract::DPI_' . $_uInt)) === NULL) {
+ if(($dpi = @constant('Syncroton_Wbxml_Abstract::DPI_' . $_uInt)) === NULL) {
throw new Syncroton_Wbxml_Exception('unknown wellknown identifier: ' . $_uInt);
}
return $dpi;
}
/**
* return multibyte integer
*
* @return integer
*/
protected function _getMultibyteUInt()
{
$uInt = 0;
do {
$byte = $this->_getByte();
$uInt <<= 7;
$uInt += ($byte & 127);
} while (($byte & 128) != 0);
return $uInt;
}
protected function _getByte()
{
$byte = fread($this->_stream, 1);
if($byte === false) {
throw new Syncroton_Wbxml_Exception("failed reading one byte");
}
return ord($byte);
}
protected function _getOpaque($_length)
{
$string = fread($this->_stream, $_length);
if($string === false) {
throw new Syncroton_Wbxml_Exception("failed reading opaque data");
}
return $string;
}
/**
* get a 0 terminated string
*
* @return string
*/
protected function _getTerminatedString()
{
$string = '';
while (($byte = $this->_getByte()) != 0) {
$string .= chr($byte);
}
return $string;
}
protected function _writeByte($_byte)
{
fwrite($this->_stream, chr($_byte));
}
protected function _writeMultibyteUInt($_integer)
{
$multibyte = NULL;
$remainder = $_integer;
do {
$byte = ($remainder & 127);
$remainder >>= 7;
if($multibyte === NULL) {
$multibyte = chr($byte);
} else {
$multibyte = chr($byte | 128) . $multibyte;
}
} while ($remainder != 0);
fwrite($this->_stream, $multibyte);
}
protected function _writeString($_string)
{
fwrite($this->_stream, $_string);
}
protected function _writeTerminatedString($_string)
{
fwrite($this->_stream, $_string);
fwrite($this->_stream, chr(0));
}
}
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Sun, Apr 19, 1:21 PM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
435773
Default Alt Text
(142 KB)

Event Timeline