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
Methods summary
public
|
#
__construct( array $params )
Constructor.
Sets up the default logging mechanism.
Parameters
- $params
- <p>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'.</p>
|
public
Horde_SyncMl_Backend
|
#
factory( string $driver, array $params = null )
Attempts to return a concrete Horde_SyncMl_Backend instance based on $driver.
Attempts to return a concrete Horde_SyncMl_Backend instance based on $driver.
Parameters
- $driver
- <p>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.</p>
- $params
- <p>A hash containing any additional configuration or
connection parameters a subclass might need.</p>
Returns
|
public
|
#
setCharset( string $charset )
Sets the charset.
All data passed to the backend uses this charset and data returned from
the backend must use this charset, too.
Parameters
- $charset
- A valid charset.
|
public
string
|
#
getCharset( )
Returns the charset.
Returns
string The charset used when talking to the backend.
|
public
string
|
#
getSyncDeviceID( )
Returns the current device's ID.
Returns the current device's ID.
Returns
string The device ID.
|
public
|
#
setUser( string $user )
Sets the user used for this session.
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.
Parameters
|
public
string
|
#
getUser( )
Returns the current user.
Returns the current user.
Returns
string The current user.
|
public
|
#
setupState( )
Is called after the Horde_SyncMl_State object has been set up, either
restored from the session, or freshly created.
Is called after the Horde_SyncMl_State object has been set up, either
restored from the session, or freshly created.
|
public
|
#
sessionStart( string $syncDeviceID, string $sessionId, integer $backendMode = Horde_SyncMl_Backend::MODE_SERVER )
Starts a PHP session.
Parameters
- $syncDeviceID
- The device ID.
- $sessionId
- $session_id The session ID to use.
- $backendMode
- <p>The backend mode, one of the
Horde_SyncMl_Backend::MODE_* constants.</p>
|
public
|
|
public
boolean
|
#
isValidDatabaseURI( string $databaseURI )
Returns whether a database URI is valid to be synced with this backend.
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).
Parameters
- $databaseURI
- <p>URI of a database. Like calendar, tasks,
contacts or notes. May include optional
parameters:
tasks?options=ignorecompleted.</p>
Returns
boolean True if a valid URI.
|
public
mixed
|
#
getServerChanges( string $databaseURI, integer $from_ts, integer $to_ts, array & $adds, array & $mods, array & $dels )
Returns entries that have been modified in the server database.
Returns entries that have been modified in the server database.
Parameters
- $databaseURI
- <p>URI of Database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
- $from_ts
- Start timestamp.
- $to_ts
- <p>Exclusive end timestamp. Not yet
implemented.</p>
- $adds
- $adds Output array: hash of adds suid => 0
- $mods
- <p>$mods Output array: hash of modifications
suid => cuid</p>
- $dels
- $dels Output array: hash of deletions suid => cuid
Returns
mixed True on success or a PEAR_Error object.
|
public
mixed
|
#
retrieveEntry( string $databaseURI, string $suid, string $contentType, array $fields )
Retrieves an entry from the backend.
Retrieves an entry from the backend.
Parameters
- $databaseURI
- <p>URI of Database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
- $suid
- <p>Server unique id of the entry: for horde
this is the guid.</p>
- $contentType
- <p>Content-Type: the MIME type in which the
public function should return the data.</p>
- $fields
- <p>Hash of field names and Horde_SyncMl_Property
properties with the requested fields.</p>
Returns
mixed A string with the data entry or a PEAR_Error object.
|
public
array
|
#
addEntry( string $databaseURI, string $content, string $contentType, string $cuid )
Adds an entry into the server database.
Adds an entry into the server database.
Parameters
- $databaseURI
- <p>URI of Database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
- $content
- The actual data.
- $contentType
- MIME type of the content.
- $cuid
- Client ID of this entry.
Returns
array PEAR_Error or suid (Horde guid) of new entry
|
public
string
|
#
replaceEntry( string $databaseURI, string $content, string $contentType, string $cuid )
Replaces an entry in the server database.
Replaces an entry in the server database.
Parameters
- $databaseURI
- <p>URI of Database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
- $content
- The actual data.
- $contentType
- MIME type of the content.
- $cuid
- Client ID of this entry.
Returns
string PEAR_Error or server ID (Horde GUID) of modified entry.
|
public
boolean
|
#
deleteEntry( string $databaseURI, string $cuid )
Deletes an entry from the server database.
Deletes an entry from the server database.
Parameters
- $databaseURI
- <p>URI of Database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
- $cuid
- Client ID of the entry.
Returns
boolean True on success or false on failed (item not found).
|
public
boolean|string
|
#
checkAuthentication( string & $username, string $credData, string $credFormat, string $credType )
Authenticates the user at the backend.
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 .
Parameters
- $username
- <p>Username as provided in the <SyncHdr>.
May be overwritten by $credData.</p>
- $credData
- <p>Authentication data provided by <Cred><Data>
in the <SyncHdr>.</p>
- $credFormat
- <p>Format of data as <Cread><Meta><Format> in
the <SyncHdr>. Typically 'b64'.</p>
- $credType
- <p>Auth type as provided by <Cred><Meta><Type>
in the <SyncHdr>. Typically
'syncml:auth-basic'.</p>
Returns
boolean|string The user name if authentication succeeded, false
otherwise.
|
public
|
#
writeSyncAnchors( string $databaseURI, string $clientAnchorNext, string $serverAnchorNext )
Stores Sync anchors after a successful synchronization to allow two-way
synchronization next time.
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.
Parameters
- $databaseURI
- <p>URI of database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
- $clientAnchorNext
- <p>The client anchor as sent by the
client.</p>
- $serverAnchorNext
- <p>The anchor as used internally by the
server.</p>
|
public
mixed
|
#
readSyncAnchors( string $databaseURI )
Reads the previously written sync anchors from the database.
Reads the previously written sync anchors from the database.
Parameters
- $databaseURI
- <p>URI of database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
Returns
mixed Two-element array with client anchor and server anchor as
stored in previous writeSyncAnchor() calls. False if no
data found.
|
public
|
#
createUidMap( string $databaseURI, string $cuid, string $suid, integer $timestamp = 0 )
Creates a map entry to map between server and client IDs.
Creates a map entry to map between server and client IDs.
If an entry already exists, it is overwritten.
Parameters
- $databaseURI
- <p>URI of database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
- $cuid
- Client ID of the entry.
- $suid
- Server ID of the entry.
- $timestamp
- <p>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".</p>
|
public
|
#
eraseMap( string $databaseURI )
Erases all mapping entries for one combination of user, device ID.
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.
Parameters
- $databaseURI
- <p>URI of database to sync. Like calendar,
tasks, contacts or notes. May include
optional parameters:
tasks?options=ignorecompleted.</p>
|
public
|
#
logMessage( mixed $message, string $priority = 'INFO' )
Logs a message in the backend.
Logs a message in the backend.
TODO: This should be done via Horde_Log or the equivalent.
Parameters
- $message
- Either a string or a PEAR_Error object.
- $priority
- <p>$file What file was the log public function called from
(e.g. <strong>FILE</strong>)?</p>
|
public
|
#
logFile( integer $type, string $content, boolean $wbxml = false, boolean $sessionClose = false )
Logs data to a file in the debug directory.
Logs data to a file in the debug directory.
Parameters
- $type
- <p>The data type. One of the Horde_SyncMl_Backend::LOGFILE_*
constants.</p>
- $content
- The data content.
- $wbxml
- Whether the data is wbxml encoded.
- $sessionClose
- <p>Whether this is the last SyncML message
in a session. Bump the file number.</p>
|
public
|
#
close( )
Cleanup public function called after all message processing is finished.
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
mixed
|
#
getCurrentTimeStamp( )
Returns the current timestamp in the same format as used by
getServerChanges().
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.
Returns
mixed A timestamp of the current time.
|
public
|
#
testSetup( string $user, string $pwd )
Creates a clean test environment in the backend.
Creates a clean test environment in the backend.
Ensures there's a user with the given credentials and an empty data
store.
Parameters
- $user
- This user accout has to be created in the backend.
- $pwd
- The password for user $user.
|
public
|
#
testStart( string $user )
Prepares the test start.
Parameters
- $user
- This user accout has to be created in the backend.
|
public
|
#
testTearDown( )
Tears down the test environment after the test is run.
Tears down the test environment after the test is run.
|
public
string
|
#
normalize( string $databaseURI )
Normalizes a databaseURI to a database name, so that
_normalize('tasks?ignorecompleted') should return just 'tasks'.
Normalizes a databaseURI to a database name, so that
_normalize('tasks?ignorecompleted') should return just 'tasks'.
Parameters
- $databaseURI
- <p>URI of a database. Like calendar, tasks,
contacts or notes. May include optional
parameters:
tasks?options=ignorecompleted.</p>
Returns
string The normalized database name.
|
public
|
#
getParameter( string $url, string $parameter, string $default = null )
Extracts an HTTP GET like parameter from an URL.
Extracts an HTTP GET like parameter from an URL.
Example:
getParameter('test?q=1', 'q') == 1
Parameters
- $url
- The complete URL.
- $parameter
- The parameter name to extract.
- $default
- <p>A default value to return if none has been
provided in the URL.</p>
|