/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2008-2009  Michael Bell <michael.bell@opensync.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include "data_sync_callbacks.h"
#include "libsyncml/syncml_internals.h"
#include "libsyncml/sml_support.h"
#include "libsyncml/sml_error_internals.h"
#include <string.h>
#include <strings.h>
#include "defines.h"
#include "data_sync_devinf.h"
#include "libsyncml/objects/sml_ds_server_internals.h"
#include "libsyncml/sml_manager_internals.h"

//#include "syncml_common.h"
//#include "syncml_callbacks.h"
//#include "syncml_vformat.h"
//#include "syncml_devinf.h"
//#include "syncml_ds_client.h"
//#include "syncml_ds_server.h"

/* **************************************** */
/* *****     Management Callbacks     ***** */
/* **************************************** */

void smlDataSyncEventCallback(
		SmlManager *manager,
		SmlManagerEventType type,
		SmlSession *session,
		SmlError *error,
		void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p)", __func__, manager, type, session, error, userdata);
	SmlDataSyncObject *dsObject = userdata;
	smlAssert(dsObject);
	GList *o = NULL;
	/* FIXME: Is this lock really needed? */
	/* FIXME: Who is allowed to call smlManagerDispatch? */
	/* FIXME: The SmlManager must be synchronized and not the callback. */
	g_mutex_lock(dsObject->managerMutex);

	switch (type) {
		case SML_MANAGER_SESSION_FLUSH:
		case SML_MANAGER_CONNECT_DONE:
			smlTrace(TRACE_INTERNAL, "%s: ignored event %d ", __func__, type);
			dsObject->internalState = SML_DATA_SYNC_STATE_CONNECTED;
			break;
		case SML_MANAGER_SESSION_ESTABLISHED:
			smlTrace(TRACE_INTERNAL, "%s: session established", __func__);
			dsObject->internalState = SML_DATA_SYNC_STATE_SESSION_READY;
			smlDataSyncSendEvent(
				dsObject, SML_DATA_SYNC_EVENT_CONNECT,
				dsObject->eventUserdata, NULL);
			break;
		case SML_MANAGER_DISCONNECT_DONE:
			smlTrace(TRACE_INTERNAL, "%s: connection with device has ended", __func__);
			/* If the transport was never connected
			 * then it is useless to handle the disconnect
			 * event.
			 */
			if (dsObject->internalState < SML_DATA_SYNC_STATE_CONNECTED)
			{
				smlTrace(TRACE_INTERNAL,
					"%s: ignored disconnect because never connected",
					__func__);
				break;
			}
			/* anchor caching is optional */
			if (dsObject->setAnchorCallback)
			{
				o = dsObject->datastores;
				for (; o; o = o->next) {
					SmlDataSyncDatastore *datastore = o->data;
					/* write new sync anchors */
					char *anchor = g_strdup_printf(
								"localanchor%s",
								 smlDsSessionGetLocation(datastore->session));
					if (!dsObject->setAnchorCallback(
							dsObject,
							anchor, datastore->localNext,
							dsObject->setAnchorUserdata,
							&error))
						goto error;
					anchor = g_strdup_printf(
								"remoteanchor%s",
								 smlDsSessionGetLocation(datastore->session));
					if (!dsObject->setAnchorCallback(
							dsObject,
							anchor, datastore->remoteNext,
							dsObject->setAnchorUserdata,
							&error))
						goto error;
				}
			}

			dsObject->internalState = SML_DATA_SYNC_STATE_DISCONNECTED;
			smlDataSyncSendEvent(
				dsObject, SML_DATA_SYNC_EVENT_DISCONNECT,
				dsObject->eventUserdata, NULL);
			smlDataSyncSendEvent(
				dsObject, SML_DATA_SYNC_EVENT_FINISHED,
				dsObject->eventUserdata, NULL);
			break;
		case SML_MANAGER_TRANSPORT_ERROR:
			smlTrace(TRACE_INTERNAL,
				"There was an error in the transport: %s",
				smlErrorPrint(&error));
			if (SML_DATA_SYNC_STATE_CONNECTED <= dsObject->internalState &&
			    dsObject->internalState < SML_DATA_SYNC_STATE_DISCONNECTED) {
				if (dsObject->internalState < SML_DATA_SYNC_STATE_DISCONNECT_IN_PROGRESS) {
					dsObject->internalState = SML_DATA_SYNC_STATE_DISCONNECT_IN_PROGRESS;
					SmlError *locerror = NULL;
					SmlLink *link_ = smlManagerSessionGetLink(
								dsObject->manager,
								dsObject->session,
								&locerror);
					if (link_ || !locerror)
						smlTransportDisconnect(
							dsObject->tsp,
							link_,
							&locerror);
					if (link_)
						smlLinkDeref(link_);
					/* error is already tracked.
					 * So locerror can be ignored.
					 */
					if (locerror)
						smlErrorDeref(&locerror);
					/* It is not a good idea to wait for the
					 * disconnect here. First this is an
					 * asynchronous software so it is always
					 * bad if the software blocks. Second it
					 * is dangerous to call smlManagerDispatch
					 * here because an error during these
					 * dispatch activities can lead to another
					 * error which overwrites the original
					 * error.
					 *
					 * Deadlock must be handled in another way.
					 * The SyncML protocol is usually already
					 * broken if this happens (TRANSPORT_ERROR).
					 *
					 * So yes, it is important to disconnect
					 * and no, it must not run dispatch.
					 */
				} else {
					/* disconnect failed */
					dsObject->internalState = SML_DATA_SYNC_STATE_DISCONNECTED;
				}
			}
			goto error;
			break;
		case SML_MANAGER_SESSION_NEW:
			smlTrace(TRACE_INTERNAL, "%s: Just received a new session with ID %s",
				__func__, VA_STRING(smlSessionGetSessionID(session)));
			if (dsObject->session) {
				smlTrace(TRACE_INTERNAL,
					"%s: WARNING: There was an old session %s in the environment.",
					__func__, VA_STRING(smlSessionGetSessionID(dsObject->session)));
				smlSessionUnref(dsObject->session);
				dsObject->session = NULL;
			}
			smlSessionUseStringTable(session, dsObject->useStringTable);
			smlSessionUseOnlyReplace(session, dsObject->onlyReplace);
			smlSessionUseNumberOfChanges(session, dsObject->useNumberOfChanges);

			smlTrace(TRACE_INTERNAL, "%s: maxObjSize %d",
				__func__, dsObject->maxObjSize);
			
			dsObject->session = session;
			smlSessionRef(session);

			/* authentication management for OMA DS clients*/
			if (dsObject->dsType == SML_SESSION_TYPE_CLIENT &&
			    (dsObject->username || dsObject->password))
			{
				/* prepare credential */
				SmlCred *cred = smlCredNewAuth(dsObject->authType,
							dsObject->username, dsObject->password,
							&error);
				if (!cred)
					goto error;
				smlSessionRegisterCred(dsObject->session, cred);
				smlTrace(TRACE_INTERNAL, "%s: credential initialized", __func__);
			}

			break;
		case SML_MANAGER_SESSION_FINAL:
			smlTrace(TRACE_INTERNAL, "%s: Session %s reported final",
				__func__, VA_STRING(smlSessionGetSessionID(session)));

			/* determine which package was received */

			if (dsObject->dsType == SML_SESSION_TYPE_CLIENT)
			{
				/* only devinf receiving is not supported */
				if (dsObject->actualPackage < SML_PACKAGE_1)
					dsObject->actualPackage = SML_PACKAGE_1;
				else
					dsObject->actualPackage += 2;
			} else { // SML_SESSION_TYPE_SERVER
				/* only devinf receiving is not supported */
				if (dsObject->actualPackage < SML_PACKAGE_2)
					dsObject->actualPackage = SML_PACKAGE_2;
				else
					dsObject->actualPackage += 2;
			}
			smlTrace(TRACE_INTERNAL, "%s: package == %d", __func__, dsObject->actualPackage);

			/* start callbacks etc. according to state */

			switch(dsObject->actualPackage)
			{
				case SML_PACKAGE_1: /* SAN received by client */
					/* This is the best position to check for
					 * the availability of the remote device
					 * information. If it is not present then
					 * the own device information is send and
					 * the remote ones is requested.
					 */
					if (!smlDataSyncManageDevInf(dsObject, TRUE, &error))
						goto error;
       					if (!smlSessionFlush(dsObject->session, TRUE, &error))
						goto error;
					break;
				case SML_PACKAGE_2: /* alerts received by server */
					/* This is the best position to check for
					 * the availability of the remote device
					 * information. If it is not present then
					 * the own device information is send and
					 * the remote ones is requested.
					 */
					if (!smlDataSyncManageDevInf(dsObject, TRUE, &error))
						goto error;
					
       					if (!smlSessionFlush(dsObject->session, TRUE, &error))
						goto error;

					smlDataSyncSendEvent(
						dsObject, SML_DATA_SYNC_EVENT_GOT_ALL_ALERTS,
						dsObject->eventUserdata, NULL);
					break;
				case SML_PACKAGE_3: /* alerts received by client */
					smlDataSyncSendEvent(
						dsObject, SML_DATA_SYNC_EVENT_GOT_ALL_ALERTS,
						dsObject->eventUserdata, NULL);
					break;
				case SML_PACKAGE_4: /* syncs received by server */
					smlDataSyncSendEvent(
						dsObject, SML_DATA_SYNC_EVENT_GOT_ALL_CHANGES,
						dsObject->eventUserdata, NULL);
					break;
				case SML_PACKAGE_5: /* syncs received by client */
					if (!smlDataSyncSendMap(dsObject, &error))
						goto error;
       					if (!smlSessionFlush(dsObject->session, TRUE, &error))
						goto error;
					smlDataSyncSendEvent(
						dsObject, SML_DATA_SYNC_EVENT_GOT_ALL_CHANGES,
						dsObject->eventUserdata, NULL);
					break;
				case SML_PACKAGE_6: /* map received by server */
       					if (!smlSessionFlush(dsObject->session, TRUE, &error))
						goto error;
					break;
				case SML_PACKAGE_END: /* end received by client */
					/* everything done */
					/* auto disconnect by library */
					break;
				default:
					smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED,
						"The package %d is actually not supported.",
						dsObject->actualPackage);
					goto error;
			}
			break;
		case SML_MANAGER_SESSION_END:
			smlTrace(TRACE_INTERNAL, "%s: Session %s has ended",
				__func__, VA_STRING(smlSessionGetSessionID(session)));
			SmlLink *link_ = smlManagerSessionGetLink(
							dsObject->manager,
							dsObject->session,
							&error);
			if (!link_ && error)
				goto error;
			/* OBEX is a stateful protocol and the client should
			 * init the disconnect. The problem is what happens
			 * when the client hangs?
			 *
			 * if (dsObject->tspType != SML_TRANSPORT_OBEX_SERVER &&
			 *     !smlTransportDisconnect(dsObject->tsp, link, &error))
			 */
			if (!smlTransportDisconnect(dsObject->tsp, link_, &error))
			{
				if (link_)
					smlLinkDeref(link_);
				goto error;
			}
			if (link_)
				smlLinkDeref(link_);
			break;
		case SML_MANAGER_SESSION_ERROR:
			if (session)
				smlTrace(TRACE_INTERNAL,
					"There was an error in the session %s: %s",
					VA_STRING(smlSessionGetSessionID(session)),
					smlErrorPrint(&error));
			else
				smlTrace(TRACE_INTERNAL,
					"There was a general error in the manager. %s",
					smlErrorPrint(&error));

			goto error;
			break;
		case SML_MANAGER_SESSION_WARNING:
			g_warning("WARNING: %s\n", smlErrorPrint(&error));
			break;
		default:
			g_warning("%s: Unknown event received: %d.", __func__, type);
			break;
	}
	
	g_mutex_unlock(dsObject->managerMutex);
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
	
error:
	g_mutex_unlock(dsObject->managerMutex);
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
	smlDataSyncSendEvent(
		dsObject, SML_DATA_SYNC_EVENT_ERROR,
		dsObject->eventUserdata, error);
}

/* *************************************** */
/* *****     DsSession Callbacks     ***** */
/* *************************************** */

void smlDataSyncDatastoreConnectCallback(
		SmlDsSession *dsession,
		void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsession, userdata);
	smlAssert(dsession);
	smlAssert(userdata);

	SmlDataSyncDatastore *datastore = userdata;
	SmlDataSyncObject *dsObject = datastore->dsObject;

	/* The session is required here. So if the session is missing
	 * then the function has to block here. This is no real problem
	 * because the SESSION_EVENT_NEW must already be in the queue of
	 * the manager object.
	 */
	while (!dsObject->session) {
		smlManagerDispatch(dsObject->manager);
	}

	/* set callbacks if the DsSession was not ready before */
	if (!datastore->session || datastore->session != dsession)
	{
		smlTrace(TRACE_INTERNAL, "%s: should be an OMA DS server", __func__);
		smlAssert(dsObject->funcDatastoreAlert);
		datastore->session = dsession;
		smlDsSessionGetAlert(datastore->session, dsObject->funcDatastoreAlert, datastore);
		if (dsObject->changeCallback) {
			smlDsSessionGetSync(datastore->session,
				smlDataSyncSyncCallback, datastore);
			smlDsSessionGetChanges(datastore->session,
				smlDataSyncChangeCallback, datastore);
			smlDsSessionGetMapping(datastore->session,
				smlDataSyncMappingCallback, datastore);
		}
		smlDsSessionRef(dsession);
	}

	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

#if 0 /* UNUSED */
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
	smlDataSyncSendEvent(
		dsObject, SML_DATA_SYNC_EVENT_ERROR,
		dsObject->eventUserdata, error);
#endif /* UNUSED */
}


/* *********************************** */
/* *****     Alert Callbacks     ***** */
/* *********************************** */

void smlDataSyncAlertStatusCallback(
		SmlSession *session,
		SmlStatus *status,
		void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	
	SmlDataSyncDatastore *datastore = userdata;

	/* If we talk as an OMA DS client with server like an OCS
	 * then it can happen that this server denies the alert
	 * because of an internal problem.
	 * Example OCS: If there is an error inside of an SyncML session
	 *              then you must wait a configured time before you
	 *              can again sucessfully connect this server.
	 *              Typically the server responds with error 503.
	 */
	unsigned int code = smlStatusGetCode(status);
	if (code >= 300 && code != SML_ERROR_REQUIRE_REFRESH)
	{
		/* This is an error. */
		SmlError *error = NULL;
		smlErrorSet(&error, SML_ERROR_GENERIC,
			"The alert response signals an error - %d.", code);
		smlErrorRef(&error);
		smlDataSyncSendEvent(
			datastore->dsObject, SML_DATA_SYNC_EVENT_ERROR,
			datastore->dsObject->eventUserdata, error);
		smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
		smlErrorDeref(&error);
	} else {
		smlTrace(TRACE_EXIT, "%s", __func__);
	}
}

/* ********************************** */
/* *****     Sync Callbacks     ***** */
/* ********************************** */

void smlDataSyncSyncCallback(
		SmlDsSession *dsession,
		unsigned int numchanges,
		void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, dsession, numchanges, userdata);
	SmlDataSyncDatastore *datastore = userdata;
	SmlError *error = NULL;

	/* If the device information was not sent together with the
	 * alerts and it was not cached then the device information
	 * can be received together with the sync command(s).
	 * This can happen with OMA DS clients and servers.
	 */
	if (!smlDataSyncManageDevInf(datastore->dsObject, FALSE, &error))
		goto error;

	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
	smlDataSyncSendEvent(
		datastore->dsObject, SML_DATA_SYNC_EVENT_ERROR,
		datastore->dsObject->eventUserdata, error);
}

void smlDataSyncSyncStatusCallback(
		SmlSession *session,
		SmlStatus *status,
		void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, session, status, userdata);
	SmlDataSyncDatastore *datastore = userdata;
	
	if (smlStatusGetClass(status) != SML_ERRORCLASS_SUCCESS)
	{
		// inform user
		smlTrace(TRACE_INTERNAL, "%s: The synchronisation request failed.", __func__);
		smlTrace(TRACE_INTERNAL, "%s: Location => %s", __func__, VA_STRING(datastore->dsObject->url));
		smlTrace(TRACE_INTERNAL, "%s: Database => %s", __func__, VA_STRING(datastore->sourceUri));
		smlTrace(TRACE_INTERNAL, "%s: Error => %d", __func__, smlStatusGetCode(status));
		if (smlStatusGetCode(status) == SML_ERROR_SERVICE_UNAVAILABLE &&
		    (strstr(datastore->dsObject->url, "ocst") ||
		     strstr(datastore->dsObject->url, "ocas")))
		{
			/* this is a potential Oracle Collaboration Suite */
			/* typical errorcode from OCS if there is something wrong */
			smlTrace(TRACE_INTERNAL,
				"%s: Oracle Collaboration Suite detected.",
				__func__);
			smlTrace(TRACE_INTERNAL,
				"%s: Typical undefined error from OCS (503 - SyncML timeout error).",
				__func__);
			smlTrace(TRACE_INTERNAL,
				"%s: Please wait 5 minutes before retry - default session timeout.",
				__func__);
		}
		// stop session
		// FIXME: this is not available in a clean way today
		// FIXME: we need a session state
		// FIXME: osync must be signalled
		// FIXME: we need a mutex lock on database->env
		// smlSessionEnd(database->env->session, NULL);
		// printf("    Session finished.\n");
		// smlManagerSessionRemove(database->env->manager, database->env->session);
		// smlManagerStop(database->env->manager);
		// smlManagerQuit(database->env->manager);
		// printf("    Manager finished.\n");
		SmlError *error = NULL;
		smlErrorSet(&error, smlStatusGetCode(status),
			"Sync failed with error %d.",
			smlStatusGetCode(status));
		smlErrorRef(&error);
		smlDataSyncSendEvent(
			datastore->dsObject, SML_DATA_SYNC_EVENT_ERROR,
			datastore->dsObject->eventUserdata, error);
		smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
		smlErrorDeref(&error);
	} else {
		smlTrace(TRACE_EXIT, "%s", __func__);
	}
}

/* ************************************ */
/* *****     Change Callbacks     ***** */
/* ************************************ */

SmlBool smlDataSyncChangeCallback(
		SmlDsSession *dsession,
		SmlChangeType type,
		const char *uid,
		char *data,
		unsigned int size,
		const char *contenttype,
		void *userdata,
		SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %p, %i, %s, %p, %p)", __func__, dsession, type, VA_STRING(uid), data, size, VA_STRING(contenttype), userdata, error);
	CHECK_ERROR_REF

	SmlDataSyncDatastore *datastore = userdata;

	smlAssert(type);
	smlAssert(datastore);
	smlAssert(datastore->dsObject);
	smlAssert(datastore->dsObject->changeCallback);

	/* some mobiles sends replace commands during slow-sync */
	if (datastore->alertType == SML_ALERT_SLOW_SYNC &&
	    type == SML_CHANGE_REPLACE)
		type = SML_CHANGE_ADD;

	/* decode base64 data if necessary */
	size_t appClassLength = ((size_t) index(datastore->contentType, '/')) -
	                        ((size_t) datastore->contentType);
	if ( ( strstr(datastore->contentType, SML_CONTENT_TYPE_APPLICATION) == datastore->contentType &&
	       appClassLength == strlen(SML_CONTENT_TYPE_APPLICATION) ) ||
	     ( strstr(datastore->contentType, SML_CONTENT_TYPE_AUDIO) == datastore->contentType &&
	       appClassLength == strlen(SML_CONTENT_TYPE_AUDIO) ) ||
	     ( strstr(datastore->contentType, SML_CONTENT_TYPE_IMAGE) == datastore->contentType &&
	       appClassLength == strlen(SML_CONTENT_TYPE_IMAGE) ) ||
	     ( strstr(datastore->contentType, SML_CONTENT_TYPE_MESSAGE) == datastore->contentType &&
	       appClassLength == strlen(SML_CONTENT_TYPE_MESSAGE) ) ||
	     ( strstr(datastore->contentType, SML_CONTENT_TYPE_VIDEO) == datastore->contentType &&
	       appClassLength == strlen(SML_CONTENT_TYPE_AUDIO) ) )
	{
		/* binary data must be base64 encoded */
		char *b64data = data;
		size_t length = 0;
		data = (char *) g_base64_decode(b64data, &length);
		size = length;
		smlSafeCFree(&b64data);
		if (!data) {
			smlErrorSet(error, SML_ERROR_GENERIC,
			            "The base 64 decoding of glib failed.");
			goto error;
		}
	}

	/* perform callback */
	if (!datastore->dsObject->changeCallback(
			datastore->dsObject, datastore->sourceUri,
			type, uid, data, size,
			datastore->dsObject->changeUserdata,
			error))
		goto error;

	/* if this is a client then the callback should add a mapping */

	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
	return FALSE;
}

void smlDataSyncChangeStatusCallback(
		SmlDsSession *dsession,
		SmlStatus *status,
		const char *newuid,
		void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, dsession, status, VA_STRING(newuid), userdata);
	SmlError *error = NULL;

	smlAssert(userdata);
	SmlDataSyncChange *change = userdata;
	SmlDataSyncDatastore *datastore = change->datastore;
	SmlDataSyncObject *dsObject = datastore->dsObject;

	smlAssert(dsObject);
	if (dsObject->changeStatusCallback &&
	    !dsObject->changeStatusCallback(
				dsObject,
				smlStatusGetCode(status),
				newuid,
				change->userdata, &error))
		goto error;

	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
error:
	smlErrorRef(&error);
	smlDataSyncSendEvent(
		dsObject, SML_DATA_SYNC_EVENT_ERROR,
		dsObject->eventUserdata, error);
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
	smlErrorDeref(&error);
}

void smlDataSyncMappingCallback(
		SmlDsSession *dsession,
		SmlLocation *orig,
		SmlLocation *newuid,
		void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, dsession, VA_STRING(smlLocationGetURI(orig)), VA_STRING(smlLocationGetURI(newuid)), userdata);

	SmlError *error = NULL;
	SmlDataSyncDatastore *datastore = userdata;

	smlAssert(datastore);
	smlAssert(datastore->dsObject);
	smlAssert(datastore->dsObject->mappingCallback);

	/* perform callback */
	if (!datastore->dsObject->mappingCallback(
			datastore->dsObject, datastore->sourceUri,
			smlLocationGetURI(orig),
			smlLocationGetURI(newuid),
			datastore->dsObject->mappingUserdata,
			&error))
		goto error;

	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
error:
	smlErrorRef(&error);
	smlDataSyncSendEvent(
		datastore->dsObject, SML_DATA_SYNC_EVENT_ERROR,
		datastore->dsObject->eventUserdata, error);
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
	smlErrorDeref(&error);
}

/* ********************************* */
/* *****     Map Callbacks     ***** */
/* ********************************* */

void smlDataSyncMapStatusCallback(
		SmlSession *session,
		SmlStatus *status,
		void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	if (smlStatusGetClass(status) != SML_ERRORCLASS_SUCCESS)
	{
		SmlDataSyncDatastore *datastore = userdata;
		SmlError *error = NULL;
		smlErrorSet(&error, smlStatusGetCode(status),
			"Map of datastore %s was rejected with error code %d",
			datastore->sourceUri, smlStatusGetCode(status));
		smlDataSyncSendEvent(
			datastore->dsObject, SML_DATA_SYNC_EVENT_ERROR,
			datastore->dsObject->eventUserdata, error);
	}
	smlTrace(TRACE_EXIT, "%s", __func__);
}

/* ******************************************* */
/* *****     Authentication Callback     ***** */
/* ******************************************* */

SmlBool smlDataSyncVerifyUserCallback(
                SmlChal *chal,
                SmlCred *cred,
                const char *username,
                void *userdata,
                SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, chal, cred, VA_STRING(username), userdata);
	CHECK_ERROR_REF

	SmlDataSyncObject *dsObject = userdata;

	/* We have only one user and not a whole user database. */
	smlTrace(TRACE_EXIT, "%s", __func__);
	return smlAuthVerify(chal, cred, dsObject->username, dsObject->password, error);
}

