<?php

class Atmail_SyncML_Backend_Atmail extends SyncML_Backend 
{

	var $_db;

	function __construct( $params = array() )
	{
        
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		if( array_key_exists('backendMode', $params) )
			$this->_backendMode = $params['backendMode'];
		else
			$backendMode = SYNCML_BACKENDMODE_SERVER;
		    
		parent::__construct($params);
		$this->_db = Zend_Registry::get('dbAdapter');
		
	}

	function isValidDatabaseURI($databaseURI)
	{

		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$database = $this->_normalize($databaseURI);
		switch($database) 
		{
			case 'tasks':
			case 'calendar':
			case 'notes':
			case 'contacts':
			case 'events':
			case 'memo':
			case 'configuration':
				return true;
			
			default:
				Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ': Invalid database ' . $database . '. Try tasks, calendar, notes or contacts.');
				return false;
		}

	}

	/**
	 * Returns entries that have been modified in the server database.
	 *
	 * @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.
	 * @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.
	 */
	function getServerChanges( $databaseURI, $from_ts_Unix_GMT, $to_ts_Unix_GMT, &$adds, &$mods, &$dels )
	{

		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$database = $this->_normalize($databaseURI);
		$adds = $mods = $dels = array();

		// Find contacts that are not listed in the syncml_suidlist table
		//N.B. syncml_suidlist contains the suids from last sync, so can be referenced for server side adds, mods and deletes
		//for ads we can select contacts with create date after last sync
		$this->_contacts = new contacts(array('Account' => $this->_user) );
		$contacts = $this->_contacts->getContacts();
		
		//N.B. MySQL (NOW()) DateCreated timestamps are in MySQL Locale format, so we need that to do converted comparisons with $from_ts (GMT) and $to_th (GMT)
		//query must convert $from_ts (GMT) to MySQL DATETIME string in $timeZoneMySQL locale for comparison OR use 
		//get from_ts in MySQL Locale format
		$from_ts_Mysql_Locale = $this->_db->fetchOne('SELECT FROM_UNIXTIME(' . $this->_db->quote($from_ts_Unix_GMT) . ')');
		$to_ts_Mysql_Locale = $this->_db->fetchOne('SELECT FROM_UNIXTIME(' . $this->_db->quote($to_ts_Unix_GMT) . ')');
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": \$from_ts_Mysql_Locale: " . $from_ts_Mysql_Locale);
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": \$to_ts_Mysql_Locale: " . $to_ts_Mysql_Locale);
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . "\n________________________________________________");
		
		
		
		//get timestamps as stored in the db
		$from_ts_Unix_Locale = MySQL_to_Unix_Timestamp_Raw($from_ts_Mysql_Locale);
		$to_ts_Unix_Locale = MySQL_to_Unix_Timestamp_Raw($to_ts_Mysql_Locale);
		
		$from_ts_outlook = Unix_to_Outlook_Timestamp_GMT($from_ts_GMT);
		$to_ts_outlook = Unix_to_Outlook_Timestamp_GMT($to_ts_GMT);
		
		//first now narrow results to between from and to stamps, then check if after last sync stamp
		foreach( $contacts as $contact )
		{
			
		    $suid = $contact['id'];
			$suid_ts_MySQL_Locale = $contact['DateAdded'];
			//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":    $suid contact added Locale: " . $suid_ts_MySQL_Locale);
			
			$suid_ts_Unix_Locale = MySQL_to_Unix_Timestamp_Raw($suid_ts_MySQL_Locale);
			//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": \$suid_ts_Unix_Locale: " . $suid_ts_Unix_Locale);
			
			
			$sync_ts_Unix_GMT = $this->_getChangeTS($databaseURI, $suid);
			//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": \$sync_ts_Unix_GMT: " . $sync_ts_Unix_GMT);
			
			$q = 'SELECT FROM_UNIXTIME(' . $this->_db->quote($sync_ts_Unix_GMT) . ')';
			$sync_ts_MySQL_Locale = $this->_db->fetchOne( $q );
			//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": \n" . $q );
			//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": sync_ts_MySQL_Locale synced: " . $sync_ts_MySQL_Locale);
			//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": should be same as: Unix_to_MySQL_DATETIME_Locale(\$sync_ts_Unix_GMT): " . Unix_to_MySQL_DATETIME_Locale($sync_ts_Unix_GMT));
			$sync_ts_Unix_Locale = MySQL_to_Unix_Timestamp_Raw($sync_ts_MySQL_Locale);
			//Zend_Registry::get('log')->debug( "\n" . print_r($sync_ts_Unix_Locale, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$sync_ts_Unix_Locale \n");
			
			//ignore contacts that are outside requested range
			if( $suid_ts_Unix_Locale <= $from_ts_Unix_Locale || $to_ts_Unix_Locale <= $suid_ts_Unix_Locale )
				continue;
			 
			if( $sync_ts_Unix_Locale >= $suid_ts_Unix_Locale) 
			{
			
				// Change was done by us upon request of client, don't mirror
				// that back to the client.
				Zend_Registry::get('log')->debug(__METHOD__ . ' #' . __LINE__ . ": Added to server from client: SUID: $suid ignored");
				continue;
			
			}
			$adds[$suid] = 0; // 0 because we dont have a cuid until created on device
			
		}
        Zend_Registry::get('log')->debug( "\n" . print_r($adds, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$adds \n");
		
		// Only compile changes on delta sync:
		//Zend_Registry::get('log')->debug( "\n" . print_r($from_ts_Unix_GMT, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$from_ts_Unix_GMT \n");
		//Zend_Registry::get('log')->debug( "\n" . print_r($from_ts_Mysql_Locale, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$from_ts_Mysql_Locale \n");
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Test OTS to MySQL GMT TS:  40438.175139 : Outlook_to_Unix_Timestamp_GMT: " . Outlook_to_Unix_Timestamp_GMT(40438.175139));
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Test OTS to MySQL GMT TS:  40438.175139 : Unix_to_MySQL_DATETIME_Locale(Outlook_to_Unix_Timestamp_GMT: " . Unix_to_MySQL_DATETIME_GMT(Outlook_to_Unix_Timestamp_GMT(40438.175139)));
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Test OTS to MySQL GMT TS:  40438.175139 : Unix_to_MySQL_DATETIME_Locale(Outlook_to_Unix_Timestamp_GMT: " . Unix_to_MySQL_DATETIME_Locale(Outlook_to_Unix_Timestamp_GMT(40438.175139)));
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Test OTS to MySQL GMT TS:  40438.275339 : Outlook_to_Unix_Timestamp_GMT: " . Outlook_to_Unix_Timestamp_GMT(40438.275339));
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Test OTS to MySQL GMT TS:  40438.275339 : Unix_to_MySQL_DATETIME_Locale(Outlook_to_Unix_Timestamp_GMT: " . Unix_to_MySQL_DATETIME_GMT(Outlook_to_Unix_Timestamp_GMT(40438.275339)));
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Test OTS to MySQL GMT TS:  40438.275339 : Unix_to_MySQL_DATETIME_Locale(Outlook_to_Unix_Timestamp_GMT: " . Unix_to_MySQL_DATETIME_Locale(Outlook_to_Unix_Timestamp_GMT(40438.275339)));
		
		if( $from_ts_Unix_GMT > 0 )
		{
		
			Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": Calculating mods");
			
			// Handle replaces. We might get IDs that are already in the adds
			// array but that's ok: The calling code takes care to ignore
			// these.
			foreach( $contacts as $contact )
			{

			    $suid = $contact['id'];
				$suid_ts_Outlook_GMT = $contact['DateModified'];
				$suid_ts_Unix_GMT = Outlook_to_Unix_Timestamp_GMT($suid_ts_Outlook_GMT);
                //Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": $suid contact mod GMT: " . Unix_to_MySQL_DATETIME_GMT($suid_ts_Unix_GMT) );
				
				$sync_ts_Unix_GMT = $this->_getChangeTS($databaseURI, $suid);
				//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":   \$sync_ts_MySQL_GMT: " . Unix_to_MySQL_DATETIME_GMT($sync_ts_Unix_GMT) );
				
				//ignore contacts that are outside requested range
				if( $suid_ts_Unix_GMT <= $from_ts_Unix_GMT || $to_ts_Unix_GMT <= $suid_ts_Unix_GMT )
					continue;
					
				// Only the server needs to check the change timestamp to
				// identify client-sent changes.
				if( $this->_backendMode == SYNCML_BACKENDMODE_SERVER )
				{
				
					if( $sync_ts_Unix_GMT >= $suid_ts_Unix_GMT) 
					{
					
						// Change was done by us upon request of client, don't
						// mirror that back to the client.
						Zend_Registry::get('log')->debug( "Changed on server after sent from client: SUID: $suid ignored");
						
					}
					else
					{
					
						$cuid = $this->_getCuid($databaseURI, $suid);
						Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": \$suid : \$cuid from map: " . $suid . ":" . $cuid);
						
						if( $cuid !== false )
							$mods[$suid] = $cuid;
						
					}
						
				} 
				else
					$mods[$suid] = $suid;

			}
			Zend_Registry::get('log')->debug( "\n" . print_r($mods, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$mods \n");
			//$mods = array();    

		}
		
		
		// Handle deletions:
		// We assume stupid a backend datastore (Abook) where deleted
		// items are simply "gone" from the datastore. So we need to do our
		// own bookkeeping to identify entries that have been deleted since
		// the last sync run.
		// This is done by the _trackDeletes() helper function (syncml_suidlist table): we feed it with
		// a current list of all suids and get the ones missing (and thus deleted) in return.
		
		$contacts_ids = array();
		foreach( $contacts as $contact )
			$contacts_ids[] = $contact['id'];
		
		// Get deleted items and store current items:
		// Only use the deleted information on delta sync. On initial slowsync
		// we just need to call _trackDeletes() once to init the list.
		$contacts_ids_deleted = $this->_trackDeletes($databaseURI, $contacts_ids);
		//Zend_Registry::get('log')->debug( "\n" . print_r($contacts_ids_deleted, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$contacts_ids_deleted \n");
		
		if( $from_ts_Unix_GMT > 0 )
		{
		
			foreach( $contacts_ids_deleted as $contact_id => $cuid )
			{
				
				// Only the server needs to handle the cuid suid map:
				if ($this->_backendMode == SYNCML_BACKENDMODE_SERVER)
					$dels[$contact_id] = $cuid;
				else
					$dels[$contact_id] = $contact_id;
				
			}
			
		}
		Zend_Registry::get('log')->debug( "\n" . print_r($dels, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$dels \n");
		
	}

	/**
	 * Retrieves an entry from the backend.
	 *
	 * @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
	 *							 function should return the data.
	 * @param array $fields		Hash of field names and SyncML_Property
	 *							 properties with the requested fields.
	 *
	 * @return mixed  A string with the data entry or a PEAR_Error object.
	 */
	function retrieveEntry($databaseURI, $suid, $contentType, $fields)
	{
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI . ', $suid: ' . $suid . ', $contentType: ' . $contentType);
		if( $contentType != 'text/x-vcard' )
			return false;
		
		if( $suid == null || $suid == '' )
			throw new Exception(' no suid');
			
		$database = $this->_normalize($databaseURI);
                                              
		$this->_contacts = new contacts(array('Account' => $this->_user) );
		$contact = $this->_contacts->getContact($suid);
		
		//Zend_Registry::get('log')->debug( "\n" . print_r($contact, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$contact \n");
		
		if( !$contact )
			return false;
		
		//convert Abook record to vcard
		include_once('Atmail/Vcard.php');
		$vcardObj = new Vcard();
		$vcard = $vcardObj->exportPart($contact);
		$vcard = implode("\n\r", $vcard);
		//Zend_Registry::get('log')->debug( "\n" . print_r($vcard, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$vcard \n");
		return $vcard;
		//return $r[0]['syncml_data'];

	}

	/**
	 * Adds an entry into the server database.
	 *
	 * @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
	 */
	function addEntry($databaseURI, $content, $contentType, $cuid = null)
	{
		
		//TODO: prevent duplicates (exact matching data)
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . '$databaseURI: ' . $databaseURI . ', $contentType: ' . $contentType . ', $cuid: ' . $cuid . "\n\$content:\n" . $content );
		$database = $this->_normalize($databaseURI);

		// Generate an id (suid). It's also possible to use a database
		// generated primary key here.
		$created_ts = $this->getCurrentTimeStamp();
		
		$this->_contacts = new contacts(array('Account' => $this->_user) );
		//convert Abook record to vcard
		include_once('Atmail/Vcard.php');
		$vcardObj = new Vcard();
		$contactNew = $vcardObj->viewPart($content);
		//Zend_Registry::get('log')->debug( "\n" . print_r($contactNew, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$contactNew \n");
		$suid = $this->_contacts->addContact($contactNew);
		//Zend_Registry::get('log')->debug( "\n" . print_r($suid, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$suid \n");
		
		// Only the server needs to handle the cuid suid map:
		if ($this->_backendMode == SYNCML_BACKENDMODE_SERVER)
		   $this->createUidMap($databaseURI, $cuid, $suid, $created_ts);
		
        return $suid;

	}

	/**
	 * Replaces an entry in the server database.
	 *
	 * @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.
	 */
	function replaceEntry($databaseURI, $content, $contentType, $cuid)
	{
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI . ', $contentType: ' . $contentType . ', $cuid: ' . $cuid . ', $content: ' . "\n" . $content );
		$database = $this->_normalize($databaseURI);

		if ($this->_backendMode == SYNCML_BACKENDMODE_SERVER)
		{
			
			$suid = $this->_getSuid($databaseURI, $cuid);
			if( $suid === false )
			{
			    
				Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . " No map entry found for client id $cuid replacing on server");
				return false;
				
			}
		}
		else
			$suid = $cuid;

		// Entry exists: replace current one.
		$modified_ts = $this->getCurrentTimeStamp();
		//convert Abook record to vcard
		include_once('Atmail/Vcard.php');
		$vcardObj = new Vcard();
		Zend_Registry::get('log')->debug( "\n" . print_r($content, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$content \n");
		
		$contactUpdated = $vcardObj->viewPart($content);
		$contactUpdated['id'] = $suid;
		Zend_Registry::get('log')->debug( "\n" . print_r($contactUpdated, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$contactUpdated \n");
		$this->_contacts = new contacts(array('Account' => $this->_user) );
		$result = $this->_contacts->updateContact($contactUpdated);
		//Zend_Registry::get('log')->debug( "\n" . print_r($result, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$result \n");


		// Only the server needs to keep the map:
		if( $this->_backendMode == SYNCML_BACKENDMODE_SERVER )
			$this->createUidMap($databaseURI, $cuid, $suid, $modified_ts);
   		
		return $suid;

	}

	/**
	 * Deletes an entry from the server database.
	 *
	 * @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).
	 */
	function deleteEntry($databaseURI, $cuid)
	{
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI . ', $cuid: ' . $cuid );
		$database = $this->_normalize($databaseURI);

		// Find ID for this entry:
		if ($this->_backendMode == SYNCML_BACKENDMODE_SERVER)
			$suid = $this->_getSuid($databaseURI, $cuid);
		else
			$suid = $cuid;
		
		if( $suid === false )
			return true;

		// A clever backend datastore would store some information about a
		// deletion so this information can be extracted from the history.
		// However we do a "stupid" datastore here where deleted items are
		// simply gone. This allows us to illustrate the _trackDeletes()
		// bookkeeping mechanism.
		$this->_contacts = new contacts(array('Account' => $this->_user) );
        $affectedC = $this->_contacts->deleteContact( array('id' => $suid) );
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": \$affectedC: " . $affectedC );
		
		// Deleted bookkeeping is required for server and client, but not
		// for test mode:
		if ($this->_backendMode != SYNCML_BACKENDMODE_TEST)
		{
			
			$this->_removeFromSuidList($databaseURI, $suid);
			$this->_removeFromMap($databaseURI, $suid);
			
		}
		return true;

	}

	/**
	 * Authenticates the user at the backend.
	 *
	 * @param string $username	A user name.
	 * @param string $password	A password.
	 *
	 * @return boolean|string  The user name if authentication succeeded, false
	 *						 otherwise.
	 */
	function _checkAuthentication($username, $password)
	{
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . '$username: ' . $username . ', $password: ' . $password );
		$authSuccess = false;
		try
		{

			include_once('users.php');
			$userInfo = users::get($username);
			$authSuccess = users::auth( $username, $password );
            Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": \$authSuccess " . ($authSuccess?'true':'false'));
            
		}
		catch( Exception $e)
		{

			$authSuccess = false;

		}
		if( $authSuccess )
			return $username;
		return false;

		//$groupDefaultInfo = groups::get('default');
		//$groupUsersInfo = groups::get($userInfo['Ugroup']);                                                                          

		//if( $groupDefaultInfo['PushSupport'] != '1' || $groupUsersInfo['PushSupport'] != '1' )
		//	die('Push support not enabled for this user.');

	}

	/**
	 * 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.
	 */
	function setAuthenticated($username, $credData)
	{
		return $username;
	}

	/**
	 * 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.
	 *
	 * @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.
	 */
	function writeSyncAnchors( $databaseURI, $clientAnchorNext, $serverAnchorNext )
	{
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . print_r($databaseURI,true) . ', $clientAnchorNext: ' . print_r($clientAnchorNext,true) . ', $serverAnchorNext: ' . print_r($serverAnchorNext,true) );
		$database = $this->_normalize($databaseURI);

		// Check if entry exists. If not insert, otherwise update.
		if( !$this->readSyncAnchors($databaseURI) ) 
		{
			$q = 'INSERT INTO syncml_anchors 
			(syncml_syncpartner,  syncml_db,syncml_uid, syncml_clientanchor, syncml_serveranchor) VALUES ('
			. $this->_db->quote($this->_syncDeviceID, 'text') . ', '
			. $this->_db->quote($database, 'text') . ', '
			. $this->_db->quote($this->_user, 'text') . ', '
			. $this->_db->quote($clientAnchorNext, 'text') . ', '
			. $this->_db->quote($serverAnchorNext, 'text')
			. ')';
			$r = $this->_db->query($q);
			//Zend_Registry::get('log')->debug( "\n" . print_r($q, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$q \n");
			
		} 
		else
		{
		    
			$q = 'UPDATE syncml_anchors '
				. ' SET syncml_clientanchor = ' . $this->_db->quote($clientAnchorNext, 'text')
				. ', syncml_serveranchor = ' . $this->_db->quote($serverAnchorNext, 'text')
				. ' WHERE syncml_syncpartner = ' . $this->_db->quote($this->_syncDeviceID, 'text')
				. ' AND syncml_db = ' . $this->_db->quote($database, 'text')
				. ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text');
			$r = $this->_db->query($q);                   
			//Zend_Registry::get('log')->debug( "\n" . print_r($q, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$q \n");
			
		}
		return true;

	}

	/**
	 * Reads the previously written sync anchors from the database.
	 *
	 * @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.
	 */
	function readSyncAnchors($databaseURI)
	{
	
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI );
		$database = $this->_normalize($databaseURI);
        //Zend_Registry::get('log')->debug( "\n" . print_r($database, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$database \n");
                                    
		$q = 'SELECT syncml_clientanchor, syncml_serveranchor '
			. 'FROM syncml_anchors WHERE syncml_syncpartner = ' . $this->_db->quote($this->_syncDeviceID, 'text')
			. ' AND syncml_db = ' . $this->_db->quote($database, 'text')
			. ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text');
		$r = $this->_db->fetchRow($q);
		
		//Zend_Registry::get('log')->debug( "\n" . print_r($q, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$q \n");
		//Zend_Registry::get('log')->debug( "\n" . print_r($r, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$r \n");
		
		if( !is_array($r) )
			return false;

		return array($r['syncml_clientanchor'], $r['syncml_serveranchor']);
	
	}

	/**
	 * Creates a map entry to map between server and client IDs.
	 *
	 * If an entry already exists, it is overwritten.
	 *
	 * @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".
	 */
	function createUidMap($databaseURI, $cuid, $suid, $timestamp = 0)
	{
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI . ', $cuid: ' . $cuid . ', $suid: ' . $suid . ', $timestamp: ' . $timestamp );
		if( $timestamp == 0 )
			$timestamp = time();
		//	throw new Exception('WTF');
		$database = $this->_normalize($databaseURI);

		// Check if entry exists. If not insert, otherwise update.
		if( !$this->_getSuid($databaseURI, $cuid) )
		{
		                             
			$q = 'INSERT INTO syncml_map (syncml_syncpartner, '
			. 'syncml_db, syncml_uid, syncml_cuid, syncml_suid, '
			. 'syncml_timestamp) VALUES ('
			. $this->_db->quote($this->_syncDeviceID, 'text') . ', '
			. $this->_db->quote($database, 'text') . ', '
			. $this->_db->quote($this->_user, 'text') . ', '
			. $this->_db->quote($cuid, 'text') . ', '
			. $this->_db->quote($suid, 'text') . ', '
			. $this->_db->quote($timestamp, 'integer')
			. ')';
			//Zend_Registry::get('log')->debug( "\n" . print_r($q, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$q \n");
			
			$r = $this->_db->query($q);
		}
		else
		{
		                            
			$q = 'UPDATE syncml_map SET '
				. 'syncml_suid = ' . $this->_db->quote($suid, 'text')
				. ', syncml_timestamp = ' . $this->_db->quote($timestamp, 'text')
				. ' WHERE'
				. ' syncml_syncpartner = ' . $this->_db->quote($this->_syncDeviceID, 'text')
				. ' AND syncml_db = ' . $this->_db->quote($database, 'text')
				. ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text')
				. ' AND syncml_cuid = ' . $this->_db->quote($cuid, 'text');
			//Zend_Registry::get('log')->debug( "\n" . print_r($q, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$q \n");
				
			$r = $this->_db->query($q);
		
		}
		return true;
		
	}

	/**
	 * Retrieves the Server ID for a given Client ID from the map.
	 *
	 * @param string $databaseURI  URI of database to sync. Like calendar,
	 *							 tasks, contacts or notes. May include
	 *							 optional parameters:
	 *							 tasks?options=ignorecompleted.
	 * @param string $cuid		 The client ID.
	 *
	 * @return mixed  The server ID string or false if no entry is found.
	 */
	function _getSuid($databaseURI, $cuid)
	{

		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI . ', $cuid: ' . $cuid );
		$database = $this->_normalize($databaseURI);
        $q = 'SELECT `syncml_suid` FROM syncml_map '
			. ' WHERE syncml_syncpartner = ' . $this->_db->quote($this->_syncDeviceID, 'text')
			. ' AND syncml_db = ' . $this->_db->quote($database, 'text')
			. ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text')
			. ' AND syncml_cuid = ' . $this->_db->quote($cuid, 'text')
			. ' LIMIT 1';
		$r = $this->_db->fetchOne($q);
		//Zend_Registry::get('log')->debug( "\n" . print_r($q, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$q \n");
		//Zend_Registry::get('log')->debug( "\n" . print_r($r, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$r \n");
        
		if( !empty($r) )
			return $r;
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": No Suid found for $database:$cuid");
		return false;

	}

	/**
	 * Retrieves the Client ID for a given Server ID from the map.
	 *
	 * @param string $databaseURI  URI of database to sync. Like calendar,
	 *							 tasks, contacts or notes. May include
	 *							 optional parameters:
	 *							 tasks?options=ignorecompleted.
	 * @param string $suid		 The server ID.
	 *
	 * @return mixed  The client ID string or false if no entry is found.
	 */
	function _getCuid($databaseURI, $suid)
	{
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$database = $this->_normalize($databaseURI);
                                    
		$q = 'SELECT syncml_cuid FROM syncml_map '
		. ' WHERE syncml_syncpartner = ' . $this->_db->quote($this->_syncDeviceID, 'text')
		. ' AND syncml_db = ' . $this->_db->quote($database, 'text')
		. ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text')
		. ' AND syncml_suid = ' . $this->_db->quote($suid, 'text');
		$r = $this->_db->fetchOne($q);
	
		if( !empty($r) )
			return $r;
        
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ": No Cuid found for $database:$suid");
		return false;
		
	}

	/**
	 * Returns a timestamp stored in the map for a given Server ID.
	 *
	 * The timestamp is the timestamp of the last change to this server ID
	 * that was done inside a sync session (as a result of a change received
	 * by the server). It's important to distinguish changes in the backend a)
	 * made by the user during normal operation and b) changes made by SyncML
	 * to reflect client updates.  When the server is sending its changes it
	 * is only allowed to send type a). However the history feature in the
	 * backend my not know if a change is of type a) or type b). So the
	 * timestamp is used to differentiate between the two.
	 *
	 * @param string $databaseURI  URI of database to sync. Like calendar,
	 *							 tasks, contacts or notes. May include
	 *							 optional parameters:
	 *							 tasks?options=ignorecompleted.
	 * @param string $suid		 The server ID.
	 *
	 * @return mixed  The previously stored timestamp or false if no entry is
	 *				found.
	 */
	//$dbAdapter->getConnection()->exec($sql);
	
	function _getChangeTS($databaseURI, $suid)
	{

		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$database = $this->_normalize($databaseURI);
        $q = 'SELECT `syncml_timestamp` FROM `syncml_map` '
		. ' WHERE `syncml_syncpartner` = ' . $this->_db->quote($this->_syncDeviceID, 'text')
		. ' AND syncml_db = ' . $this->_db->quote($database, 'text')
		. ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text')
		. ' AND syncml_suid = ' . $this->_db->quote($suid, 'text')
		. ' LIMIT 1';
		$r = $this->_db->fetchOne($q);
		//Zend_Registry::get('log')->debug( "\n" . print_r($r, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$r \n");
		
		if( $r )
			return $r;
		return false;

	}

	/**
	 * 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.
	 *
	 * @param string $databaseURI  URI of database to sync. Like calendar,
	 *							 tasks, contacts or notes. May include
	 *							 optional parameters:
	 *							 tasks?options=ignorecompleted.
	 */
	function eraseMap($databaseURI)
	{
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$database = $this->_normalize($databaseURI);

		$r = $this->_db->query(
			'DELETE FROM syncml_map '
			. ' WHERE syncml_syncpartner = '
			. $this->_db->quote($this->_syncDeviceID, 'text')
			. ' AND syncml_db = '
			. $this->_db->quote($database, 'text')
			. ' AND syncml_uid = '
			. $this->_db->quote($this->_user, 'text'));
		if ($this->_checkForError($r)) {
			return $r;
		}

		$r = $this->_db->query(
			'DELETE FROM syncml_suidlist '
			. ' WHERE syncml_syncpartner = '
			. $this->_db->quote($this->_syncDeviceID, 'text')
			. ' AND syncml_db = '
			. $this->_db->quote($database, 'text')
			. ' AND syncml_uid = '
			. $this->_db->quote($this->_user, 'text'));
		if ($this->_checkForError($r)) {
			return $r;
		}

		return true;
	}

	/**
	 * Cleanup 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.
	 */
	function close()
	{

		parent::close();

	}


	/**
	 * Checks if the parameter is a PEAR_Error object and if so logs the
	 * error.
	 *
	 * @param mixed $o  An object or value to check.
	 *
	 * @return mixed  The error object if an error has been passed or false if
	 *				no error has been passed.
	 */
	function _checkForError($o)
	{
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		if( is_a($o, 'PEAR_Error') )
		{
			
			Zend_Registry::get('log')->debug('ERROR: ' . print_r($o,true));
			return $o;
			
		}
		return false;
		
	}

	/**
	 * Returns a list of item IDs that have been deleted since the last sync
	 * run and stores a complete list of IDs for next sync run.
	 *
	 * Some backend datastores don't keep information about deleted entries.
	 * So we have to create a workaround that finds out what entries have been
	 * deleted since the last sync run. This method provides this
	 * functionality: it is called with a list of all IDs currently in the
	 * database. It then compares this list with its own previously stored
	 * list of IDs to identify those missing (and thus deleted). The passed
	 * list is then stored for the next invocation.
	 *
	 * @param string $databaseURI  URI of database to sync. Like calendar,
	 *							 tasks, contacts or notes. May include
	 *							 optional parameters:
	 *							 tasks?options=ignorecompleted.
	 * @param array $currentSuids  Array of all SUIDs (primary keys) currently
	 *							 in the server datastore.
	 *
	 * @return array  Array of all entries that have been deleted since the
	 *				last call.
	 */
	function _trackDeletes($databaseURI, $currentSuids)
	{
		
		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI . ', $currentSuids: ' . print_r($currentSuids,true) );
		$database = $this->_normalize($databaseURI);
		if( !is_array($currentSuids) )
			$currentSuids = array();

		//Zend_Registry::get('log')->debug('_trackDeletes() with ' . count($currentSuids) . ' current ids');
                                                 
		$q = 'SELECT `syncml_suid` FROM syncml_suidlist '
			 . ' WHERE syncml_syncpartner = ' . $this->_db->quote($this->_syncDeviceID, 'text')
			 . ' AND syncml_db = ' . $this->_db->quote($database, 'text')
			 . ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text');
		$prevSuids = $this->_db->fetchCol($q);                     
		//Zend_Registry::get('log')->debug( "\n" . print_r($prevSuids, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$prevSuids \n");
		
        
		if( is_array($prevSuids) )
			$prevSuids = array_flip($prevSuids);
		else
			$prevSuids = array();

		//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' found ' . count($prevSuids) . ' items in prevlist');
		//Zend_Registry::get('log')->debug( "\n" . print_r($currentSuids, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$currentSuids \n");
		//Zend_Registry::get('log')->debug( "\n" . print_r($prevSuids, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$prevSuids \n");
		
		foreach( $currentSuids as $currentSuid )
		{
		
			if( isset($prevSuids[$currentSuid]) )
			{
		
				// Entry is there now and in $prevSuids. Unset in $prevSuids
				// array so we end up with only those in $prevSuids that are
				// no longer there now.
				unset($prevSuids[$currentSuid]);
		
			}
			else
			{
			
				// Entry is there now but not in $prevSuids. New entry, store
				// in syncml_suidlist    
				$q = 'INSERT INTO syncml_suidlist '
				. ' (syncml_syncpartner, syncml_db, syncml_uid, '
				. 'syncml_suid) VALUES ('
				. $this->_db->quote($this->_syncDeviceID, 'text') . ', '
				. $this->_db->quote($database, 'text') . ', '
				. $this->_db->quote($this->_user, 'text') . ', '
				. $this->_db->quote($currentSuid, 'text')
				. ')';
				$this->_db->query($q);
                //Zend_Registry::get('log')->debug( "\n" . print_r($q, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$q \n");
				//Zend_Registry::get('log')->debug( "\n" . print_r($r, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$r \n");

			}
			
		}

		// $prevSuids now contains the deleted suids. Remove those from
		// syncml_suidlist so we have a current list of all existing suids.
		foreach ($prevSuids as $suid => &$cuid)
		{
		
			$cuid = $this->_getCuid($databaseURI, $suid);
			$r = $this->_removeFromSuidList($databaseURI, $suid);
			$r = $this->_removeFromMap($databaseURI, $suid);
			//TODO: cleanup map as well ( will need to prefetch cuids here so available after deleting from map )
			
		}

		//Zend_Registry::get('log')->debug(__METHOD__ . ' #' . __LINE__ . ' found ' . count($prevSuids) . ' deleted items');
		//Zend_Registry::get('log')->debug( "\n" . print_r($prevSuids, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": to be deleted: \n");
		
		//Zend_Registry::get('log')->debug( "\n" . print_r($prevSuids, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$prevSuids \n");
        return $prevSuids;
		
	}

	/**
	 * Removes a suid from the suidlist.
	 *
	 * Called by _trackDeletes() when updating the suidlist and deleteEntry()
	 * when removing an entry due to a client request.
	 *
	 * @param string $databaseURI  URI of database to sync. Like calendar,
	 *							 tasks, contacts or notes. May include
	 *							 optional parameters:
	 *							 tasks?options=ignorecompleted.
	 * @param array $suid		  The suid to remove from the list.
	 */
	function _removeFromSuidList($databaseURI, $suid)
	{
		
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI . ', $suid: ' . $suid);
		$database = $this->_normalize($databaseURI);

		$r = $this->_db->query( 'DELETE FROM syncml_suidlist WHERE '
			. 'syncml_syncpartner = ' . $this->_db->quote($this->_syncDeviceID, 'text')
			. ' AND syncml_db = ' . $this->_db->quote($database, 'text')
			. ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text')
			. ' AND syncml_suid = ' . $this->_db->quote($suid, 'text'));

		return true;

	}

	function _removeFromMap($databaseURI, $suid)
	{
		
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ' $databaseURI: ' . $databaseURI . ', $suid: ' . $suid);
		$database = $this->_normalize($databaseURI);

		$r = $this->_db->query( 'DELETE FROM syncml_map WHERE '
			. 'syncml_syncpartner = ' . $this->_db->quote($this->_syncDeviceID, 'text')
			. ' AND syncml_db = ' . $this->_db->quote($database, 'text')
			. ' AND syncml_uid = ' . $this->_db->quote($this->_user, 'text')
			. ' AND syncml_suid = ' . $this->_db->quote($suid, 'text'));

		return true;

	}

	/**
	 * Creates a clean test environment in the backend.
	 *
	 * Ensures there's a user with the given credentials and an empty data
	 * store.
	 *
	 * @param string $user This user accout has to be created in the backend.
	 * @param string $pwd  The password for user $user.
	 */
	function testSetup($user, $pwd)
	{
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$this->_user = $user;
		$this->_cleanUser($user);
		$this->_backend->_user = $user;

		$r = $this->_db->query(
			'INSERT INTO syncml_uids (syncml_uid, syncml_password)'
			. ' VALUES ('
			. $this->_db->quote($user, 'text') . ', '
			. $this->_db->quote($pwd, 'text') . ')');
		
		
	}

	/**
	 * Prepares the test start.
	 *
	 * @param string $user This user accout has to be created in the backend.
	 */
	function testStart($user)
	{
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$this->_user = $user;
		$this->_backend->_user = $user;
	}

	/**
	 * Tears down the test environment after the test is run.
	 *
	 * Should remove the testuser created during testSetup and all its data.
	 */
	function testTearDown()
	{
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$this->_cleanUser($this->_user);
		$this->_db->disconnect();
	}

	/* Database access functions. The following methods are not part of the
	 * backend API. They are here to illustrate how a backend application
	 * (like a web calendar) has to modify the data with respect to the
	 * history. There are three functions:
	 * addEntry_backend(), replaceEntry_backend(), deleteEntry_backend().
	 * They are very similar to the API methods above, but don't use cuids or
	 * syncDeviceIDs as these are only relevant for syncing. */

	/**
	 * Adds an entry into the server database.
	 *
	 * @param string $user		 The username to use. Not strictly necessery
	 *							 to store this, but it helps for the test
	 *							 environment to clean up all entries for a
	 *							 test user.
	 * @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.
	 *
	 * @return array  PEAR_Error or suid of new entry.
	 */
	function addEntry_backend($user, $databaseURI, $content, $contentType)
	{
		
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$database = $this->_normalize($databaseURI);

		// Generate an id (suid). It's also possible to use a database
		// generated primary key here. */
		$suid = $this->_generateID();

		$created_ts = $this->getCurrentTimeStamp();
		$r = $this->_db->query(
			'INSERT INTO syncml_data (syncml_id, syncml_db, syncml_uid, '
			. 'syncml_data, syncml_contenttype, syncml_created_ts, '
			. 'syncml_modified_ts) VALUES ('
			. $this->_db->quote($suid, 'text') . ', '
			. $this->_db->quote($database, 'text') . ', '
			. $this->_db->quote($user, 'text') . ', '
			. $this->_db->quote($content, 'text') . ', '
			. $this->_db->quote($contentType, 'text') . ', '
			. $this->_db->quote($created_ts, 'integer') . ', '
			. $this->_db->quote($created_ts, 'integer')
			. ')');

		return $suid;

	}

	/**
	 * Replaces an entry in the server database.
	 *
	 * @param string $user		 The username to use. Not strictly necessery
	 *							 to store this but, it helps for the test
	 *							 environment to clean up all entries for a
	 *							 test user.
	 * @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 $suid		 Server ID of this entry.
	 *
	 * @return string  PEAR_Error or suid of modified entry.
	 */
	function replaceEntry_backend($user, $databaseURI, $content, $contentType,
								  $suid)
	{
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$database = $this->_normalize($databaseURI);
		$modified_ts = $this->getCurrentTimeStamp();

		// Entry exists: replace current one.
		$r = $this->_db->query(
			'UPDATE syncml_data '
			. 'SET syncml_modified_ts = '
			. $this->_db->quote($modified_ts, 'integer')
			. ',syncml_data = '
			. $this->_db->quote($content, 'text')
			. ',syncml_contenttype = '
			. $this->_db->quote($contentType, 'text')
			. 'WHERE syncml_db = '
			. $this->_db->quote($database, 'text')
			. ' AND syncml_uid = '
			. $this->_db->quote($user, 'text')
			. ' AND syncml_id = '
			. $this->_db->quote($suid, 'text'));
		if ($this->_checkForError($r)) {
			return $r;
		}

		return $suid;
	}

	/**
	 * Deletes an entry from the server database.
	 *
	 * @param string $user		 The username to use. Not strictly necessery
	 *							 to store this, but it helps for the test
	 *							 environment to clean up all entries for a
	 *							 test user.
	 * @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 ID of the entry.
	 *
	 * @return boolean  True on success or false on failed (item not found).
	 */
	function deleteEntry_backend($user, $databaseURI, $suid)
	{
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$database = $this->_normalize($databaseURI);

		$r = $this->_db->queryOne(
			'DELETE FROM syncml_data '
			. 'WHERE syncml_db = '
			. $this->_db->quote($database, 'text')
			. ' AND syncml_uid = '
			. $this->_db->quote($user, 'text')
			. ' AND syncml_id = '
			. $this->_db->quote($suid, 'text'));
		if ($this->_checkForError($r)) {
			return false;
		}

		return true;
	}

	function _cleanUser($user)
	{
		Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ );
		$r = $this->_db->query('DELETE FROM syncml_data WHERE syncml_uid = '
							  . $this->_db->quote($user, 'text'));
		$this->_checkForError($r);

		$r = $this->_db->query('DELETE FROM syncml_map WHERE syncml_uid = '
							  . $this->_db->quote($user, 'text'));
		$this->_checkForError($r);

		$r = $this->_db->query('DELETE FROM syncml_anchors WHERE syncml_uid = '
							  . $this->_db->quote($user, 'text'));
		$this->_checkForError($r);

		$r = $this->_db->query('DELETE FROM syncml_uids WHERE syncml_uid = '
							  . $this->_db->quote($user, 'text'));
		$this->_checkForError($r);

		$r = $this->_db->query('DELETE FROM syncml_suidlist WHERE syncml_uid = '
							  . $this->_db->quote($user, 'text'));
		$this->_checkForError($r);
	}

}
