⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.89
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
/
SyncMl
/
Edit File: Backend.php
<?php /** * A SyncML Backend provides the interface between the SyncML protocol and an * actual calendar or address book application. This "actual application" is * called the "data store" in this description. * * The backend provides the following groups of functions: * * 1) Access to the datastore * Reading, adding, replacing and deleting of entries. Also retrieve * information about changes in data store. This is done via the * retrieveEntry(), addEntry(), replaceEntry(), deleteEntry() and * getServerChanges() methods. * * 2) User management functions * This is the checkAuthentication() method to verify that a given user * password combination is allowed to access the backend data store, and * the setUser() method which does a "login" to the backend data store if * required by the type of backend data store. Please note that the * password is only transferred once in a sync session, so when handling * the subsequent packets messages, the user may need to be "logged in" * without a password. (Or the session management keeps the user "logged * in"). * * 3) Maintainig the client ID <-> server ID map * The SyncML protocol does not require clients and servers to use the same * primary keys for the data entries. So a map has to be in place to * convert between client primary keys (called cuid's here) and server * primary keys (called suid's). It's up to the server to maintain this * map. Method for this is createUidMap(). * * 4) Sync anchor handling * After a successful initial sync, the client and server sync timestamps * are stored. This allows to perform subsequent syncs as delta syncs, * where only new changes are replicated. Servers as well as clients need * to be able to store two sync anchors (the client's and the server's) for * a sync. Methods for this are readSyncAnchors() and writeSyncAnchors(). * * 5) Test supporting functions * The SyncML module comes with its own testing framework. All you need to * do is implement the two methods testSetup() and testTearDown() and you * are able to test your backend with all the test cases that are part of * the module. * * 6) Miscellaneous functions * This involves session handling (sessionStart() and sessionClose()), * logging (logMessage() and logFile()), timestamp creation * (getCurrentTimeStamp()), charset handling (getCharset(), setCharset()) * and database identification (isValidDatabaseURI()). For all of these * functions, a default implementation is provided in Horde_SyncMl_Backend. * * If you want to create a backend for your own appliction, you can either * derive from Horde_SyncMl_Backend and implement everything in groups 1 to 5 * or you derive from Horde_SyncMl_Backend_Sql which implements an example * backend based on direct database access using the PEAR MDB2 package. In this * case you only need to implement groups 1 to 3 and can use the implementation * from Horde_SyncMl_Backend_Sql as a guideline for these functions. * * Key Concepts * ------------ * In order to successfully create a backend, some understanding of a few key * concepts in SyncML and the Horde_SyncMl package are certainly helpful. So * here's some stuff that should make some issues clear (or at lest less * obfuscated): * * 1) DatabaseURIs and Databases * The SyncML protocol itself is completly independant from the data that * is replicated. Normally the data are calendar or address book entries * but it may really be anything from browser bookmarks to comeplete * database tables. An ID (string name) of the database you want to * actually replicate has to be configured in the client. Typically that's * something like 'calendar' or 'tasks'. Client and server must agree on * these names. In addition this string may be used to provide additional * arguments. These are provided in a HTTP GET query style: like * tasks?ignorecompletedtasks to replicate only pending tasks. Such a "sync * identifier" is called a DatabaseURI and is really a database name plus * some additional options. * The Horde_SyncMl package completly ignores these options and simply passes * them on to the backend. It's up to the backend to decide what to do with * them. However when dealing with the internal maps (cuid<->suid and sync * anchors), it's most likely to use the database name only rather than the * full databaseURI. The map information saying that server entry * 20070101203040xxa@mypc.org has id 768 in the client device is valid for * the database "tasks", not for "tasks?somesillyoptions". So what you * normally do is calling some kind of <code>$database = * $this->normalize($databaseURI)</cod> in every backend method that deals * with databaseURIs and use $database afterwards. However actual usage of * options is up to the backend implementation. SyncML works fine without. * * 2) Suid and Guid mapping * This is the mapping of client IDs to server IDs and vice versa. Please * note that this map is per user and per client device: the server entry * 20070101203040xxa@mypc.org may have ID 720 in your PDA and AA10FC3A in * your mobile phone. * * 3) Sync Anchors * @todo describe sync anchors * Have a look at the SyncML spec * http://www.openmobilealliance.org/tech/affiliates/syncml/syncmlindex.html * to find out more. * * 4) Changes and Timestamps * @todo description of Changes and Timestamps, "mirroring effect" * This is real tricky stuff. * First it's important to know, that the SyncML protocol requires the * ending timestamp of the sync timeframe to be exchanged _before_ the * actual syncing starts. So all changes made during a sync have timestamps * that are in the timeframe for the next upcoming sync. Data exchange in * a sync session works in two steps: 1st) the clients sends its changes to * the server, 2nd) the server sends its changes to the client. * So when in step 2, the backend datastore API is called with a request * like "give me all changes in the server since the last sync". Thus you * also get the changes induced by the client in step 1 as well. You have * to somehow "tag" them to avoid echoing (and thus duplicating) them back * to the client. Simply storing the guids in the session is not * sufficient: the changes are made _after_ the end timestamp (see 1) of * the current sync so you'll dupe them in the next sync. * The current implementation deals with this as follows: whenever a client * induced change is done in the backend, the timestamp for this change is * stored in the cuid<->suid map in an additional field. That's the perfect * place as the tagging needs to be done "per client device": when an add * is received from the PDA it must not be sent back as an add to this * device, but to mobile phone it must be sent. * This is sorted out during the getServerChanges() process: if a server * change has a timestamp that's the same as in the guid<->suid map, it * came from the client and must not be added to the list of changes to be * sent to this client. * See the description of Horde_SyncMl_Backend_Sql::_getChangeTS() for some * more information. * * 5) Messages and Packages * A message is a single HTTP Request. A package is single "logical * message", a sync step. Normally the two coincide. However due to message * size restrictions one package may be transferred in multiple messages * (HTTP requests). * * 7) Server mode, client mode and test mode * Per default, a backend is used for an SyncML server. Regarding the * SyncML protocol, the working of client and server is similar, except * that * a) the client initiates the sync requests and the server respons to them, * and * b) the server must maintain the client id<->server id map. * * Currently the Horde_SyncMl package is designed to create servers. But * is's an obvious (and straightforward) extension to do it for clients as * well. And as a client has actually less work to do than a server, the * backend should work for servers _and_ clients. During the sessionStart(), * the backend gets a parameter to let it know whether it's in client or * server mode (or test, see below). When in client mode, it should behave * slightly different: * a) the client doesn't do suid<->cuid mapping, so all invokations to the * map creation method createUidMap(). * b) the client has only client ids, no server ids. So all arguments are * considered cuids even when named suid. See the Horde_SyncMl_Backend_Sql * implementation, it's actually not that difficult. * * Finally there's the test mode. The test cases consist of replaying * pre-recorded sessions. For that to work, the test script must "simulate" * user entries in the server data store. To do so, it creates a backend in * test mode. This behaves similar to a client: when an server entry is * created (modified) using addEntry() (replaceEntry()), no map entry must * be done. * The test backend uses also the two methods testSetup() and testTearDown() * to create a clean (empty) enviroment for the test user "syncmltest". See * the Horde_SyncMl_Backend_Sql implementation for details. * * Copyright 2005-2016 Horde LLC (http://www.horde.org/) * * See the enclosed file COPYING for license information (LGPL). If you * did not receive this file, see http://www.horde.org/licenses/lgpl21. * * @author Karsten Fourmont <karsten@horde.org> * @package SyncMl */ class Horde_SyncMl_Backend { /** Types of logfiles. See logFile() method. */ const LOGFILE_CLIENTMESSAGE = 1; const LOGFILE_SERVERMESSAGE = 2; const LOGFILE_DEVINF = 3; const LOGFILE_DATA = 4; /** Backend modes. */ const MODE_SERVER = 1; const MODE_CLIENT = 2; const MODE_TEST = 3; /** * The State object. * * @var Horde_SyncMl_State */ public $state; /** * The concatenated log messages. * * @var string */ protected $_logtext = ''; /** * The directory where debugging information is stored. * * @see Horde_SyncMl_Backend() * @var string */ protected $_debugDir; /** * Whether to save SyncML messages in the debug directory. * * @see Horde_SyncMl_Backend() * @var boolean */ protected $_debugFiles; /** * The log level. * * @see Horde_SyncMl_Backend() * @var string */ protected $_logLevel = 'INFO'; /** * The charset used in the SyncML messages. * * @var string */ protected $_charset; /** * The current user. * * @var string */ protected $_user; /** * The ID of the client device. * * This is used for all data access as an ID to allow to distinguish * between syncs with different devices. $this->_user together with * $this->_syncDeviceID is used as an additional key for all persistence * operations. * * @var string */ protected $_syncDeviceID; /** * The backend mode. One of the Horde_SyncMl_Backend::MODE_* constants. * * @var integer */ protected $_backendMode; /** * Constructor. * * Sets up the default logging mechanism. * * @param array $params A hash with parameters. The following are * supported by the default implementation. * Individual backends may support other parameters. * - debug_dir: A directory to write debug output * to. Must be writeable by the web * server. * - debug_files: If true, log all incoming and * outgoing packets and data * conversions and devinf log in * debug_dir. * - log_level: Only log entries with at least * this level. Defaults to 'INFO'. */ public function __construct($params) { if (!empty($params['debug_dir']) && is_dir($params['debug_dir'])) { $this->_debugDir = $params['debug_dir']; } $this->_debugFiles = !empty($params['debug_files']); if (isset($params['log_level'])) { $this->_logLevel = $params['log_level']; } $this->logMessage('Backend of class ' . get_class($this) . ' created', 'DEBUG'); } /** * Attempts to return a concrete Horde_SyncMl_Backend instance based on $driver. * * @param string $driver The type of concrete Backend subclass to return. * The code is dynamically included from * Backend/$driver.php if no path is given or * directly with "include_once $driver . '.php'" * if a path is included. So make sure this parameter * is "safe" and not directly taken from web input. * The class in the file must be named * 'Horde_SyncMl_Backend_' . basename($driver) and extend * Horde_SyncMl_Backend. * @param array $params A hash containing any additional configuration or * connection parameters a subclass might need. * * @return Horde_SyncMl_Backend The newly created concrete Horde_SyncMl_Backend * instance, or false on an error. */ public function factory($driver, $params = null) { if (empty($driver) || ($driver == 'none')) { return false; } $driver = basename($driver); $class = 'Horde_SyncMl_Backend_' . $driver; if (class_exists($class)) { $backend = new $class($params); } else { return false; } return $backend; } /** * Sets the charset. * * All data passed to the backend uses this charset and data returned from * the backend must use this charset, too. * * @param string $charset A valid charset. */ public function setCharset($charset) { $this->_charset = $charset; } /** * Returns the charset. * * @return string The charset used when talking to the backend. */ public function getCharset() { return $this->_charset; } /** * Returns the current device's ID. * * @return string The device ID. */ public function getSyncDeviceID() { return $this->_syncDeviceID; } /** * Sets the user used for this session. * * This method is called by SyncML right after sessionStart() when either * authentication is accepted via checkAuthentication() or a valid user * has been retrieved from the state. $this->_user together with * $this->_syncDeviceID is used as an additional key for all persistence * operations. * This method may have to force a "login", when the backend doesn't keep * auth state within a session or when in test mode. * * @param string $user A user name. */ public function setUser($user) { $this->_user = $user; } /** * Returns the current user. * * @return string The current user. */ public function getUser() { return $this->_user; } /** * Is called after the Horde_SyncMl_State object has been set up, either * restored from the session, or freshly created. */ public function setupState() { } /** * Starts a PHP session. * * @param string $syncDeviceID The device ID. * @param string $session_id The session ID to use. * @param integer $backendMode The backend mode, one of the * Horde_SyncMl_Backend::MODE_* constants. */ public function sessionStart($syncDeviceID, $sessionId, $backendMode = Horde_SyncMl_Backend::MODE_SERVER) { $this->_syncDeviceID = $syncDeviceID; $this->_backendMode = $backendMode; // Only the server needs to start a session: if ($this->_backendMode == Horde_SyncMl_Backend::MODE_SERVER) { $sid = md5($syncDeviceID . $sessionId); session_id($sid); @session_start(); } } /** * Closes the PHP session. */ public function sessionClose() { // Only the server needs to start a session: if ($this->_backendMode == Horde_SyncMl_Backend::MODE_SERVER) { session_unset(); session_destroy(); } } /** * Returns whether a database URI is valid to be synced with this backend. * * This default implementation accepts "tasks", "calendar", "notes" and * "contacts". However individual backends may offer replication of * different or completly other databases (like browser bookmarks or * cooking recipes). * * @param string $databaseURI URI of a database. Like calendar, tasks, * contacts or notes. May include optional * parameters: * tasks?options=ignorecompleted. * * @return boolean True if a valid URI. */ public function isValidDatabaseURI($databaseURI) { $database = $this->normalize($databaseURI); switch($database) { case 'tasks': case 'calendar': case 'notes': case 'contacts': case 'configuration': return true; default: $this->logMessage('Invalid database "' . $database . '". Try tasks, calendar, notes or contacts.', 'ERR'); return false; } } /** * Returns entries that have been modified in the server database. * * @abstract * * @param string $databaseURI URI of Database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. * @param integer $from_ts Start timestamp. * @param integer $to_ts Exclusive end timestamp. Not yet * implemented. * @param array &$adds Output array: hash of adds suid => 0 * @param array &$mods Output array: hash of modifications * suid => cuid * @param array &$dels Output array: hash of deletions suid => cuid * * @return mixed True on success or a PEAR_Error object. */ public function getServerChanges($databaseURI, $from_ts, $to_ts, &$adds, &$mods, &$dels) { die('getServerChanges() not implemented!'); } /** * Retrieves an entry from the backend. * * @abstract * * @param string $databaseURI URI of Database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. * @param string $suid Server unique id of the entry: for horde * this is the guid. * @param string $contentType Content-Type: the MIME type in which the * public function should return the data. * @param array $fields Hash of field names and Horde_SyncMl_Property * properties with the requested fields. * * @return mixed A string with the data entry or a PEAR_Error object. */ public function retrieveEntry($databaseURI, $suid, $contentType, $fields) { die('retrieveEntry() not implemented!'); } /** * Adds an entry into the server database. * * @abstract * * @param string $databaseURI URI of Database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. * @param string $content The actual data. * @param string $contentType MIME type of the content. * @param string $cuid Client ID of this entry. * * @return array PEAR_Error or suid (Horde guid) of new entry */ public function addEntry($databaseURI, $content, $contentType, $cuid) { die('addEntry() not implemented!'); } /** * Replaces an entry in the server database. * * @abstract * * @param string $databaseURI URI of Database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. * @param string $content The actual data. * @param string $contentType MIME type of the content. * @param string $cuid Client ID of this entry. * * @return string PEAR_Error or server ID (Horde GUID) of modified entry. */ public function replaceEntry($databaseURI, $content, $contentType, $cuid) { die('replaceEntry() not implemented!'); } /** * Deletes an entry from the server database. * * @abstract * * @param string $databaseURI URI of Database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. * @param string $cuid Client ID of the entry. * * @return boolean True on success or false on failed (item not found). */ public function deleteEntry($databaseURI, $cuid) { die('deleteEntry() not implemented!'); } /** * Authenticates the user at the backend. * * For some types of authentications (notably auth:basic) the username * gets extracted from the authentication data and is then stored in * username. For security reasons the caller must ensure that this is the * username that is used for the session, overriding any username * specified in <LocName>. * * @param string $username Username as provided in the <SyncHdr>. * May be overwritten by $credData. * @param string $credData Authentication data provided by <Cred><Data> * in the <SyncHdr>. * @param string $credFormat Format of data as <Cread><Meta><Format> in * the <SyncHdr>. Typically 'b64'. * @param string $credType Auth type as provided by <Cred><Meta><Type> * in the <SyncHdr>. Typically * 'syncml:auth-basic'. * * @return boolean|string The user name if authentication succeeded, false * otherwise. */ public function checkAuthentication(&$username, $credData, $credFormat, $credType) { if (empty($credData) || empty($credType)) { return false; } switch ($credType) { case 'syncml:auth-basic': list($username, $pwd) = explode(':', base64_decode($credData), 2); $this->logMessage('Checking authentication for user ' . $username, 'DEBUG'); return $this->_checkAuthentication($username, $pwd); case 'syncml:auth-md5': /* syncml:auth-md5 only transfers hash values of passwords. * Currently the syncml:auth-md5 hash scheme is not supported * by the authentication backend. So we can't use Horde to do * authentication. Instead here is a very crude direct manual hook: * To allow authentication for a user 'dummy' with password 'sync', * run * php -r 'print base64_encode(pack("H*",md5("dummy:sync")));' * from the command line. Then create an entry like * 'dummy' => 'ZD1ZeisPeQs0qipHc9tEsw==' in the users array below, * where the value is the command line output. * This user/password combination is then accepted for md5-auth. */ $users = array( // example for user dummy with pass pass: // 'dummy' => 'ZD1ZeisPeQs0qipHc9tEsw==' ); if (empty($users[$username])) { return false; } // @todo: nonce may be specified by client. Use it then. $nonce = ''; if (base64_encode(pack('H*', md5($users[$username] . ':' . $nonce))) === $credData) { return $this->_setAuthenticated($username, $credData); } return false; default: $this->logMessage('Unsupported authentication type ' . $credType, 'ERR'); return false; } } /** * Authenticates the user at the backend. * * @abstract * * @param string $username A user name. * @param string $password A password. * * @return boolean|string The user name if authentication succeeded, false * otherwise. */ protected function _checkAuthentication($username, $password) { die('_checkAuthentication() not implemented!'); } /** * Sets a user as being authenticated at the backend. * * @abstract * * @param string $username A user name. * @param string $credData Authentication data provided by <Cred><Data> * in the <SyncHdr>. * * @return string The user name. */ protected function _setAuthenticated($username, $credData) { die('setAuthenticated() not implemented!'); } /** * Stores Sync anchors after a successful synchronization to allow two-way * synchronization next time. * * The backend has to store the parameters in its persistence engine * where user, syncDeviceID and database are the keys while client and * server anchor ar the payload. See readSyncAnchors() for retrieval. * * @abstract * * @param string $databaseURI URI of database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. * @param string $clientAnchorNext The client anchor as sent by the * client. * @param string $serverAnchorNext The anchor as used internally by the * server. */ public function writeSyncAnchors($databaseURI, $clientAnchorNext, $serverAnchorNext) { } /** * Reads the previously written sync anchors from the database. * * @abstract * * @param string $databaseURI URI of database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. * * @return mixed Two-element array with client anchor and server anchor as * stored in previous writeSyncAnchor() calls. False if no * data found. */ public function readSyncAnchors($databaseURI) { } /** * Creates a map entry to map between server and client IDs. * * If an entry already exists, it is overwritten. * * @abstract * * @param string $databaseURI URI of database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. * @param string $cuid Client ID of the entry. * @param string $suid Server ID of the entry. * @param integer $timestamp Optional timestamp. This can be used to * 'tag' changes made in the backend during the * sync process. This allows to identify these, * and ensure that these changes are not * replicated back to the client (and thus * duplicated). See key concept "Changes and * timestamps". */ public function createUidMap($databaseURI, $cuid, $suid, $timestamp = 0) { } /** * Erases all mapping entries for one combination of user, device ID. * * This is used during SlowSync so that we really sync everything properly * and no old mapping entries remain. * * @abstract * * @param string $databaseURI URI of database to sync. Like calendar, * tasks, contacts or notes. May include * optional parameters: * tasks?options=ignorecompleted. */ public function eraseMap($databaseURI) { } /** * Logs a message in the backend. * * TODO: This should be done via Horde_Log or the equivalent. * * @param mixed $message Either a string or a PEAR_Error object. * @param string $file What file was the log public function called from * (e.g. __FILE__)? * @param integer $line What line was the log public function called from * (e.g. __LINE__)? * @param integer $priority The priority of the message. One of: * - EMERG * - ALERT * - CRIT * - ERR * - WARN * - NOTICE * - INFO * - DEBUG */ public function logMessage($message, $priority = 'INFO') { if (is_string($priority)) { $priority = defined('Horde_Log::' . $priority) ? constant('Horde_Log::' . $priority) : Horde_Log::INFO; } if (is_string($this->_logLevel)) { $loglevel = defined('Horde_Log::' . $this->_logLevel) ? constant('Horde_Log::' . $this->_logLevel) : Horde_Log::INFO; } else { $loglevel = $this->_logLevel; } if ($priority > $loglevel) { return; } // Internal logging to logtext if (is_string($this->_logtext)) { switch ($priority) { case Horde_Log::EMERG: $this->_logtext .= 'EMERG: '; break; case Horde_Log::ALERT: $this->_logtext .= 'ALERT: '; break; case Horde_Log::CRIT: $this->_logtext .= 'CRIT: '; break; case Horde_Log::ERR: $this->_logtext .= 'ERR: '; break; case Horde_Log::WARN: $this->_logtext .= 'WARNING:'; break; case Horde_Log::NOTICE: $this->_logtext .= 'NOTICE: '; break; case Horde_Log::INFO: $this->_logtext .= 'INFO: '; break; case Horde_Log::DEBUG: $this->_logtext .= 'DEBUG: '; break; default: $this->_logtext .= 'UNKNOWN:'; } if (is_string($message)) { $this->_logtext .= $message; } elseif (is_a($message, 'PEAR_Error')) { $this->_logtext .= $message->getMessage(); } $this->_logtext .= "\n"; } } /** * Logs data to a file in the debug directory. * * @param integer $type The data type. One of the Horde_SyncMl_Backend::LOGFILE_* * constants. * @param string $content The data content. * @param boolean $wbxml Whether the data is wbxml encoded. * @param boolean $sessionClose Whether this is the last SyncML message * in a session. Bump the file number. */ public function logFile($type, $content, $wbxml = false, $sessionClose = false) { if (empty($this->_debugDir) || !$this->_debugFiles) { return; } switch ($type) { case Horde_SyncMl_Backend::LOGFILE_CLIENTMESSAGE: $filename = 'client_'; $mode = 'wb'; break; case Horde_SyncMl_Backend::LOGFILE_SERVERMESSAGE: $filename = 'server_'; $mode = 'wb'; break; case Horde_SyncMl_Backend::LOGFILE_DEVINF: $filename = 'devinf.txt'; $mode = 'wb'; break; case Horde_SyncMl_Backend::LOGFILE_DATA: $filename = 'data.txt'; $mode = 'a'; break; default: // Unkown type. Use $type as filename: $filename = $type; $mode = 'a'; break; } if ($type === Horde_SyncMl_Backend::LOGFILE_CLIENTMESSAGE || $type === Horde_SyncMl_Backend::LOGFILE_SERVERMESSAGE) { $packetNum = @intval(file_get_contents($this->_debugDir . '/packetnum.txt')); if (empty($packetNum)) { $packetNum = 10; } if ($wbxml) { $filename .= $packetNum . '.wbxml'; } else { $filename .= $packetNum . '.xml'; } } /* Write file */ $fp = @fopen($this->_debugDir . '/' . $filename, $mode); if ($fp) { @fwrite($fp, $content); @fclose($fp); } if ($type === Horde_SyncMl_Backend::LOGFILE_CLIENTMESSAGE) { $this->logMessage('Started at ' . date('Y-m-d H:i:s') . '. Packet logged in ' . $this->_debugDir . '/' . $filename, 'DEBUG'); } /* Increase packet number. */ if ($type === Horde_SyncMl_Backend::LOGFILE_SERVERMESSAGE) { $this->logMessage('Finished at ' . date('Y-m-d H:i:s') . '. Packet logged in ' . $this->_debugDir . '/' . $filename, 'DEBUG'); $fp = @fopen($this->_debugDir . '/packetnum.txt', 'w'); if ($fp) { /* When one complete session is finished: go to next 10th. */ if ($sessionClose) { $packetNum += 10 - $packetNum % 10; } else { $packetNum += 1; } fwrite($fp, $packetNum); fclose($fp); } } } /** * Cleanup public function called after all message processing is finished. * * Allows for things like closing databases or flushing logs. When * running in test mode, tearDown() must be called rather than close. */ public function close() { if (!empty($this->_debugDir)) { $f = @fopen($this->_debugDir . '/log.txt', 'a'); if ($f) { fwrite($f, $this->_logtext . "\n"); fclose($f); } } session_write_close(); } /** * Returns the current timestamp in the same format as used by * getServerChanges(). * * Backends can use their own way to represent timestamps, like unix epoch * integers or UTC Datetime strings. * * @return mixed A timestamp of the current time. */ public function getCurrentTimeStamp() { /* Use unix epoch as default method for timestamps. */ return time(); } /** * Creates a clean test environment in the backend. * * Ensures there's a user with the given credentials and an empty data * store. * * @abstract * * @param string $user This user accout has to be created in the backend. * @param string $pwd The password for user $user. */ public function testSetup($user, $pwd) { die('testSetup() not implemented!'); } /** * Prepares the test start. * * @param string $user This user accout has to be created in the backend. */ public function testStart($user) { die('testStart() not implemented!'); } /** * Tears down the test environment after the test is run. * * @abstract * * Should remove the testuser created during testSetup and all its data. */ public function testTearDown() { die('testTearDown() not implemented!'); } /** * Normalizes a databaseURI to a database name, so that * _normalize('tasks?ignorecompleted') should return just 'tasks'. * * @param string $databaseURI URI of a database. Like calendar, tasks, * contacts or notes. May include optional * parameters: * tasks?options=ignorecompleted. * * @return string The normalized database name. */ public function normalize($databaseURI) { $database = Horde_String::lower( basename(preg_replace('|\?.*$|', '', $databaseURI))); /* Convert some commonly encountered types to a fixed set of known * service names: */ switch($database) { case 'contacts': case 'contact': case 'card': case 'scard': return 'contacts'; case 'calendar': case 'event': case 'events': case 'cal': case 'scal': return 'calendar'; case 'notes': case 'memo': case 'note': case 'snote': return 'notes'; case 'tasks': case 'task': case 'stask': return 'tasks'; default: return $database; } } /** * Extracts an HTTP GET like parameter from an URL. * * Example: <code>getParameter('test?q=1', 'q') == 1</code> * * @static * * @param string $url The complete URL. * @param string $parameter The parameter name to extract. * @param string $default A default value to return if none has been * provided in the URL. */ public function getParameter($url, $parameter, $default = null) { if (preg_match('|[&\?]' . $parameter . '=([^&]*)|', $url, $m)) { return $m[1]; } return $default; } }
Simpan