Commit 6797670b authored by Sebastian Kummer's avatar Sebastian Kummer

Merge branch 'develop' into bugfix/ZP-1085-update-trademark

parents 1961828e b8be1f7a
......@@ -8,7 +8,7 @@ Homepage: http://z-push.org
Package: z-push-common
Architecture: all
Depends: ${misc:Depends}, php5 (>= 5.4) | php (>= 5.4) , php5-cli | php-cli, php-soap
Depends: ${misc:Depends}, php5 (>= 5.4) | php (>= 5.4) , php5-cli | php-cli, php-soap, ${dist:Depends}
Conflicts: d-push, z-push
Replaces: d-push, z-push
Description: open source implementation of the ActiveSync protocol
......
......@@ -4,6 +4,12 @@ export DH_VERBOSE=1
#include /usr/share/quilt/quilt.make
# Since only Ubuntu 16.04 requires php-mbstring we detect that here
ifeq ($(shell dpkg-vendor --derives-from Ubuntu && echo yes),yes)
ifeq ($(shell grep -q Xenial /etc/os-release && echo yes),yes)
SUBSTVARS = -Vdist:Depends="php-mbstring"
endif
endif
DEB_VERSION := $(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d ' ')
......@@ -11,6 +17,9 @@ DEB_VERSION := $(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d ' '
%:
dh $@
override_dh_gencontrol:
dh_gencontrol -- $(SUBSTVARS)
override_dh_install:
mv src/* .
sed -s "s/ZPUSHVERSION/${DEB_VERSION}/" build/version.php.in > version.php
......
......@@ -66,7 +66,7 @@ class SqlStateMachine implements IStateMachine {
throw new FatalMisconfigurationException("SqlStateMachine(): missing configuration for the state sql. Check STATE_SQL_* values in the configuration.");
}
if (!preg_match('/[0-9a-zA-Z$_]+/', STATE_SQL_DATABASE)) {
if (!preg_match('/^[0-9a-zA-Z$_]+$/', STATE_SQL_DATABASE)) {
throw new FatalMisconfigurationException(sprintf("SqlStateMachine(): invalid database name '%s'. The name may contain ASCII 7bit letters, numbers and the '$' and '_' signs. Please change your configuration.", STATE_SQL_DATABASE));
}
......
......@@ -837,11 +837,13 @@ class ASDevice extends StateObject {
* @param string $name the name of the additional folder (has to be unique for all folders on the device).
* @param string $type AS foldertype of SYNC_FOLDER_TYPE_USER_*
* @param int $flags Additional flags, like DeviceManager::FLD_FLAGS_REPLYASUSER
* @param string $parentid the parentid of this folder.
* @param boolean $checkDups indicates if duplicate names and ids should be verified. Default: true
*
* @access public
* @return boolean
*/
public function AddAdditionalFolder($store, $folderid, $name, $type, $flags) {
public function AddAdditionalFolder($store, $folderid, $name, $type, $flags, $parentid = 0, $checkDups = true) {
// check if a folderid and name were sent
if (!$folderid || !$name) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->AddAdditionalFolder(): No valid folderid ('%s') or name ('%s') sent. Aborting. ", $folderid, $name));
......@@ -860,15 +862,29 @@ class ASDevice extends StateObject {
return false;
}
// check if a folder with that Name is already in the list
// check if a folder with that Name is already in the list and that its parent exists
$parentFound = false;
foreach ($this->additionalfolders as $k => $folder) {
if ($folder['name'] == $name) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->AddAdditionalFolder(): folder can not be added because there is already an additional folder with the same name: '%s'", $name));
// TODO: this parentid check should go into fixstates!
if (!isset($folder['parentid'])) $folder['parentid'] = "0";
if ($folder['name'] == $name && $folder['parentid'] == $parentid) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->AddAdditionalFolder(): folder can not be added because there is already an additional folder with the same name in the same folder: '%s'", $name));
return false;
}
if ($folder['folderid'] == $parentid) {
$parentFound = true;
}
}
if ($parentid != '0' && !$parentFound) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->AddAdditionalFolder(): folder '%s' ('%s') can not be added because the parent folder '%s' can not be found'", $name, $folderid, $parentid));
return false;
}
// check if a folder with this ID or Name is already known on the device (regular folder)
if ($checkDups) {
// in order to check for the parent-ids we need a shortid
$parentShortId = $this->GetFolderIdForBackendId($parentid, false, null, null);
foreach($this->GetHierarchyCache()->ExportFolders() as $syncedFolderid => $folder) {
if ($syncedFolderid === $folderid || $folder->BackendId === $folderid) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->AddAdditionalFolder(): folder can not be added because there is already a folder with the same folder id synchronized: '%s'", $folderid));
......@@ -876,17 +892,19 @@ class ASDevice extends StateObject {
}
// $folder is a SyncFolder object here
if ($folder->displayname == $name) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->AddAdditionalFolder(): folder can not be added because there is already a folder with the same name synchronized: '%s'", $name));
if ($folder->displayname == $name && ($folder->parentid == $parentid || $folder->parentid == $parentShortId)) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->AddAdditionalFolder(): folder can not be added because there is already a folder with the same name synchronized in the same parent: '%s'", $name));
return false;
}
}
}
// add the folder
$af = $this->additionalfolders;
$af[$folderid] = array(
'store' => $store,
'folderid' => $folderid,
'parentid' => $parentid,
'name' => $name,
'type' => $type,
'flags' => $flags,
......@@ -973,6 +991,108 @@ class ASDevice extends StateObject {
return true;
}
/**
* Sets a list of additional folders of one store to the device.
* If there are additional folders for the set_store, that are not in the list they will be removed.
*
* @param string $store the store where this folder is located, e.g. "SYSTEM" (for public folder) or an username/email address.
* @param array $folders a list of folders to be set for this user. Other existing additional folders (that are not in this list)
* will be removed. The list is an array containing folders, where each folder is an array with the following keys:
* 'folderid' (string) the folder id of the additional folder.
* 'parentid' (string) the folderid of the parent folder. If no parent folder is set or the parent folder is not defined, '0' (main folder) is used.
* 'name' (string) the name of the additional folder (has to be unique for all folders on the device).
* 'type' (string) AS foldertype of SYNC_FOLDER_TYPE_USER_*
* 'flags' (int) Additional flags, like DeviceManager::FLD_FLAGS_REPLYASUSER
*
* @access public
* @return boolean
*/
public function SetAdditionalFolderList($store, $folders) {
// remove all folders already shared for this store
$newAF = array();
$noDupsCheck = array();
foreach($this->additionalfolders as $keepFolder) {
if ($keepFolder['store'] !== $store) {
$newAF[$keepFolder['folderid']] = $keepFolder;
}
else {
$noDupsCheck[$keepFolder['folderid']] = true;
}
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ASDevice->SetAdditionalFolderList(): cleared additional folder lists of store '%s', total %d folders, kept %d and removed %d", $store, count($this->additionalfolders), count($newAF), count($noDupsCheck)));
// set remaining additional folders
$this->additionalfolders = $newAF;
// transform our array in a key/value array where folderids are keys and do some basic checks
$toOrder = array();
$ordered = array();
$validTypes = array(SYNC_FOLDER_TYPE_USER_CONTACT, SYNC_FOLDER_TYPE_USER_APPOINTMENT, SYNC_FOLDER_TYPE_USER_TASK, SYNC_FOLDER_TYPE_USER_MAIL, SYNC_FOLDER_TYPE_USER_NOTE, SYNC_FOLDER_TYPE_USER_JOURNAL);
foreach($folders as $f) {
// fail early
if (!$f['folderid'] || !$f['name']) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->SetAdditionalFolderList(): No valid folderid ('%s') or name ('%s') sent. Aborting. ", $f['folderid'], $f['name']));
return false;
}
// check if type is of a additional user type
if (!in_array($f['type'], $validTypes)) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ASDevice->SetAdditionalFolderList(): folder (id: '%s' - name: '%s') can not be added because the specified type '%s' is not a permitted user type.", $f['folderid'], $f['name'], $f['type']));
return false;
}
$toOrder[$f['folderid']] = $f;
}
// order the array, so folders with leafs come first
$this->orderAdditionalFoldersHierarchically($toOrder, $ordered);
// if there are folders that are not be positioned in the tree, we can't add them!
if (!empty($toOrder)) {
$s = "";
foreach($toOrder as $f) {
$s .= sprintf("'%s'('%s') ", $f['name'], $f['folderid']);
}
ZLog::Write(LOGLEVEL_ERROR, "ASDevice->SetAdditionalFolderList(): cannot proceed as these folders have invalid parentids (not found): ". $s);
return false;
}
foreach($ordered as $f) {
$status = $this->AddAdditionalFolder($store, $f['folderid'], $f['name'], $f['type'], $f['flags'], $f['parentid'], !isset($noDupsCheck[$f['folderid']]));
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ASDevice->SetAdditionalFolderList(): set folder '%s' in additional folders list with status: %s", $f['name'], Utils::PrintAsString($status)));
// break if a folder can not be added
if (!$status) {
return false;
}
}
return true;
}
/**
* Orders a list of folders so the parents are first in the array, all leaves come afterwards.
*
* @param array $toOrderFolders an array of folders, where the folderids are keys. This array should be empty at the end.
* @param array $orderedFolders the ordered array
* @param string $parentid the parentid to start with, if not set '0' (main folders) is used.
*/
private function orderAdditionalFoldersHierarchically(&$toOrderFolders, &$orderedFolders, $parentid = '0') {
$stepInto = array();
// loop through the remaining folders that need to be ordered
foreach($toOrderFolders as $folder) {
// move folders with the matching parentid to the ordered array
if ($folder['parentid'] == $parentid) {
echo "found.. \n";
$fid = $folder['folderid'];
$orderedFolders[$fid] = $folder;
unset($toOrderFolders[$fid]);
$stepInto[] = $fid;
}
}
// call recursively to move/order the leaves as well
foreach($stepInto as $fid) {
$this->orderAdditionalFoldersHierarchically($toOrderFolders, $orderedFolders, $fid);
}
}
/**
* Generates the AS folder hash from the backend folder id, type and name.
*
......@@ -993,7 +1113,7 @@ class ASDevice extends StateObject {
$cnt = 0;
// Collision avoiding. Append an increasing number to the string to hash
// until there aren't any collisions. Probably a smaller number is also sufficient.
while (isset($this->contentData[$folderId]) && $cnt < 10000) {
while ((isset($this->contentData[$folderId]) || in_array($folderId, $this->backend2folderidCache, true)) && $cnt < 10000) {
$folderId = substr($folderOrigin . dechex(crc32($backendid . $folderName . $cnt++)), 0, 6);
ZLog::Write(LOGLEVEL_WARN, sprintf("ASDevice->generateFolderHash(): collision avoiding nr %05d. Generated hash: '%s'", $cnt, $folderId));
}
......
......@@ -233,7 +233,6 @@ class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IEx
// The Zarafa/Kopano HierarchyExporter exports all kinds of changes for folders (e.g. update no. of unread messages in a folder).
// These changes are not relevant for the mobiles, as something changes but the relevant displayname and parentid
// stay the same. These changes will be dropped and are not sent!
$cacheFolder = $this->GetFolder($folder->serverid);
if ($folder->equals($this->GetFolder($folder->serverid))) {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Change for folder '%s' will not be sent as modification is not relevant.", $folder->displayname));
return false;
......
......@@ -481,8 +481,12 @@ class DeviceManager {
$df['flags'] = 0;
ZLog::Write(LOGLEVEL_WARN, sprintf("DeviceManager->GetAdditionalUserSyncFolders(): Additional folder '%s' has no flags. Please run 'z-push-admin -a fixstates' to fix this issue.", $df['name']));
}
if (!isset($df['parentid'])) {
$df['parentid'] = '0';
ZLog::Write(LOGLEVEL_WARN, sprintf("DeviceManager->GetAdditionalUserSyncFolders(): Additional folder '%s' has no parentid. // TODO FIX: Please run 'z-push-admin -a fixstates' to fix this issue.", $df['name']));
}
$folder = $this->getAdditionalSyncFolderObject($df['store'], $df['folderid'], $df['name'], $df['type'], $df['flags'], DeviceManager::FLD_ORIGIN_SHARED);
$folder = $this->getAdditionalSyncFolderObject($df['store'], $df['folderid'], $df['parentid'], $df['name'], $df['type'], $df['flags'], DeviceManager::FLD_ORIGIN_SHARED);
$folders[$folder->BackendId] = $folder;
}
......@@ -490,7 +494,7 @@ class DeviceManager {
if (KOE_CAPABILITY_GAB && $this->IsKoe() && KOE_GAB_STORE != "" && KOE_GAB_NAME != "") {
// if KOE_GAB_FOLDERID is set, use it
if (KOE_GAB_FOLDERID != "") {
$folder = $this->getAdditionalSyncFolderObject(KOE_GAB_STORE, KOE_GAB_FOLDERID, KOE_GAB_NAME, SYNC_FOLDER_TYPE_USER_APPOINTMENT, 0, DeviceManager::FLD_ORIGIN_GAB);
$folder = $this->getAdditionalSyncFolderObject(KOE_GAB_STORE, KOE_GAB_FOLDERID, '0', KOE_GAB_NAME, SYNC_FOLDER_TYPE_USER_APPOINTMENT, 0, DeviceManager::FLD_ORIGIN_GAB);
$folders[$folder->BackendId] = $folder;
}
else {
......@@ -507,7 +511,7 @@ class DeviceManager {
}
if ($backendGabId) {
$folders[$backendGabId] = $this->getAdditionalSyncFolderObject(KOE_GAB_STORE, $backendGabId, KOE_GAB_NAME, SYNC_FOLDER_TYPE_USER_APPOINTMENT, 0, DeviceManager::FLD_ORIGIN_GAB);
$folders[$backendGabId] = $this->getAdditionalSyncFolderObject(KOE_GAB_STORE, $backendGabId, '0', KOE_GAB_NAME, SYNC_FOLDER_TYPE_USER_APPOINTMENT, 0, DeviceManager::FLD_ORIGIN_GAB);
}
}
}
......@@ -1173,11 +1177,11 @@ class DeviceManager {
* @access private
* @returns SyncFolder
*/
private function getAdditionalSyncFolderObject($store, $folderid, $name, $type, $flags, $folderOrigin) {
private function getAdditionalSyncFolderObject($store, $folderid, $parentid, $name, $type, $flags, $folderOrigin) {
$folder = new SyncFolder();
$folder->BackendId = $folderid;
$folder->serverid = $this->GetFolderIdForBackendId($folder->BackendId, true, $folderOrigin, $name);
$folder->parentid = 0; // only top folders are supported
$folder->parentid = $this->GetFolderIdForBackendId($parentid);
$folder->displayname = $name;
$folder->type = $type;
// save store as custom property which is not streamed directly to the device
......
......@@ -129,7 +129,7 @@ abstract class SyncObject extends Streamer {
}
else {
if (isset($this->$val) && isset($odo->$val)) {
if ($this->$val != $odo->$val){
if ($this->$val !== $odo->$val){
ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() false on field '%s': '%s' != '%s'", $val, Utils::PrintAsString($this->$val), Utils::PrintAsString($odo->$val)));
return false;
}
......
......@@ -482,7 +482,7 @@ class ZPushAdmin {
foreach ($device->GetAdditionalFolders() as $folder) {
$syncfolderid = $device->GetFolderIdForBackendId($folder['folderid'], false, false, null);
$folder['syncfolderid'] = $syncfolderid;
$folder['origin'] = Utils::GetFolderOriginStringFromId($syncfolderid);
$folder['origin'] = ($syncfolderid !== $folder['folderid'])?Utils::GetFolderOriginStringFromId($syncfolderid):'unknown';
$new_list[$folder['folderid']] = $folder;
}
foreach (ZPush::GetAdditionalSyncFolders() as $fid => $so) {
......@@ -495,7 +495,7 @@ class ZPushAdmin {
'syncfolderid' => $syncfolderid,
'name' => $so->displayname,
'type' => $so->type,
'origin' => Utils::GetFolderOriginStringFromId($syncfolderid),
'origin' => ($syncfolderid !== $fid)?Utils::GetFolderOriginStringFromId($syncfolderid):'unknown',
'flags' => 0, // static folders have no flags
);
}
......@@ -645,6 +645,55 @@ class ZPushAdmin {
return false;
}
/**
* Sets a list of additional folders of one store to the given device and user.
* If there are additional folders for this store, that are not in the list they will be removed.
*
* @param string $user user of the device.
* @param string $devid device id of where the folder should be set.
* @param string $set_store the store where this folder is located, e.g. "SYSTEM" (for public folder) or an username/email address.
* @param array $set_folders a list of folders to be set for this user. Other existing additional folders (that are not in this list)
* will be removed. The list is an array containing folders, where each folder is an array with the following keys:
* 'folderid' (string) the folder id of the additional folder.
* 'parentid' (string) the folderid of the parent folder. If no parent folder is set or the parent folder is not defined, '0' (main folder) is used.
* 'name' (string) the name of the additional folder (has to be unique for all folders on the device).
* 'type' (string) AS foldertype of SYNC_FOLDER_TYPE_USER_*
* 'flags' (int) Additional flags, like DeviceManager::FLD_FLAGS_REPLYASUSER
*
* @access public
* @return boolean
*/
static public function AdditionalFolderSetList($user, $devid, $set_store, $set_folders) {
// load device data
$device = new ASDevice($devid, ASDevice::UNDEFINED, $user, ASDevice::UNDEFINED);
try {
// set device data
$device->SetData(ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA), false);
// get the last hierarchy counter
$spa = ZPush::GetStateMachine()->GetState($devid, IStateMachine::FOLDERDATA, $device->GetFolderUUID());
list($uuid, $counter) = StateManager::ParseStateKey($spa->GetSyncKey());
// instantiate hierarchycache
$device->SetHierarchyCache(ZPush::GetStateMachine()->GetState($devid, IStateMachine::HIERARCHY, $device->GetFolderUUID(), $counter, false));
if ($device->IsNewDevice()) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::AdditionalFolderSetList(): data of user '%s' not synchronized on device '%s'. Aborting.", $user, $devid));
return false;
}
$status = $device->SetAdditionalFolderList($set_store, $set_folders);
if ($status && $device->GetData() !== false) {
ZPush::GetStateMachine()->SetState($device->GetData(), $devid, IStateMachine::DEVICEDATA);
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::AdditionalFolderSetList(): added '%s' folders of '%s' to additional folders list of device '%s' of user '%s' with status: %s", count($set_folders), $set_store, $devid, $user, Utils::PrintAsString($status)));
return $status;
}
catch (StateNotFoundException $e) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::AdditionalFolderSetList(): state for device '%s' of user '%s' can not be found or saved", $devid, $user));
return false;
}
return false;
}
/**
* Clears loop detection data
*
......
......@@ -247,4 +247,47 @@ class WebserviceDevice {
return $status;
}
/**
* Sets a list of additional folders of one store to the given device and the Request::GetGETUser().
* If there are additional folders for this store, that are not in the list they will be removed.
*
* @param string $deviceId device id the folder should be added to.
* @param string $set_store the store where this folder is located, e.g. "SYSTEM" (for public folder) or an username/email address.
* @param array $set_folders a list of folders to be set for this user. Other existing additional folders (that are not in this list)
* will be removed. The list is an array containing folders, where each folder is an array with the following keys:
* 'folderid' (string) the folder id of the additional folder.
* 'parentid' (string) the folderid of the parent folder. If no parent folder is set or the parent folder is not defined, '0' (main folder) is used.
* 'name' (string) the name of the additional folder (has to be unique for all folders on the device).
* 'type' (string) AS foldertype of SYNC_FOLDER_TYPE_USER_*
* 'flags' (int) Additional flags, like DeviceManager::FLD_FLAGS_REPLYASUSER
*
* @access public
* @return boolean
*/
public function AdditionalFolderSetList($deviceId, $set_store, $set_folders) {
$user = Request::GetGETUser();
$deviceId = preg_replace("/[^A-Za-z0-9]/", "", $deviceId);
array_walk($set_folders, function(&$folder) {
if (!isset($folder['folderid'])) $folder['folderid'] = "";
if (!isset($folder['parentid'])) $folder['parentid'] = "0";
if (!isset($folder['type'])) $folder['type'] = SYNC_FOLDER_TYPE_USER_MAIL;
if (!isset($folder['flags'])) $folder['flags'] = 0;
$folder['folderid'] = preg_replace("/[^A-Za-z0-9]/", "", $folder['folderid']);
$folder['parentid'] = preg_replace("/[^A-Za-z0-9]/", "", $folder['parentid']);
$folder['type'] = preg_replace("/[^0-9]/", "", $folder['type']);
$folder['flags'] = preg_replace("/[^0-9]/", "", $folder['flags']);
});
$status = ZPushAdmin::AdditionalFolderSetList($user, $deviceId, $set_store, $set_folders);
if (!$status) {
ZPush::GetTopCollector()->AnnounceInformation(ZLog::GetLastMessage(LOGLEVEL_ERROR), true);
throw new SoapFault("ERROR", ZLog::GetLastMessage(LOGLEVEL_ERROR));
}
ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::AdditionalFolderSetList(): set '%d' folders for device '%s' of user '%s': %s", count($set_folders), $deviceId, $user, Utils::PrintAsString($status)));
ZPush::GetTopCollector()->AnnounceInformation("Set additional folders", true);
return $status;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment