1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/z-push_ynh.git synced 2024-09-03 18:05:58 +02:00
z-push_ynh/sources/lib/core/asdevice.php
2014-12-17 15:40:48 +00:00

690 lines
No EOL
26 KiB
PHP

<?php
/***********************************************
* File : asdevice.php
* Project : Z-Push
* Descr : The ASDevice holds basic data about a device,
* its users and the linked states
*
* Created : 11.04.2011
*
* Copyright 2007 - 2013 Zarafa Deutschland GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation with the following additional
* term according to sec. 7:
*
* According to sec. 7 of the GNU Affero General Public License, version 3,
* the terms of the AGPL are supplemented with the following terms:
*
* "Zarafa" is a registered trademark of Zarafa B.V.
* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
* The licensing of the Program under the AGPL does not imply a trademark license.
* Therefore any rights, title and interest in our trademarks remain entirely with us.
*
* However, if you propagate an unmodified version of the Program you are
* allowed to use the term "Z-Push" to indicate that you distribute the Program.
* Furthermore you may use our trademarks where it is necessary to indicate
* the intended purpose of a product or service provided you use it in accordance
* with honest practices in industrial or commercial matters.
* If you want to propagate modified versions of the Program under the name "Z-Push",
* you may only do so if you have a written permission by Zarafa Deutschland GmbH
* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
class ASDevice extends StateObject {
const UNDEFINED = -1;
// content data
const FOLDERUUID = 1;
const FOLDERTYPE = 2;
const FOLDERSUPPORTEDFIELDS = 3;
const FOLDERSYNCSTATUS = 4;
// expected values for not set member variables
protected $unsetdata = array(
'useragenthistory' => array(),
'hierarchyuuid' => false,
'contentdata' => array(),
'wipestatus' => SYNC_PROVISION_RWSTATUS_NA,
'wiperequestedby' => false,
'wiperequestedon' => false,
'wipeactionon' => false,
'lastupdatetime' => 0,
'conversationmode' => false,
'policies' => array(),
'policykey' => self::UNDEFINED,
'forcesave' => false,
'asversion' => false,
'ignoredmessages' => array(),
'announcedASversion' => false,
);
static private $loadedData;
protected $newdevice;
protected $hierarchyCache;
protected $ignoredMessageIds;
/**
* AS Device constructor
*
* @param string $devid
* @param string $devicetype
* @param string $getuser
* @param string $useragent
*
* @access public
* @return
*/
public function ASDevice($devid, $devicetype, $getuser, $useragent) {
$this->deviceid = $devid;
$this->devicetype = $devicetype;
list ($this->deviceuser, $this->domain) = Utils::SplitDomainUser($getuser);
$this->useragent = $useragent;
$this->firstsynctime = time();
$this->newdevice = true;
$this->ignoredMessageIds = array();
}
/**
* initializes the ASDevice with previousily saved data
*
* @param mixed $stateObject the StateObject containing the device data
* @param boolean $semanticUpdate indicates if data relevant for all users should be cross checked (e.g. wipe requests)
*
* @access public
* @return
*/
public function SetData($stateObject, $semanticUpdate = true) {
if (!($stateObject instanceof StateObject) || !isset($stateObject->devices) || !is_array($stateObject->devices)) return;
// is information about this device & user available?
if (isset($stateObject->devices[$this->deviceuser]) && $stateObject->devices[$this->deviceuser] instanceof ASDevice) {
// overwrite local data with data from the saved object
$this->SetDataArray($stateObject->devices[$this->deviceuser]->GetDataArray());
$this->newdevice = false;
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ASDevice data loaded for user: '%s'", $this->deviceuser));
}
// check if RWStatus from another user on same device may require action
if ($semanticUpdate && count($stateObject->devices) > 1) {
foreach ($stateObject->devices as $user=>$asuserdata) {
if ($user == $this->user) continue;
// another user has a required action on this device
if (isset($asuserdata->wipeStatus) && $asuserdata->wipeStatus > SYNC_PROVISION_RWSTATUS_OK) {
ZLog::Write(LOGLEVEL_INFO, sprintf("User '%s' has requested a remote wipe for this device on '%s'", $asuserdata->wipeRequestBy, strftime("%Y-%m-%d %H:%M", $asuserdata->wipeRequstOn)));
// reset status to PENDING if wipe was executed before
$this->wipeStatus = ($asuserdata->wipeStatus & SYNC_PROVISION_RWSTATUS_WIPED)?SYNC_PROVISION_RWSTATUS_PENDING:$asuserdata->wipeStatus;
$this->wipeRequestBy = $asuserdata->wipeRequestBy;
$this->wipeRequestOn = $asuserdata->wipeRequestOn;
$this->wipeActionOn = $asuserdata->wipeActionOn;
break;
}
}
}
self::$loadedData = $stateObject;
$this->changed = false;
}
/**
* Returns the current AS Device in it's StateObject
* If the data was not changed, it returns false (no need to update any data)
*
* @access public
* @return array/boolean
*/
public function GetData() {
if (! $this->changed)
return false;
// device was updated
$this->lastupdatetime = time();
unset($this->ignoredMessageIds);
if (!isset(self::$loadedData) || !isset(self::$loadedData->devices) || !is_array(self::$loadedData->devices)) {
self::$loadedData = new StateObject();
$devices = array();
}
else
$devices = self::$loadedData->devices;
$devices[$this->deviceuser] = $this;
// check if RWStatus has to be updated so it can be updated for other users on same device
if (isset($this->wipeStatus) && $this->wipeStatus > SYNC_PROVISION_RWSTATUS_OK) {
foreach ($devices as $user=>$asuserdata) {
if ($user == $this->deviceuser) continue;
if (isset($this->wipeStatus)) $asuserdata->wipeStatus = $this->wipeStatus;
if (isset($this->wipeRequestBy)) $asuserdata->wipeRequestBy = $this->wipeRequestBy;
if (isset($this->wipeRequestOn)) $asuserdata->wipeRequestOn = $this->wipeRequestOn;
if (isset($this->wipeActionOn)) $asuserdata->wipeActionOn = $this->wipeActionOn;
$devices[$user] = $asuserdata;
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Updated remote wipe status for user '%s' on the same device", $user));
}
}
self::$loadedData->devices = $devices;
return self::$loadedData;
}
/**
* Removes internal data from the object, so this data can not be exposed
*
* @access public
* @return boolean
*/
public function StripData() {
unset($this->changed);
unset($this->unsetdata);
unset($this->hierarchyCache);
unset($this->forceSave);
unset($this->newdevice);
unset($this->ignoredMessageIds);
if (isset($this->ignoredmessages) && is_array($this->ignoredmessages)) {
$imessages = $this->ignoredmessages;
$unserializedMessage = array();
foreach ($imessages as $im) {
$im->asobject = unserialize($im->asobject);
$im->asobject->StripData();
$unserializedMessage[] = $im;
}
$this->ignoredmessages = $unserializedMessage;
}
return true;
}
/**
* Indicates if the object was just created
*
* @access public
* @return boolean
*/
public function IsNewDevice() {
return (isset($this->newdevice) && $this->newdevice === true);
}
/**----------------------------------------------------------------------------------------------------------
* Non-standard Getter and Setter
*/
/**
* Returns the user agent of this device
*
* @access public
* @return string
*/
public function GetDeviceUserAgent() {
if (!isset($this->useragent) || !$this->useragent)
return "unknown";
return $this->useragent;
}
/**
* Returns the user agent history of this device
*
* @access public
* @return string
*/
public function GetDeviceUserAgentHistory() {
return $this->useragentHistory;
}
/**
* Sets the useragent of the current request
* If this value is alreay available, no update is done
*
* @param string $useragent
*
* @access public
* @return boolean
*/
public function SetUserAgent($useragent) {
if ($useragent == $this->useragent || $useragent === false || $useragent === Request::UNKNOWN)
return true;
// save the old user agent, if available
if ($this->useragent != "") {
// [] = changedate, previous user agent
$a = $this->useragentHistory;
// only add if this agent was not seen before
if (! in_array(array(true, $this->useragent), $a)) {
$a[] = array(time(), $this->useragent);
$this->useragentHistory = $a;
}
}
$this->useragent = $useragent;
return true;
}
/**
* Sets the current remote wipe status
*
* @param int $status
* @param string $requestedBy
* @access public
* @return int
*/
public function SetWipeStatus($status, $requestedBy = false) {
// force saving the updated information if there was a transition between the wiping status
if ($this->wipeStatus > SYNC_PROVISION_RWSTATUS_OK && $status > SYNC_PROVISION_RWSTATUS_OK)
$this->forceSave = true;
if ($requestedBy != false) {
$this->wipeRequestedBy = $requestedBy;
$this->wipeRequestedOn = time();
}
else {
$this->wipeActionOn = time();
}
$this->wipeStatus = $status;
if ($this->wipeStatus > SYNC_PROVISION_RWSTATUS_PENDING)
ZLog::Write(LOGLEVEL_INFO, sprintf("ASDevice id '%s' was %s remote wiped on %s. Action requested by user '%s' on %s",
$this->deviceid, ($this->wipeStatus == SYNC_PROVISION_RWSTATUS_REQUESTED ? "requested to be": "sucessfully"),
strftime("%Y-%m-%d %H:%M", $this->wipeActionOn), $this->wipeRequestedBy, strftime("%Y-%m-%d %H:%M", $this->wipeRequestedOn)));
}
/**
* Sets the deployed policy key
*
* @param int $policykey
*
* @access public
* @return
*/
public function SetPolicyKey($policykey) {
$this->policykey = $policykey;
if ($this->GetWipeStatus() == SYNC_PROVISION_RWSTATUS_NA)
$this->wipeStatus = SYNC_PROVISION_RWSTATUS_OK;
}
/**
* Adds a messages which was ignored to the device data
*
* @param StateObject $ignoredMessage
*
* @access public
* @return boolean
*/
public function AddIgnoredMessage($ignoredMessage) {
// we should have all previousily ignored messages in an id array
if (count($this->ignoredMessages) != count($this->ignoredMessageIds)) {
foreach($this->ignoredMessages as $oldMessage) {
if (!isset($this->ignoredMessageIds[$oldMessage->folderid]))
$this->ignoredMessageIds[$oldMessage->folderid] = array();
$this->ignoredMessageIds[$oldMessage->folderid][] = $oldMessage->id;
}
}
// serialize the AS object - if available
if (isset($ignoredMessage->asobject))
$ignoredMessage->asobject = serialize($ignoredMessage->asobject);
// try not to add the same message several times
if (isset($ignoredMessage->folderid) && isset($ignoredMessage->id)) {
if (!isset($this->ignoredMessageIds[$ignoredMessage->folderid]))
$this->ignoredMessageIds[$ignoredMessage->folderid] = array();
if (in_array($ignoredMessage->id, $this->ignoredMessageIds[$ignoredMessage->folderid]))
$this->RemoveIgnoredMessage($ignoredMessage->folderid, $ignoredMessage->id);
$this->ignoredMessageIds[$ignoredMessage->folderid][] = $ignoredMessage->id;
$msges = $this->ignoredMessages;
$msges[] = $ignoredMessage;
$this->ignoredMessages = $msges;
return true;
}
else {
$msges = $this->ignoredMessages;
$msges[] = $ignoredMessage;
$this->ignoredMessages = $msges;
ZLog::Write(LOGLEVEL_WARN, "ASDevice->AddIgnoredMessage(): added message has no folder/id");
return true;
}
}
/**
* Removes message in the list of ignored messages
*
* @param string $folderid parent folder id of the message
* @param string $id message id
*
* @access public
* @return boolean
*/
public function RemoveIgnoredMessage($folderid, $id) {
// we should have all previousily ignored messages in an id array
if (count($this->ignoredMessages) != count($this->ignoredMessageIds)) {
foreach($this->ignoredMessages as $oldMessage) {
if (!isset($this->ignoredMessageIds[$oldMessage->folderid]))
$this->ignoredMessageIds[$oldMessage->folderid] = array();
$this->ignoredMessageIds[$oldMessage->folderid][] = $oldMessage->id;
}
}
$foundMessage = false;
// there are ignored messages in that folder
if (isset($this->ignoredMessageIds[$folderid])) {
// resync of a folder.. we should remove all previousily ignored messages
if ($id === false || in_array($id, $this->ignoredMessageIds[$folderid], true)) {
$ignored = $this->ignoredMessages;
$newMessages = array();
foreach ($ignored as $im) {
if ($im->folderid == $folderid) {
if ($id === false || $im->id === $id) {
$foundMessage = true;
if (count($this->ignoredMessageIds[$folderid]) == 1) {
unset($this->ignoredMessageIds[$folderid]);
}
else {
unset($this->ignoredMessageIds[$folderid][array_search($id, $this->ignoredMessageIds[$folderid])]);
}
continue;
}
else
$newMessages[] = $im;
}
}
$this->ignoredMessages = $newMessages;
}
}
return $foundMessage;
}
/**
* Indicates if a message is in the list of ignored messages
*
* @param string $folderid parent folder id of the message
* @param string $id message id
*
* @access public
* @return boolean
*/
public function HasIgnoredMessage($folderid, $id) {
// we should have all previousily ignored messages in an id array
if (count($this->ignoredMessages) != count($this->ignoredMessageIds)) {
foreach($this->ignoredMessages as $oldMessage) {
if (!isset($this->ignoredMessageIds[$oldMessage->folderid]))
$this->ignoredMessageIds[$oldMessage->folderid] = array();
$this->ignoredMessageIds[$oldMessage->folderid][] = $oldMessage->id;
}
}
$foundMessage = false;
// there are ignored messages in that folder
if (isset($this->ignoredMessageIds[$folderid])) {
// resync of a folder.. we should remove all previousily ignored messages
if ($id === false || in_array($id, $this->ignoredMessageIds[$folderid], true)) {
$foundMessage = true;
}
}
return $foundMessage;
}
/**----------------------------------------------------------------------------------------------------------
* HierarchyCache and ContentData operations
*/
/**
* Sets the HierarchyCache
* The hierarchydata, can be:
* - false a new HierarchyCache is initialized
* - array() new HierarchyCache is initialized and data from GetHierarchy is loaded
* - string previousely serialized data is loaded
*
* @param string $hierarchydata (opt)
*
* @access public
* @return boolean
*/
public function SetHierarchyCache($hierarchydata = false) {
if ($hierarchydata !== false && $hierarchydata instanceof ChangesMemoryWrapper) {
$this->hierarchyCache = $hierarchydata;
$this->hierarchyCache->CopyOldState();
}
else
$this->hierarchyCache = new ChangesMemoryWrapper();
if (is_array($hierarchydata))
return $this->hierarchyCache->ImportFolders($hierarchydata);
return true;
}
/**
* Returns serialized data of the HierarchyCache
*
* @access public
* @return string
*/
public function GetHierarchyCacheData() {
if (isset($this->hierarchyCache))
return $this->hierarchyCache;
ZLog::Write(LOGLEVEL_WARN, "ASDevice->GetHierarchyCacheData() has no data! HierarchyCache probably never initialized.");
return false;
}
/**
* Returns the HierarchyCache Object
*
* @access public
* @return object HierarchyCache
*/
public function GetHierarchyCache() {
if (!isset($this->hierarchyCache))
$this->SetHierarchyCache();
ZLog::Write(LOGLEVEL_DEBUG, "ASDevice->GetHierarchyCache(): ". $this->hierarchyCache->GetStat());
return $this->hierarchyCache;
}
/**
* Returns all known folderids
*
* @access public
* @return array
*/
public function GetAllFolderIds() {
if (isset($this->contentData) && is_array($this->contentData))
return array_keys($this->contentData);
return array();
}
/**
* Returns a linked UUID for a folder id
*
* @param string $folderid (opt) if not set, Hierarchy UUID is returned
*
* @access public
* @return string
*/
public function GetFolderUUID($folderid = false) {
if ($folderid === false)
return (isset($this->hierarchyUuid) && $this->hierarchyUuid !== self::UNDEFINED) ? $this->hierarchyUuid : false;
else if (isset($this->contentData) && isset($this->contentData[$folderid]) && isset($this->contentData[$folderid][self::FOLDERUUID]))
return $this->contentData[$folderid][self::FOLDERUUID];
return false;
}
/**
* Link a UUID to a folder id
* If a boolean false UUID is sent, the relation is removed
*
* @param string $uuid
* @param string $folderid (opt) if not set Hierarchy UUID is linked
*
* @access public
* @return boolean
*/
public function SetFolderUUID($uuid, $folderid = false) {
if ($folderid === false) {
$this->hierarchyUuid = $uuid;
// when unsetting the hierarchycache, also remove saved contentdata and ignoredmessages
if ($folderid === false) {
$this->contentData = array();
$this->ignoredMessageIds = array();
$this->ignoredMessages = array();
}
}
else {
$contentData = $this->contentData;
if (!isset($contentData[$folderid]) || !is_array($contentData[$folderid]))
$contentData[$folderid] = array();
// check if the foldertype is set. This has to be available at this point, as generated during the first HierarchySync
if (!isset($contentData[$folderid][self::FOLDERTYPE]))
return false;
if ($uuid)
$contentData[$folderid][self::FOLDERUUID] = $uuid;
else
$contentData[$folderid][self::FOLDERUUID] = false;
$this->contentData = $contentData;
}
}
/**
* Returns a foldertype for a folder already known to the mobile
*
* @param string $folderid
*
* @access public
* @return int/boolean returns false if the type is not set
*/
public function GetFolderType($folderid) {
if (isset($this->contentData) && isset($this->contentData[$folderid]) &&
isset($this->contentData[$folderid][self::FOLDERTYPE]) )
return $this->contentData[$folderid][self::FOLDERTYPE];
return false;
}
/**
* Sets the foldertype of a folder id
*
* @param string $uuid
* @param string $folderid (opt) if not set Hierarchy UUID is linked
*
* @access public
* @return boolean true if the type was set or updated
*/
public function SetFolderType($folderid, $foldertype) {
$contentData = $this->contentData;
if (!isset($contentData[$folderid]) || !is_array($contentData[$folderid]))
$contentData[$folderid] = array();
if (!isset($contentData[$folderid][self::FOLDERTYPE]) || $contentData[$folderid][self::FOLDERTYPE] != $foldertype ) {
$contentData[$folderid][self::FOLDERTYPE] = $foldertype;
$this->contentData = $contentData;
return true;
}
return false;
}
/**
* Gets the supported fields transmitted previousely by the device
* for a certain folder
*
* @param string $folderid
*
* @access public
* @return array/boolean false means no supportedFields are available
*/
public function GetSupportedFields($folderid) {
if (isset($this->contentData) && isset($this->contentData[$folderid]) &&
isset($this->contentData[$folderid][self::FOLDERUUID]) && $this->contentData[$folderid][self::FOLDERUUID] !== false &&
isset($this->contentData[$folderid][self::FOLDERSUPPORTEDFIELDS]) )
return $this->contentData[$folderid][self::FOLDERSUPPORTEDFIELDS];
return false;
}
/**
* Sets the set of supported fields transmitted by the device for a certain folder
*
* @param string $folderid
* @param array $fieldlist supported fields
*
* @access public
* @return boolean
*/
public function SetSupportedFields($folderid, $fieldlist) {
$contentData = $this->contentData;
if (!isset($contentData[$folderid]) || !is_array($contentData[$folderid]))
$contentData[$folderid] = array();
$contentData[$folderid][self::FOLDERSUPPORTEDFIELDS] = $fieldlist;
$this->contentData = $contentData;
return true;
}
/**
* Gets the current sync status of a certain folder
*
* @param string $folderid
*
* @access public
* @return mixed/boolean false means the status is not available
*/
public function GetFolderSyncStatus($folderid) {
if (isset($this->contentData) && isset($this->contentData[$folderid]) &&
isset($this->contentData[$folderid][self::FOLDERUUID]) && $this->contentData[$folderid][self::FOLDERUUID] !== false &&
isset($this->contentData[$folderid][self::FOLDERSYNCSTATUS]) )
return $this->contentData[$folderid][self::FOLDERSYNCSTATUS];
return false;
}
/**
* Sets the current sync status of a certain folder
*
* @param string $folderid
* @param mixed $status if set to false the current status is deleted
*
* @access public
* @return boolean
*/
public function SetFolderSyncStatus($folderid, $status) {
$contentData = $this->contentData;
if (!isset($contentData[$folderid]) || !is_array($contentData[$folderid]))
$contentData[$folderid] = array();
if ($status !== false) {
$contentData[$folderid][self::FOLDERSYNCSTATUS] = $status;
}
else if (isset($contentData[$folderid][self::FOLDERSYNCSTATUS])) {
unset($contentData[$folderid][self::FOLDERSYNCSTATUS]);
}
$this->contentData = $contentData;
return true;
}
}
?>