Documentation

Horde_SyncMl_Backend
in package

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 $database = $this->normalize($databaseURI) 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

Tags
todo

describe sync anchors Have a look at the SyncML spec http://www.openmobilealliance.org/tech/affiliates/syncml/syncmlindex.html to find out more.

  1. 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.

  2. 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).

  3. 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-2017 Horde LLC (http://www.horde.org/)

See the enclosed file LICENSE for license information (LGPL). If you did not receive this file, see http://www.horde.org/licenses/lgpl21.

author

Karsten Fourmont karsten@horde.org

Table of Contents

LOGFILE_CLIENTMESSAGE  = 1
Types of logfiles. See logFile() method.
LOGFILE_DATA  = 4
LOGFILE_DEVINF  = 3
LOGFILE_SERVERMESSAGE  = 2
MODE_CLIENT  = 2
MODE_SERVER  = 1
Backend modes.
MODE_TEST  = 3
$state  : Horde_SyncMl_State
The State object.
$_backendMode  : int
The backend mode. One of the Horde_SyncMl_Backend::MODE_* constants.
$_charset  : string
The charset used in the SyncML messages.
$_debugDir  : string
The directory where debugging information is stored.
$_debugFiles  : bool
Whether to save SyncML messages in the debug directory.
$_logLevel  : string
The log level.
$_logtext  : string
The concatenated log messages.
$_syncDeviceID  : string
The ID of the client device.
$_user  : string
The current user.
__construct()  : mixed
Constructor.
addEntry()  : array<string|int, mixed>
Adds an entry into the server database.
checkAuthentication()  : bool|string
Authenticates the user at the backend.
close()  : mixed
Cleanup public function called after all message processing is finished.
createUidMap()  : mixed
Creates a map entry to map between server and client IDs.
deleteEntry()  : bool
Deletes an entry from the server database.
eraseMap()  : mixed
Erases all mapping entries for one combination of user, device ID.
factory()  : Horde_SyncMl_Backend
Attempts to return a concrete Horde_SyncMl_Backend instance based on $driver.
getCharset()  : string
Returns the charset.
getCurrentTimeStamp()  : mixed
Returns the current timestamp in the same format as used by getServerChanges().
getParameter()  : mixed
Extracts an HTTP GET like parameter from an URL.
getServerChanges()  : mixed
Returns entries that have been modified in the server database.
getSyncDeviceID()  : string
Returns the current device's ID.
getUser()  : string
Returns the current user.
isValidDatabaseURI()  : bool
Returns whether a database URI is valid to be synced with this backend.
logFile()  : mixed
Logs data to a file in the debug directory.
logMessage()  : mixed
Logs a message in the backend.
normalize()  : string
Normalizes a databaseURI to a database name, so that _normalize('tasks?ignorecompleted') should return just 'tasks'.
readSyncAnchors()  : mixed
Reads the previously written sync anchors from the database.
replaceEntry()  : string
Replaces an entry in the server database.
retrieveEntry()  : mixed
Retrieves an entry from the backend.
sessionClose()  : mixed
Closes the PHP session.
sessionStart()  : mixed
Starts a PHP session.
setCharset()  : mixed
Sets the charset.
setupState()  : mixed
Is called after the Horde_SyncMl_State object has been set up, either restored from the session, or freshly created.
setUser()  : mixed
Sets the user used for this session.
testSetup()  : mixed
Creates a clean test environment in the backend.
testStart()  : mixed
Prepares the test start.
testTearDown()  : mixed
Tears down the test environment after the test is run.
writeSyncAnchors()  : mixed
Stores Sync anchors after a successful synchronization to allow two-way synchronization next time.
_checkAuthentication()  : bool|string
Authenticates the user at the backend.
_setAuthenticated()  : string
Sets a user as being authenticated at the backend.

Constants

LOGFILE_CLIENTMESSAGE

Types of logfiles. See logFile() method.

public mixed LOGFILE_CLIENTMESSAGE = 1

LOGFILE_DATA

public mixed LOGFILE_DATA = 4

LOGFILE_DEVINF

public mixed LOGFILE_DEVINF = 3

LOGFILE_SERVERMESSAGE

public mixed LOGFILE_SERVERMESSAGE = 2

MODE_CLIENT

public mixed MODE_CLIENT = 2

MODE_SERVER

Backend modes.

public mixed MODE_SERVER = 1

MODE_TEST

public mixed MODE_TEST = 3

Properties

$_backendMode

The backend mode. One of the Horde_SyncMl_Backend::MODE_* constants.

protected int $_backendMode

$_charset

The charset used in the SyncML messages.

protected string $_charset

$_debugDir

The directory where debugging information is stored.

protected string $_debugDir
Tags
see
Horde_SyncMl_Backend()

$_debugFiles

Whether to save SyncML messages in the debug directory.

protected bool $_debugFiles
Tags
see
Horde_SyncMl_Backend()

$_logLevel

The log level.

protected string $_logLevel = 'INFO'
Tags
see
Horde_SyncMl_Backend()

$_logtext

The concatenated log messages.

protected string $_logtext = ''

$_syncDeviceID

The ID of the client device.

protected string $_syncDeviceID

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.

$_user

The current user.

protected string $_user

Methods

__construct()

Constructor.

public __construct(array<string|int, mixed> $params) : mixed

Sets up the default logging mechanism.

Parameters
$params : array<string|int, mixed>

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'.
Return values
mixed

addEntry()

Adds an entry into the server database.

public addEntry(string $databaseURI, string $content, string $contentType, string $cuid) : array<string|int, mixed>
Parameters
$databaseURI : string

URI of Database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

$content : string

The actual data.

$contentType : string

MIME type of the content.

$cuid : string

Client ID of this entry.

Tags
abstract
Return values
array<string|int, mixed>

PEAR_Error or suid (Horde guid) of new entry

checkAuthentication()

Authenticates the user at the backend.

public checkAuthentication(string &$username, string $credData, string $credFormat, string $credType) : bool|string

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 .

Parameters
$username : string

Username as provided in the . May be overwritten by $credData.

$credData : string

Authentication data provided by in the .

$credFormat : string

Format of data as in the . Typically 'b64'.

$credType : string

Auth type as provided by in the . Typically 'syncml:auth-basic'.

Return values
bool|string

The user name if authentication succeeded, false otherwise.

close()

Cleanup public function called after all message processing is finished.

public close() : mixed

Allows for things like closing databases or flushing logs. When running in test mode, tearDown() must be called rather than close.

Return values
mixed

createUidMap()

Creates a map entry to map between server and client IDs.

public createUidMap(string $databaseURI, string $cuid, string $suid, int $timestamp) : mixed

If an entry already exists, it is overwritten.

Parameters
$databaseURI : string

URI of database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

$cuid : string

Client ID of the entry.

$suid : string

Server ID of the entry.

$timestamp : int

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".

Tags
abstract
Return values
mixed

deleteEntry()

Deletes an entry from the server database.

public deleteEntry(string $databaseURI, string $cuid) : bool
Parameters
$databaseURI : string

URI of Database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

$cuid : string

Client ID of the entry.

Tags
abstract
Return values
bool

True on success or false on failed (item not found).

eraseMap()

Erases all mapping entries for one combination of user, device ID.

public eraseMap(string $databaseURI) : mixed

This is used during SlowSync so that we really sync everything properly and no old mapping entries remain.

Parameters
$databaseURI : string

URI of database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

Tags
abstract
Return values
mixed

factory()

Attempts to return a concrete Horde_SyncMl_Backend instance based on $driver.

public factory(string $driver[, array<string|int, mixed> $params = null ]) : Horde_SyncMl_Backend
Parameters
$driver : string

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.

$params : array<string|int, mixed> = null

A hash containing any additional configuration or connection parameters a subclass might need.

Return values
Horde_SyncMl_Backend

The newly created concrete Horde_SyncMl_Backend instance, or false on an error.

getCharset()

Returns the charset.

public getCharset() : string
Return values
string

The charset used when talking to the backend.

getCurrentTimeStamp()

Returns the current timestamp in the same format as used by getServerChanges().

public getCurrentTimeStamp() : mixed

Backends can use their own way to represent timestamps, like unix epoch integers or UTC Datetime strings.

Return values
mixed

A timestamp of the current time.

getParameter()

Extracts an HTTP GET like parameter from an URL.

public getParameter(string $url, string $parameter[, string $default = null ]) : mixed

Example: getParameter('test?q=1', 'q') == 1

Parameters
$url : string

The complete URL.

$parameter : string

The parameter name to extract.

$default : string = null

A default value to return if none has been provided in the URL.

Tags
static
Return values
mixed

getServerChanges()

Returns entries that have been modified in the server database.

public getServerChanges(string $databaseURI, int $from_ts, int $to_ts, array<string|int, mixed> &$adds, array<string|int, mixed> &$mods, array<string|int, mixed> &$dels) : mixed
Parameters
$databaseURI : string

URI of Database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

$from_ts : int

Start timestamp.

$to_ts : int

Exclusive end timestamp. Not yet implemented.

$adds : array<string|int, mixed>

Output array: hash of adds suid => 0

$mods : array<string|int, mixed>

Output array: hash of modifications suid => cuid

$dels : array<string|int, mixed>

Output array: hash of deletions suid => cuid

Tags
abstract
Return values
mixed

True on success or a PEAR_Error object.

getSyncDeviceID()

Returns the current device's ID.

public getSyncDeviceID() : string
Return values
string

The device ID.

getUser()

Returns the current user.

public getUser() : string
Return values
string

The current user.

isValidDatabaseURI()

Returns whether a database URI is valid to be synced with this backend.

public isValidDatabaseURI(string $databaseURI) : bool

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).

Parameters
$databaseURI : string

URI of a database. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

Return values
bool

True if a valid URI.

logFile()

Logs data to a file in the debug directory.

public logFile(int $type, string $content[, bool $wbxml = false ][, bool $sessionClose = false ]) : mixed
Parameters
$type : int

The data type. One of the Horde_SyncMl_Backend::LOGFILE_* constants.

$content : string

The data content.

$wbxml : bool = false

Whether the data is wbxml encoded.

$sessionClose : bool = false

Whether this is the last SyncML message in a session. Bump the file number.

Return values
mixed

logMessage()

Logs a message in the backend.

public logMessage(mixed $message[, int $priority = 'INFO' ]) : mixed

TODO: This should be done via Horde_Log or the equivalent.

Parameters
$message : mixed

Either a string or a PEAR_Error object.

$priority : int = 'INFO'

The priority of the message. One of:

  • EMERG
  • ALERT
  • CRIT
  • ERR
  • WARN
  • NOTICE
  • INFO
  • DEBUG
Return values
mixed

normalize()

Normalizes a databaseURI to a database name, so that _normalize('tasks?ignorecompleted') should return just 'tasks'.

public normalize(string $databaseURI) : string
Parameters
$databaseURI : string

URI of a database. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

Return values
string

The normalized database name.

