⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.78
Server IP:
41.128.143.86
Server:
Linux host.raqmix.cloud 6.8.0-1025-azure #30~22.04.1-Ubuntu SMP Wed Mar 12 15:28:20 UTC 2025 x86_64
Server Software:
Apache
PHP Version:
8.3.23
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
usr
/
share
/
psa-pear
/
pear
/
php
/
Horde
/
ActiveSync
/
Edit File: SyncCache.php
<?php /** * Horde_ActiveSync_SyncCache:: * * @license http://www.horde.org/licenses/gpl GPLv2 * * @copyright 2010-2020 Horde LLC (http://www.horde.org) * @author Michael J Rubinsky <mrubinsk@horde.org> * @package ActiveSync */ /** * Horde_ActiveSync_SyncCache:: Wraps all functionality related to maintaining * the ActiveSync SyncCache. * * @license http://www.horde.org/licenses/gpl GPLv2 * * @copyright 2010-2020 Horde LLC (http://www.horde.org) * @author Michael J Rubinsky <mrubinsk@horde.org> * @package ActiveSync * @internal Not intended for use outside of the Horde_ActiveSync library. * * The SyncCache maintains a number of "caches" - which are really more of a * shared memory store for keeping all relevant data from all running sync * requests up-to-date and available to other running requests: * * @property array $folders The folders cache: the list of * current folders, keyed by their internal uid and containing 'class', * 'serverid' and 'type'. * @property integer $hbinterval The heartbeat interval (in seconds). * @property integer $wait The wait interval (in minutes). * @property integer $pingheartbeat The heartbeat used in PING requests. * @property string $hierarchy The hierarchy synckey. * @property array $confirmed_synckeys Array of synckeys being confirmed * during a looping sync. * @property integer $lastuntil Timestamp representing the last * planned looping sync end time. * @property integer $lasthbsyncstarted Timestamp of the start of the last * looping sync. * @property integer $lastsyncendnormal Timestamp of the last looping sync * that ended normally. */ class Horde_ActiveSync_SyncCache { /** * The cache data. * * @var array */ protected $_data = array(); /** * The state driver * * @var Horde_ActiveSync_State_Base $state */ protected $_state; /** * The username * * @var string */ protected $_user; /** * The device id * * @var string */ protected $_devid; /** * Logger * * @var Horde_Log_Logger */ protected $_logger; /** * Track dirty properties. * * @var array */ protected $_dirty = array(); /** * Process id for logging. * * @var integer */ protected $_procid; /** * Constructor * * @param Horde_ActiveSync_State_Base $state The state driver * @param string $devid The device id * @param string $user The username * @param Horde_Log_Logger $logger The logger object * * @return Horde_ActiveSync_SyncCache */ public function __construct( Horde_ActiveSync_State_Base $state, $devid, $user, $logger = null) { $this->_state = $state; $this->_devid = $devid; $this->_user = $user; $this->_logger = $logger; $this->loadCacheFromStorage(); $this->_procid = getmypid(); // @todo $this->_logger = empty($logger) ? new Horde_ActiveSync_Log_Logger(new Horde_Log_Handler_Null()) : Horde_ActiveSync::_wrapLogger($logger); $this->_logger->meta('Creating new Horde_ActiveSync_SyncCache.'); } public function __get($property) { // @todo. Fixme. This is here to allow the Collections object access // to be able to close/reopen the backend connection. For H6 refactor // things so we don't need to expose this protected property. State and // Storage will be uncoupled in that refactor anyway. if ($property == 'state') { return $this->_state; } if (!$this->_isValidProperty($property)) { throw new InvalidArgumentException($property . ' is not a valid property'); } return !empty($this->_data[$property]) ? $this->_data[$property] : false; } public function __set($property, $value) { if (!$this->_isValidProperty($property)) { throw new InvalidArgumentException($property . ' is not a valid property'); } $this->_data[$property] = $value; $this->_dirty[$property] = true; } public function __isset($property) { if (!$this->_isValidProperty($property)) { throw new InvalidArgumentException($property . ' is not a valid property'); } return !empty($this->_data[$property]); } protected function _isValidProperty($property) { return in_array($property, array( 'hbinterval', 'wait', 'hierarchy', 'confirmed_synckeys', 'timestamp', 'lasthbsyncstarted', 'lastsyncendnormal', 'folders', 'pingheartbeat')); } /** * Validate the cache. Compares the cache timestamp with the current cache * timestamp in the state backend. If the timestamps are different, some * other request has modified the cache, so it should be invalidated. * * @param boolean $hb_only If true, only validate the hb timestamps. @since 2.4.0 * * @return boolean */ public function validateCache($hb_only = false) { $cache = $this->_state->getSyncCache( $this->_devid, $this->_user, array('lasthbsyncstarted', 'timestamp')); if ((!$hb_only && $cache['timestamp'] > $this->_data['timestamp']) || (!empty($cache['lasthbsyncstarted']) && $cache['lasthbsyncstarted'] > $this->_data['lasthbsyncstarted'])) { return false; } return true; } /** * Repopulate the cache data from storage. */ public function loadCacheFromStorage() { $this->_data = $this->_state->getSyncCache($this->_devid, $this->_user); $this->_dirty = array(); } /** * Perform some sanity checking on the various timestamps to ensure we * are in a valid state. Basically checks that we are not currently running * a looping sync and that the last looping sync ending normally. * * @return boolean * @deprecated Not needed any longer. Remove in H6. */ public function validateTimestamps() { if ((!empty($this->_data['lasthbsyncstarted']) && empty($this->_data['lastsyncendnormal'])) || (!empty($this->_data['lasthbsyncstarted']) && !empty($this->_data['lastsyncendnormal']) && ($this->_data['lasthbsyncstarted'] > $this->_data['lastsyncendnormal']))) { return false; } return true; } /** * Update the cache timestamp to the current time. */ public function updateTimestamp() { $this->timestamp = time(); } /** * Return all the collections in the syncCache. * * @param boolean $requireKey If true, only return collections with an * existing synckey in the cache. Otherwise * return all collections. * * @return array */ public function getCollections($requireKey = true) { $collections = array(); foreach ($this->_data['collections'] as $key => $collection) { if (!$requireKey || ($requireKey && !empty($collection['lastsynckey']))) { $collection['id'] = $key; $collections[$key] = $collection; } } return $collections; } /** * Return the count of available collections in the cache * * @param integer The count. */ public function countCollections() { if (empty($this->_data['collections'])) { return 0; } return count($this->_data['collections']); } /** * Remove all collection data. */ public function clearCollections() { $this->_logger->meta('Clearing collections data from cache.'); $this->_data['collections'] = array(); $this->_dirty['collections'] = true; } /** * Check for the existance of a specific collection in the cache. * * @param stirng $collectionid The collection id to search for. * * @return boolean */ public function collectionExists($collectionid) { return !empty($this->_data['collections'][$collectionid]); } /** * Set a specific collection to be PINGable. * * @param string $id The collection id. */ public function setPingableCollection($id) { if (empty($this->_data['collections'][$id])) { throw new InvalidArgumentException('Collection does not exist'); } $this->_data['collections'][$id]['pingable'] = true; $this->_markCollectionsDirty($id); } /** * Set a collection as non-PINGable. * * @param string $collectionid The collection id. */ public function removePingableCollection($id) { if (empty($this->_data['collections'][$id])) { $this->_logger->warn(sprintf( 'Collection %s was asked to be removed from PINGABLE but does not exist.', $id) ); return; } $this->_data['collections'][$id]['pingable'] = false; $this->_markCollectionsDirty($id); } /** * Check if a specified collection is PINGable. * * @param string $id The collection id. * * @return boolean */ public function collectionIsPingable($id) { return !empty($this->_data['collections'][$id]) && !empty($this->_data['collections'][$id]['pingable']); } /** * Set the ping change flag on a collection. Indicatates that the last * PING was terminated with a change in this collection. * * @param string $id The collection id. * @throws InvalidArgumentException * @since 2.3.0 */ public function setPingChangeFlag($id) { if (empty($this->_data['collections'][$id])) { throw new InvalidArgumentException('Collection does not exist.'); } $this->_data['collections'][$id]['pingchange'] = true; $this->_markCollectionsDirty($id); } /** * Checks the status of the ping change flag. If true, the last PING request * detected a change in the specified collection. * * @param string $collectionid The collection id to check. * * @return boolean * @since 2.3.0 */ public function hasPingChangeFlag($collectionid) { return !empty($this->_data['collections'][$collectionid]['pingchange']); } /** * Reset the specified collection's ping change flag. * * @param string $id The collectionid to reset. * @since 2.3.0 */ public function resetPingChangeFlag($id) { $this->_data['collections'][$id]['pingchange'] = false; $this->_markCollectionsDirty($id); } /** * Refresh the cached collections from the state backend. * */ public function refreshCollections() { $syncCache = $this->_state->getSyncCache( $this->_devid, $this->_user, array('collections') ); $cache_collections = !is_array($syncCache['collections']) ? array() : $syncCache['collections']; foreach ($cache_collections as $id => $cache_collection) { if (!isset($cache_collection['lastsynckey'])) { continue; } $cache_collection['id'] = $id; $cache_collection['synckey'] = $cache_collection['lastsynckey']; $this->_data['collections'][$id] = $cache_collection; $this->_markCollectionsDirty($id); } $this->_logger->meta('SyncCache collections refreshed.'); } /** * Save the synccache to storage. */ public function save() { // Iterate over the collections and persist the last known synckey. foreach ($this->_data['collections'] as $id => &$collection) { if (!empty($collection['synckey'])) { $collection['lastsynckey'] = $collection['synckey']; unset($collection['synckey']); $this->_markCollectionsDirty($id); } } $this->timestamp = time(); $this->_state->saveSyncCache( $this->_data, $this->_devid, $this->_user, $this->_dirty); $this->_dirty = array(); } /** * Add a new collection to the cache * * @param array $collection The collection array */ public function addCollection(array $collection) { $this->_data['collections'][$collection['id']] = array( 'class' => $collection['class'], 'windowsize' => isset($collection['windowsize']) ? $collection['windowsize'] : null, 'deletesasmoves' => isset($collection['deletesasmoves']) ? $collection['deletesasmoves'] : null, 'filtertype' => isset($collection['filtertype']) ? $collection['filtertype'] : null, 'truncation' => isset($collection['truncation']) ? $collection['truncation'] : null, 'rtftruncation' => isset($collection['rtftruncation']) ? $collection['rtftruncation'] : null, 'mimesupport' => isset($collection['mimesupport']) ? $collection['mimesupport'] : null, 'mimetruncation' => isset($collection['mimetruncation']) ? $collection['mimetruncation'] : null, 'conflict' => isset($collection['conflict']) ? $collection['conflict'] : null, 'bodyprefs' => isset($collection['bodyprefs']) ? $collection['bodyprefs'] : null, 'serverid' => isset($collection['serverid']) ? $collection['serverid'] : $collection['id'] ); $this->_markCollectionsDirty($collection['id']); } /** * Remove a collection from the cache. * * @param string $id The collection id. * @param boolean $purge If true, completely remove the collection entry * otherwise, just resets the synckey. */ public function removeCollection($id, $purge = true) { if ($purge) { $this->_logger->meta(sprintf( 'Removing collection %s from SyncCache.', $id) ); unset($this->_data['collections'][$id]); $this->_dirty['collections'] = true; } elseif (!empty($this->_data['collections'][$id])) { $this->_data['collections'][$id]['synckey'] = '0'; $this->_markCollectionsDirty($id); } } /** * Update the windowsize for the specified collection. * * @param string $id The collection id. * @param integer $size The updated windowsize. */ public function updateWindowSize($id, $windowsize) { $this->_data['collections'][$id]['windowsize'] = $windowsize; $this->_markCollectionsDirty($id); } /** * Update the filtertype for the specified collection. * * @param string $id The collection id. * @param integer $filtertype The updated filtertype. * @since 2.26.0 */ public function updateFiltertype($id, $filtertype) { $this->_data['collections'][$id]['filtertype'] = $filtertype; $this->_markCollectionsDirty($id); } /** * Clear all synckeys from the known collections. */ public function clearCollectionKeys() { $this->_logger->meta('Clearing all collection synckeys from the cache.'); foreach ($this->_data['collections'] as $id => &$c) { unset($c['synckey']); } $this->_dirty['collections'] = true; } /** * Add a confirmed synckey to the cache. * * @param string $key The synckey to add. */ public function addConfirmedKey($key) { $this->_data['confirmed_synckeys'][$key] = true; $this->_dirty['confirmed_synckeys'] = true; } /** * Remove a confirmed sycnkey from the cache * * @param string $key The synckey to remove. */ public function removeConfirmedKey($key) { unset($this->_data['confirmed_synckeys'][$key]); $this->_dirty['confirmed_synckeys'] = true; } /** * Update a collection in the cache. * * @param array $collection The collection data to add/update. * @param array $options Options: * - newsynckey: (boolean) Set the new synckey in the collection. * DEFAULT: false (Do not set the new synckey). * - unsetChanges: (boolean) Unset the GETCHANGES flag in the collection. * DEFAULT: false (Do not unset the GETCHANGES flag). * - unsetPingChangeFlag: (boolean) Unset the PINGCHANGES flag in the collection. * DEFUALT: false (Do not uset the PINGCHANGES flag). * @since 2.3.0 */ public function updateCollection(array $collection, array $options = array()) { $options = array_merge( array('newsynckey' => false, 'unsetChanges' => false, 'unsetPingChangeFlag' => false), $options ); if (!empty($collection['id'])) { if ($options['newsynckey']) { $this->_data['collections'][$collection['id']]['synckey'] = $collection['newsynckey']; $this->_markCollectionsDirty($collection['id']); } elseif (isset($collection['synckey'])) { $this->_data['collections'][$collection['id']]['synckey'] = $collection['synckey']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['class'])) { $this->_data['collections'][$collection['id']]['class'] = $collection['class']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['windowsize'])) { $this->_data['collections'][$collection['id']]['windowsize'] = $collection['windowsize']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['deletesasmoves'])) { $this->_data['collections'][$collection['id']]['deletesasmoves'] = $collection['deletesasmoves']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['filtertype'])) { $this->_data['collections'][$collection['id']]['filtertype'] = $collection['filtertype']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['truncation'])) { $this->_data['collections'][$collection['id']]['truncation'] = $collection['truncation']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['rtftruncation'])) { $this->_data['collections'][$collection['id']]['rtftruncation'] = $collection['rtftruncation']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['mimesupport'])) { $this->_data['collections'][$collection['id']]['mimesupport'] = $collection['mimesupport']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['mimetruncation'])) { $this->_data['collections'][$collection['id']]['mimetruncation'] = $collection['mimetruncation']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['conflict'])) { $this->_data['collections'][$collection['id']]['conflict'] = $collection['conflict']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['bodyprefs'])) { $this->_data['collections'][$collection['id']]['bodyprefs'] = $collection['bodyprefs']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['bodypartprefs'])) { $this->_data['collections'][$collection['id']]['bodypartprefs'] = $collection['bodypartprefs']; } if (isset($collection['pingable'])) { $this->_data['collections'][$collection['id']]['pingable'] = $collection['pingable']; $this->_markCollectionsDirty($collection['id']); } if (isset($collection['serverid'])) { $this->_data['collections'][$collection['id']]['serverid'] = $collection['serverid']; $this->_markCollectionsDirty($collection['id']); } if ($options['unsetChanges']) { unset($this->_data['collections'][$collection['id']]['getchanges']); $this->_markCollectionsDirty($collection['id']); } if ($options['unsetPingChangeFlag']) { unset($this->_data['collections'][$collection['id']]['pingchange']); $this->_markCollectionsDirty($collection['id']); } } else { $this->_logger->meta(sprintf( 'Collection without id found: %s', serialize($collection)) ); } } /** * Validate the collections from the cache and fill in any missing values * from the folder cache. * * @param array $collections A reference to an array of collections. */ public function validateCollectionsFromCache(&$collections) { foreach ($collections as $key => $values) { if (!isset($values['class']) && isset($this->_data['folders'][$values['id']]['class'])) { $collections[$key]['class'] = $this->_data['folders'][$values['id']]['class']; $this->_markCollectionsDirty($key); } if (!isset($values['type']) && isset($this->_data['folders'][$values['id']]['type'])) { $collections[$key]['type'] = $this->_data['folders'][$values['id']]['type']; $this->_markCollectionsDirty($key); } if (!isset($values['filtertype']) && isset($this->_data['collections'][$values['id']]['filtertype'])) { $collections[$key]['filtertype'] = $this->_data['collections'][$values['id']]['filtertype']; $this->_markCollectionsDirty($key); } if (!isset($values['mimesupport']) && isset($this->_data['collections'][$values['id']]['mimesupport'])) { $collections[$key]['mimesupport'] = $this->_data['collections'][$values['id']]['mimesupport']; $this->_markCollectionsDirty($key); } if (empty($values['bodyprefs']) && isset($this->_data['collections'][$values['id']]['bodyprefs'])) { $collections[$key]['bodyprefs'] = $this->_data['collections'][$values['id']]['bodyprefs']; $this->_markCollectionsDirty($key); } if (empty($values['truncation']) && isset($this->_data['collections'][$values['id']]['truncation'])) { $collections[$key]['truncation'] = $this->_data['collections'][$values['id']]['truncation']; $this->_markCollectionsDirty($key); } if (empty($values['mimetruncation']) && isset($this->_data['collections'][$values['id']]['mimetruncation'])) { $collections[$key]['mimetruncation'] = $this->_data['collections'][$values['id']]['mimetruncation']; $this->_markCollectionsDirty($key); } if (empty($values['serverid']) && isset($this->_data['collections'][$values['id']]['serverid'])) { $collections[$key]['serverid'] = $this->_data['collections'][$values['id']]['serverid']; $this->_markCollectionsDirty($key); } if (!isset($values['windowsize'])) { $collections[$key]['windowsize'] = isset($this->_data['collections'][$values['id']]['windowsize']) ? $this->_data['collections'][$values['id']]['windowsize'] : 100; $this->_markCollectionsDirty($key); } // According to specs, if WINDOWSIZE is out of bounds, interpret as 512. if ($collections[$key]['windowsize'] > Horde_ActiveSync_Request_Sync::MAX_WINDOW_SIZE || $collections[$key]['windowsize'] == 0) { $collections[$key]['windowsize'] = self::MAX_WINDOW_SIZE; $this->_markCollectionsDirty($key); } if (isset($values['synckey']) && $values['synckey'] == '0' && isset($this->_data['collections'][$values['id']]['synckey']) && $this->_data['collections'][$values['id']]['synckey'] != '0') { unset($this->_data['collections'][$values['id']]['synckey']); $this->_markCollectionsDirty($key); } if (!isset($values['pingable']) && isset($this->_data['collections'][$values['id']]['pingable'])) { $collections[$key]['pingable'] = $this->_data['collections'][$values['id']]['pingable']; $this->_markCollectionsDirty($key); } } } /** * Return the folders cache. * * @param array The folders cache. */ public function getFolders() { return count($this->_data['folders']) ? $this->_data['folders'] : array(); } /** * Clear the folder cache */ public function clearFolders() { $this->_data['folders'] = array(); $this->_dirty['folders'] = true; } /** * Refresh the folder cache from the backend. * * @since 2.18.0 */ public function refreshFolderCache() { $cache = $this->_state->getSyncCache($this->_devid, $this->_user); $this->_data['folders'] = $cache['folders']; $this->_dirty['folders'] = false; } /** * Update a folder entry in the cache. * * @param Horde_ActiveSync_Message_Folder $folder The folder object. */ public function updateFolder(Horde_ActiveSync_Message_Folder $folder) { switch ($folder->type) { case 7: case 15: $this->_data['folders'][$folder->serverid] = array('class' => 'Tasks'); break; case 8: case 13: $this->_data['folders'][$folder->serverid] = array('class' => 'Calendar'); break; case 9: case 14: $this->_data['folders'][$folder->serverid] = array('class' => 'Contacts'); break; case 17: case 10: $this->_data['folders'][$folder->serverid] = array('class' => 'Notes'); break; default: $this->_data['folders'][$folder->serverid] = array('class' => 'Email'); } $this->_data['folders'][$folder->serverid]['serverid'] = $folder->_serverid; $this->_data['folders'][$folder->serverid]['type'] = $folder->type; $this->_dirty['folders'] = true; } /** * Remove a folder from the cache * * @param string $folder The folder id to remove. */ public function deleteFolder($folder) { unset($this->_data['folders'][$folder]); unset($this->_data['collections'][$folder]); $this->_dirty['folders'] = true; $this->_markCollectionsDirty($folder); } /** * Return an entry from the folder cache. * * @param string $folder The folder id to return. * * @return array|boolean The folder cache array entry, false if not found. */ public function getFolder($folder) { return !empty($this->_data['folders'][$folder]) ? $this->_data['folders'][$folder] : false; } /** * Delete the entire synccache from the backend. */ public function delete() { $this->_state->deleteSyncCache($this->_devid, $this->_user); $this->_data = array(); $this->_dirty = array(); } /** * Mark specific collection as dirty, but only if the entire collection * data is not already marked dirty. * * @param boolean $id The collection to mark dirty. */ protected function _markCollectionsDirty($id) { if (isset($this->_dirty['collections']) && is_array($this->_dirty['collections'])) { $this->_dirty['collections'][$id] = true; } elseif (!isset($this->_dirty['collections']) || $this->_dirty['collections'] !== true) { $this->_dirty['collections'] = array(); $this->_markCollectionsDirty($id); } } }
Simpan