readSyncAnchors()

Reads the previously written sync anchors from the database.

public readSyncAnchors(string $databaseURI) : mixed
Parameters
$databaseURI : string

URI of database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

Tags
abstract
Return values
mixed

Two-element array with client anchor and server anchor as stored in previous writeSyncAnchor() calls. False if no data found.

replaceEntry()

Replaces an entry in the server database.

public replaceEntry(string $databaseURI, string $content, string $contentType, string $cuid) : string
Parameters
$databaseURI : string

URI of Database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

$content : string

The actual data.

$contentType : string

MIME type of the content.

$cuid : string

Client ID of this entry.

Tags
abstract
Return values
string

PEAR_Error or server ID (Horde GUID) of modified entry.

retrieveEntry()

Retrieves an entry from the backend.

public retrieveEntry(string $databaseURI, string $suid, string $contentType, array<string|int, mixed> $fields) : mixed
Parameters
$databaseURI : string

URI of Database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

$suid : string

Server unique id of the entry: for horde this is the guid.

$contentType : string

Content-Type: the MIME type in which the public function should return the data.

$fields : array<string|int, mixed>

Hash of field names and Horde_SyncMl_Property properties with the requested fields.

Tags
abstract
Return values
mixed

A string with the data entry or a PEAR_Error object.

sessionClose()

Closes the PHP session.

public sessionClose() : mixed
Return values
mixed

sessionStart()

Starts a PHP session.

public sessionStart(string $syncDeviceID, mixed $sessionId[, int $backendMode = Horde_SyncMl_Backend::MODE_SERVER ]) : mixed
Parameters
$syncDeviceID : string

The device ID.

$sessionId : mixed
$backendMode : int = Horde_SyncMl_Backend::MODE_SERVER

The backend mode, one of the Horde_SyncMl_Backend::MODE_* constants.

Return values
mixed

setCharset()

Sets the charset.

public setCharset(string $charset) : mixed

All data passed to the backend uses this charset and data returned from the backend must use this charset, too.

Parameters
$charset : string

A valid charset.

Return values
mixed

setupState()

Is called after the Horde_SyncMl_State object has been set up, either restored from the session, or freshly created.

public setupState() : mixed
Return values
mixed

setUser()

Sets the user used for this session.

public setUser(string $user) : mixed

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.

Parameters
$user : string

A user name.

Return values
mixed

testSetup()

Creates a clean test environment in the backend.

public testSetup(string $user, string $pwd) : mixed

Ensures there's a user with the given credentials and an empty data store.

Parameters
$user : string

This user accout has to be created in the backend.

$pwd : string

The password for user $user.

Tags
abstract
Return values
mixed

testStart()

Prepares the test start.

public testStart(string $user) : mixed
Parameters
$user : string

This user accout has to be created in the backend.

Return values
mixed

testTearDown()

Tears down the test environment after the test is run.

public testTearDown() : mixed
Tags
abstract

Should remove the testuser created during testSetup and all its data.

Return values
mixed

writeSyncAnchors()

Stores Sync anchors after a successful synchronization to allow two-way synchronization next time.

public writeSyncAnchors(string $databaseURI, string $clientAnchorNext, string $serverAnchorNext) : mixed

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.

Parameters
$databaseURI : string

URI of database to sync. Like calendar, tasks, contacts or notes. May include optional parameters: tasks?options=ignorecompleted.

$clientAnchorNext : string

The client anchor as sent by the client.

$serverAnchorNext : string

The anchor as used internally by the server.

Tags
abstract
Return values
mixed

_checkAuthentication()

Authenticates the user at the backend.

protected _checkAuthentication(string $username, string $password) : bool|string
Parameters
$username : string

A user name.

$password : string

A password.

Tags
abstract
Return values
bool|string

The user name if authentication succeeded, false otherwise.

_setAuthenticated()

Sets a user as being authenticated at the backend.

protected _setAuthenticated(string $username, string $credData) : string
Parameters
$username : string

A user name.

$credData : string

Authentication data provided by in the .

Tags
abstract
Return values
string

The user name.

Search results