/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2005  Armin Bauer <armin.bauer@opensync.org>
 * Copyright (C) 2008  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 <stdio.h>
#include <glib.h>
#include <libsyncml/syncml.h>

#include <config.h>

#include <libsyncml/sml_support.h>

#include <libsyncml/objects/sml_auth.h>
#include <libsyncml/objects/sml_devinf_obj.h>
#include <libsyncml/objects/sml_ds_server.h>

#include <glib.h>
#include <stdlib.h>
#include <string.h>

#ifdef ENABLE_OBEX
#include <openobex/obex.h>
#endif

#include <errno.h>

GList *sessions = NULL;
SmlManager *manager = NULL;
SmlDevInfAgent *agent = NULL;
GMainLoop *loop = NULL;

GList *dbLocations = NULL;
GList *dbSlow = NULL;
GList *dbTypes = NULL;

gboolean onlyInfo = FALSE;
char *useTimeAnchor = NULL;
unsigned int recvLimit = 65535;
unsigned int maxObjSize = 3000000;

static void usage (char *name, int ecode)
{
	fprintf (stderr, "Usage: %s\n\n", name);
	fprintf (stderr, "--sync <type> <path>\t\tEmulate a database of the given type on the url.\n");
	fprintf (stderr, "--slow-sync <type> <path>\tEmulate a database of the given type on the url and use slow-sync\n");
	fprintf (stderr, "\t\t\t\tType should be a IANA registered mimetype or your own type. Common types are:\n");
	fprintf (stderr, "\t\t\t\t\t\"text/x-vcard\" for contacts, \"text/x-vcalendar\" for events, \"text/plain\" for\n");
	fprintf (stderr, "\t\t\t\t\tnotes and \"text/x-vMessage\" for SMS\n");
	fprintf (stderr, "\t\t\t\tPath is the local name of the database. You can choose something there\n\n");
	fprintf (stderr, "[-s <device>]\t\tConnect to the serial device\n\n");
	fprintf (stderr, "[-u <id>]\t\t\tConnect to the given usb interface number\n");
	fprintf (stderr, "\t\t\t\tIf you dont specify an id, all available interface will be listed\n\n");
	fprintf (stderr, "[-b <addr> <channel>]\t\tConnect to the given bluetooth device\n\n");
	fprintf (stderr, "[--identifier <name>]\t\tUse the given identifier in the initial alert.\n");
	fprintf (stderr, "\t\t\t\tSome devices require a special string here. Nokias for example require \"PC Suite\"\n\n");
	fprintf (stderr, "[--version <version>]\t\tSet the given version. <version> can be \"1.0\", \"1.1\" or \"1.2\"\n");
	fprintf (stderr, "\t\t\t\tThe default is \"1.1\"\n\n");
	fprintf (stderr, "--add <type> <path>\t\tAdd the file given in path to the device as the given type\n");
	fprintf (stderr, "\t\t\t\tType should be a IANA registered mimetype or your own type. Common types are:\n");
	fprintf (stderr, "\t\t\t\t\t\"text/x-vcard\" for contacts, \"text/x-vcalendar\" for events, \"text/plain\" for\n");
	fprintf (stderr, "\t\t\t\t\tnotes and \"text/x-vMessage\" for SMS\n");
	fprintf (stderr, "\t\t\t\tPath to the file to add. The file has to be a VCard, VCalendar, etc.\n\n");
	fprintf (stderr, "[--wbxml]\t\t\tUse wbxml (WAP Binary XML) instead of plain xml\n\n");
	fprintf (stderr, "[--recvLimit <limit>]\t\t\tLimit the size of the receiving buffer to this size (default: %d)\n\n", recvLimit);
	fprintf (stderr, "[--maxObjSize <limit>]\t\t\tThe maximum size of a object that we can receive (default: %d)\n\n", maxObjSize);
	fprintf (stderr, "[--useStringTable]\t\t\tUse wbxml string tables (Improves transmission size, but not supported by some phones)\n\n");
	fprintf (stderr, "[--disableNumberOfChanges]\t\t\tDisable NumberOfChanges feature (the most new phones need it) (default: enabled)\n\n");
	fprintf (stderr, "[--useTimeAnchor]\t\t\tUse timestamps as anchors (only for new Nokia series 40 phones)\n\n");
	fprintf (stderr, "[--dumpinfo]\t\t\tPrint info about the phone at the end which can be sent to the developers\n");
	exit (ecode);
}

static gboolean _recv_change(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);
	
	printf("Received a ");
	
	switch (type) {	
		case SML_CHANGE_ADD:
			printf("added entry");
			break;
		case SML_CHANGE_REPLACE:
			printf("modified entry");
			break;
		case SML_CHANGE_DELETE:
			printf("deleted entry");
			break;
		default:
			;
	}
	
	printf(" %s of size %i and type %s\n", uid, size, contenttype);
	printf("\t\tData: %s\n", data);
	
	g_free(data);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
}

static void _recv_sync_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	
	printf("Received an reply to our Sync: %i\n", smlStatusGetCode(status));
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void _add_reply(SmlDsSession *session, SmlStatus *status, const char *newuid, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, session, status, VA_STRING(newuid), userdata);
	
	printf("Received an reply to our Add command: %i. The new uid is %s\n", smlStatusGetCode(status), newuid);

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

GList *types = NULL;
GList *paths = NULL;

static void _recv_sync(SmlDsSession *dsession, unsigned int numchanges, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, dsession, numchanges, userdata);
	SmlError *error = NULL;
	
	printf("Going to receive %i changes\n", numchanges);
	
	GList *t = types;
	int num = 0;
	while (t) {
		if (!strcmp(smlDsSessionGetContentType(dsession), t->data))
			num++;
		t = t->next;
	}
	
	if (!onlyInfo) {
		if (!smlDsSessionSendSync(dsession, num, _recv_sync_reply, NULL, &error))
			goto error;
		
		t = types;
		GList *p = paths;
		while (t) {
			
			if (!strcmp(smlDsSessionGetContentType(dsession), t->data)) {
				char *filename = (char *)p->data;
				printf("Adding file %s\n", filename);
				
				GError *gerror = NULL;
				gsize sz = 0;
				char *data = NULL;
				
				GIOChannel *chan = g_io_channel_new_file(filename, "r", &gerror);
				if (!chan) {
					printf("Unable to open file %s for reading: %s\n", filename, gerror->message);
				} else {
					g_io_channel_set_encoding(chan, NULL, NULL);
					if (g_io_channel_read_to_end(chan, &data, &sz, &gerror) != G_IO_STATUS_NORMAL) {
						printf("Unable to read contents of file %s: %s\n", filename, gerror->message);
					} else {
						if (!smlDsSessionQueueChange(dsession, SML_CHANGE_ADD, filename, data, (int)sz, t->data, _add_reply, NULL, NULL))
							printf("Unable to queue change\n");
					}
					g_io_channel_shutdown(chan, FALSE, NULL);
					g_io_channel_unref(chan);
				}
			}
			
			t = t->next;
			p = p->next;
		}
		
		if (!smlDsSessionCloseSync(dsession, &error))
			goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
	smlErrorDeref(&error);
}

static void _recv_alert_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	
	printf("Received an reply to our Alert\n");
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static gboolean _recv_alert(SmlDsSession *dsession, SmlAlertType type, const char *last, const char *next, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %s, %p)", __func__, dsession, type, VA_STRING(last), VA_STRING(next), userdata);
	SmlError *error = NULL;
	const char *anchor = next;
	if (useTimeAnchor)
		anchor = useTimeAnchor;
	
	printf("Received an Alert for the DS Server at %s: Type: %i, Last %s, Next %s\n", smlDsSessionGetLocation(dsession), type, last, next);
	
	gboolean slow = FALSE;
	int i = 0;
	for (i = 0; i < g_list_length(dbTypes); i++) {
		if (!strcmp(smlDsSessionGetContentType(dsession), g_list_nth_data(dbTypes, i))) {
			slow = GPOINTER_TO_INT(g_list_nth_data(dbSlow, i));
			break;
		}
	}
	
	if (onlyInfo) {
		if (!smlDsSessionSendAlert(dsession, SML_ALERT_ONE_WAY_FROM_SERVER_BY_SERVER, last, next, _recv_alert_reply, NULL, &error))
			goto error;
	} else if (slow && type != SML_ALERT_SLOW_SYNC) {
		/* SLOW-SYNC enforced by tool */
		printf("Enforcing SLOW-SYNC\n");
		if (!smlDsSessionSendAlert(dsession, SML_ALERT_SLOW_SYNC, NULL, anchor, _recv_alert_reply, NULL, &error))
			goto error;

		smlTrace(TRACE_EXIT, "%s: Slow syncing", __func__);
		return FALSE;
	} else {
		/* We only support SLOW-SYNC and TWO-WAY-SYNC */
		/* FIXME: Why do we use alert 206? */
		/* FIXME: Usually 200 and 201 are the correct alert codes. */
		/* FIXME: 206 is only useful in SANs */
		if (type != SML_ALERT_SLOW_SYNC &&
		    type != SML_ALERT_TWO_WAY &&
		    type != SML_ALERT_TWO_WAY_BY_SERVER)
		{
			smlErrorSet(&error, SML_ERROR_GENERIC, "Unsupported alert type %d.", type);
			goto error;
		}
		if (!smlDsSessionSendAlert(dsession, type, last, anchor, _recv_alert_reply, NULL, &error))
			goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	//smlDsSessionSetError(dsession, error);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
	smlErrorDeref(&error);
	return TRUE;
}

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

	smlDsSessionGetAlert(dsession, _recv_alert, NULL);
	smlDsSessionGetSync(dsession, _recv_sync, NULL);
	smlDsSessionGetChanges(dsession, _recv_change, NULL);
	
	smlDsSessionRef(dsession);
	
	sessions = g_list_append(sessions, dsession);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

gboolean dumpinfo = FALSE;
gboolean useStringTable = FALSE;
gboolean useNumberOfChanges = TRUE;
gboolean useLargeObjs = TRUE;
SmlProtocolVersion sessionVersion = SML_VERSION_UNKNOWN;
SmlDevInf *deviceDevinf = NULL;
SmlDevInf *devinf = NULL;

static void _manager_event(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);
	SmlError *locerror = NULL;
	
	switch (type) {
		case SML_MANAGER_CONNECT_DONE:
			printf("connection with device succeeded\n");
			break;
		case SML_MANAGER_SESSION_ESTABLISHED:
			printf("session incl. authentication successfully established\n");
			break;
		case SML_MANAGER_DISCONNECT_DONE:
			printf("connection with device has ended\n");
			break;
		case SML_MANAGER_TRANSPORT_ERROR:
			printf("Received an transport error: %s\n", smlErrorPrint(&error));
			smlManagerQuit(manager);
			g_main_loop_quit(loop);
			break;
		case SML_MANAGER_SESSION_NEW:
			printf("Just received a new session with ID %s\n", smlSessionGetSessionID(session));
			sessionVersion = smlSessionGetVersion(session);

			if (smlSessionGetRemoteMaxMsgSize(session) > recvLimit &&
			    recvLimit > 0)
				recvLimit = smlSessionGetRemoteMaxMsgSize(session);

			if (recvLimit)
				smlSessionSetLocalMaxMsgSize(session, recvLimit);
				
			if (smlSessionGetRemoteMaxObjSize(session) > maxObjSize &&
			    maxObjSize > 0)
				maxObjSize = smlSessionGetRemoteMaxObjSize(session);

			if (maxObjSize)
				smlSessionSetLocalMaxObjSize(session, maxObjSize);
				
			if (useStringTable)
				smlSessionUseStringTable(session, TRUE);
				
			smlSessionUseNumberOfChanges(session, useNumberOfChanges);
			smlSessionUseLargeObjects(session, useLargeObjs);

			break;
		case SML_MANAGER_SESSION_FINAL:
			if (!deviceDevinf && (deviceDevinf = smlDevInfAgentGetDevInf(agent))) {
				printf("Received the DevInf\n");
				smlSessionUseNumberOfChanges(session, smlDevInfSupportsNumberOfChanges(deviceDevinf));
				smlDevInfSetSupportsNumberOfChanges(devinf, smlDevInfSupportsNumberOfChanges(deviceDevinf));
			}
			if ((deviceDevinf && smlDevInfSupportsLargeObjs(deviceDevinf)) ||
			    (!deviceDevinf && useLargeObjs)) {
				/* enable standard compliant large object support */
				if (recvLimit)
					smlSessionSetLocalMaxMsgSize(session, recvLimit);
				if (maxObjSize)
					smlSessionSetLocalMaxObjSize(session, maxObjSize);
				smlSessionUseLargeObjects(session, TRUE);
				smlDevInfSetSupportsLargeObjs(devinf, TRUE);
			}
			
			/* We want to know about the device so we check if we received
			 * the devinf already */
			if (dumpinfo && !deviceDevinf) {
				printf("Going to request the devinf\n");
				if (!smlDevInfAgentRequestDevInf(agent, session, &locerror))
					goto error;
			}
			
			printf("Session %s reported final. flushing\n", smlSessionGetSessionID(session));

			SmlError *locerror = NULL;
			if (onlyInfo && deviceDevinf)
				smlSessionEnd(session, &locerror);
			else
				smlSessionFlush(session, TRUE, &locerror);
			break;
		case SML_MANAGER_SESSION_END:
			printf("Session %s has ended\n", smlSessionGetSessionID(session));
			smlManagerQuit(manager);
			g_main_loop_quit(loop);
			break;
		case SML_MANAGER_SESSION_ERROR:
			printf("There was an error in the session %s: %s", smlSessionGetSessionID(session), smlErrorPrint(&error));
			smlManagerQuit(manager);
			g_main_loop_quit(loop);
			break;
		case SML_MANAGER_SESSION_WARNING:
			printf("WARNING: %s", smlErrorPrint(&error));
			break;
		case SML_MANAGER_SESSION_FLUSH:
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

error:
	printf("An error occured while handling events: %s\n", smlErrorPrint(&locerror));
	smlManagerQuit(manager);
	g_main_loop_quit(loop);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&locerror));
	smlErrorDeref(&locerror);
}

#ifdef ENABLE_OBEX

static void discover_cb(obex_t *handle, obex_object_t *object, int mode, int event, int obex_cmd, int obex_rsp)
{
        (void) handle;
        (void) object;
        (void) mode;
        (void) event;
        (void) obex_cmd;
        (void) obex_rsp;
}

void list_interfaces()
{
	obex_t *handle;
	obex_interface_t* obex_intf;
	int i, interfaces_number = 0;
	
	if(!(handle = OBEX_Init(OBEX_TRANS_USB, discover_cb, 0))) {
		printf("OBEX_Init failed\n");
		return;
	}
	
	if (geteuid() != 0)
		fprintf(stderr, "Superuser privileges are required to access complete USB information.\n");

	interfaces_number = OBEX_FindInterfaces(handle, &obex_intf);
	printf("Found %d USB OBEX interfaces\n", interfaces_number);
	
	for (i = 0; i < interfaces_number; i++)
		printf("Interface %d:\n\tManufacturer: %s\n\tProduct: %s\n\tInterface description: %s\n", i,
			obex_intf[i].usb.manufacturer,
			obex_intf[i].usb.product,
			obex_intf[i].usb.control_interface);
	
	printf("Use '-u interface_number' to connect\n");
	OBEX_Cleanup(handle);
}

#endif

gboolean _sessions_prepare(GSource *source, gint *timeout_)
{
	*timeout_ = 50;
	return FALSE;
}

gboolean _sessions_check(GSource *source)
{
	GList *s = NULL;
	for (s = sessions; s; s = s->next) {
		SmlDsSession *session = s->data;
		if (smlDsSessionCheck(session))
			return TRUE;
	}
	
	if (smlManagerCheck(manager))
		return TRUE;
		
	return FALSE;
}

gboolean _sessions_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
	GList *s = NULL;
	for (s = sessions; s; s = s->next) {
		SmlDsSession *session = s->data;
		smlDsSessionDispatch(session);
	}
	
	smlManagerDispatch(manager);
	
	return TRUE;
}

GMainContext *_sessions_attach()
{
	GMainContext *context = g_main_context_new();
	
	GSourceFuncs *functions = g_malloc0(sizeof(GSourceFuncs));
	functions->prepare = _sessions_prepare;
	functions->check = _sessions_check;
	functions->dispatch = _sessions_dispatch;
	functions->finalize = NULL;

	GSource *source = g_source_new(functions, sizeof(GSource));
	g_source_set_callback(source, NULL, NULL, NULL);
	g_source_attach(source, context);
	
	return context;
}

int main(int argc, char *argv[])
{
	SmlTransportConnectionType type = SML_TRANSPORT_CONNECTION_TYPE_USB;
	char *url = NULL;
	char *port = NULL;
	
	SmlMimeType mimeType = SML_MIMETYPE_XML;
	
	SmlError *error = NULL;
	char *identifier = NULL;
	char *username = NULL;
	char *password = NULL;
	SmlNotificationVersion sanVersion = SML_SAN_VERSION_11;

	if (!g_thread_supported ()) g_thread_init (NULL);
	
	if (argc == 1)
		usage (argv[0], 1);
	
	int i = 0;
	for (i = 1; i < argc; i++) {
		char *arg = argv[i];
		if (!strcmp (arg, "--sync") || !strcmp(arg, "--slow-sync") || !strcmp(arg, "--add")) {
			i += 2;
			continue;
		} else if (!strcmp (arg, "-u")) {
			i++;
			if (!argv[i]) {

#ifdef ENABLE_OBEX
				list_interfaces();
				return 0;
#else
				printf("OBEX not available in this build\n");
				return 1;
#endif
			}
			
			errno = 0;
			port = argv[i];
			if (!port)
				usage (argv[0], 1);
		} else if (!strcmp (arg, "-b")) {
#ifdef ENABLE_BLUETOOTH
			type = SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH;

			i++;
			if (!argv[i])
				usage (argv[0], 1);
			url = g_strdup(argv[i]);

			i++;
			if (!argv[i])
				usage (argv[0], 1);
			errno = 0;
			port = argv[i];
			if (!port)
				usage (argv[0], 1);
#else
			printf("Bluetooth not available in this build\n");
			return 1;
#endif
		} else if (!strcmp (arg, "-s")) {
			type = SML_TRANSPORT_CONNECTION_TYPE_SERIAL;

			i++;
			if (!argv[i])
				usage (argv[0], 1);

			url = g_strdup(argv[i]);

 		} else if (!strcmp (arg, "--identifier")) {
			i++;
			if (!argv[i])
				usage (argv[0], 1);
			identifier = g_strdup(argv[i]);
		} else if (!strcmp (arg, "--username")) {
			i++;
			if (!argv[i])
				usage (argv[0], 1);
			username = g_strdup(argv[i]);
		} else if (!strcmp (arg, "--password")) {
			i++;
			if (!argv[i])
				usage (argv[0], 1);
			password = g_strdup(argv[i]);
		} else if (!strcmp (arg, "--recvLimit")) {
			i++;
			if (!argv[i])
				usage (argv[0], 1);
			recvLimit = atoi(argv[i]);
		} else if (!strcmp (arg, "--maxObjSize")) {
			i++;
			if (!argv[i])
				usage (argv[0], 1);
			maxObjSize = atoi(argv[i]);
		} else if (!strcmp (arg, "--useStringTable")) {
			useStringTable = TRUE;
		} else if (!strcmp (arg, "--disableNumberOfChanges")) {
			useNumberOfChanges = FALSE;
		} else if (!strcmp (arg, "--useTimeAnchor")) {
			useTimeAnchor = malloc(sizeof(char)*17);
			time_t htime = time(NULL);
			strftime(useTimeAnchor, 17, "%Y%m%dT%H%M%SZ", gmtime(&htime));
		} else if (!strcmp (arg, "--version")) {
			i++;
			if (!argv[i])
				usage (argv[0], 1);
				
			if (!strcmp (argv[i], "1.0")) {
				sanVersion = SML_SAN_VERSION_10;
			} else if (!strcmp (argv[i], "1.1")) {
				sanVersion = SML_SAN_VERSION_11;
			} else if (!strcmp (argv[i], "1.2")) {
				sanVersion = SML_SAN_VERSION_12;
			} else
				usage (argv[0], 1);
		} else if (!strcmp (arg, "--help")) {
			usage (argv[0], 0);
		} else if (!strcmp (arg, "--wbxml")) {
			mimeType = SML_MIMETYPE_WBXML;
		} else if (!strcmp (arg, "--dumpinfo")) {
			dumpinfo = TRUE;
		} else if (!strcmp (arg, "--onlyinfo")) {
			onlyInfo = TRUE;
		} else if (!strcmp (arg, "--")) {
			break;
		} else if (arg[0] == '-') {
			usage (argv[0], 1);
		} else {
			usage (argv[0], 1);
		}
	}
	
	/* The transport needed to transport the data */
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	if (!client)
		goto error;
	
	/* The manager responsible for handling the other objects */
	manager = smlManagerNew(client, &error);
	if (!manager)
		goto error;
	smlManagerSetEventCallback(manager, _manager_event, NULL);
	if (useLargeObjs)
	{
		/* This is a server which supports SyncML 1.1 or higher.
		 * So large objects must be supported.
		 */
		smlManagerSetLocalMaxMsgSize(manager, recvLimit);
		smlManagerSetLocalMaxObjSize(manager, maxObjSize);
	}
	
	/* The authenticator */
	SmlAuthenticator *auth = smlAuthNew(&error);
	if (!auth)
		goto error;

	if (!username)
		smlAuthSetEnable(auth, FALSE);
	
	if (!smlAuthRegister(auth, manager, &error))
		goto error_free_auth;
	
	
	/* Now create the devinf handler */
	devinf = smlDevInfNew("LibSyncmML", SML_DEVINF_DEVTYP_WORKSTATION, &error);
	if (!devinf)
		goto error_free_manager;
	smlDevInfSetSupportsNumberOfChanges(devinf, useNumberOfChanges);
	smlDevInfSetSupportsLargeObjs(devinf, useLargeObjs);
	smlDevInfSetSupportsUTC(devinf, TRUE);
	
	agent = smlDevInfAgentNew(devinf, &error);
	if (!agent)
		goto error_free_manager;
	
	if (!smlDevInfAgentRegister(agent, manager, &error))
		goto error_free_manager;
		
	/* Create the alert for the remote device */
	if (!identifier)
		identifier = g_strdup("LibSyncML Test Suite");
		
	SmlNotification *san = smlNotificationNew(sanVersion, SML_SAN_UIMODE_UNSPECIFIED, SML_SAN_INITIATOR_USER, 1, identifier, "/", mimeType, &error);
	if (!san)
		goto error;
	smlNotificationSetManager(san, manager);
		
	for (i = 1; i < argc; i++) {
		char *arg = argv[i];
		if (!strcmp (arg, "--sync") || !strcmp(arg, "--slow-sync")) {

			i++;
			if (!argv[i])
				usage (argv[0], 1);
			
			dbTypes = g_list_append(dbTypes, argv[i]);
			if (!strstr(argv[i], "/"))
				fprintf(stderr, "WARNING: Specified database type \"%s\" doesn't look like a valid MIME type!\n"
						"WARNING: (Mixed up database path/location with database type?)\n", argv[i] ? argv[i] : "");

			
			i++;
			if (!argv[i])
				usage (argv[0], 1);
			
			dbLocations = g_list_append(dbLocations, argv[i]);
			
			if (!strcmp(arg, "--slow-sync"))
				dbSlow = g_list_append(dbSlow, GINT_TO_POINTER(TRUE));
			else
				dbSlow = g_list_append(dbSlow, GINT_TO_POINTER(FALSE));
				
			/* We now create the ds server hat the given location */
			SmlLocation *loc = smlLocationNew(g_list_last(dbLocations)->data, NULL, &error);
			if (!loc)
				goto error;
			
			SmlDsServer *dsserver = smlDsServerNew(g_list_last(dbTypes)->data, loc, &error);
			if (!dsserver)
				goto error_free_manager;
				
			if (!smlDsServerRegister(dsserver, manager, &error))
				goto error_free_auth;
			
			smlDsServerSetConnectCallback(dsserver, _ds_alert, NULL);
			
			/* Then we add the alert to the SAN */
			if (!smlDsServerAddSan(dsserver, san, &error))
				goto error;
			
			/* And we also add the devinfo to the devinf agent */
			SmlDevInfDataStore *datastore = smlDevInfDataStoreNew(smlLocationGetURI(loc), &error);
			if (!datastore)
				goto error;
			
			if (!strcmp(g_list_last(dbTypes)->data, SML_ELEMENT_TEXT_VCARD)) {
				smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_VCARD, "2.1");
				smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_VCARD, "2.1");
			} else if (!strcmp(g_list_last(dbTypes)->data, SML_ELEMENT_TEXT_VCAL)) {
				smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_VCAL, "1.0");
				smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_VCAL, "1.0");
			} else if (!strcmp(g_list_last(dbTypes)->data, SML_ELEMENT_TEXT_PLAIN)) {
				smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_PLAIN, "1.0");
				smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_PLAIN, "1.0");
			}
			
			smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
			smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC, TRUE);
			smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);
			
			smlDevInfAddDataStore(devinf, datastore);
		} else if (!strcmp (arg, "--add")) {

			i++;
			if (!argv[i])
				usage (argv[0], 1);
			
			types = g_list_append(types, argv[i]);
			
			i++;
			if (!argv[i])
				usage (argv[0], 1);
			
			paths = g_list_append(paths, argv[i]);
		}
	}

	if (g_list_length(dbLocations) == 0) {
		smlErrorSet(&error, SML_ERROR_GENERIC, "You have to configure at least one database");
		goto error;
	}

	/* Initialize the Transport */
	if (!smlTransportSetConfigOption(client, "PORT", port, &error) ||
	    !smlTransportSetConfigOption(client, "URL", url, &error) ||
	    !smlTransportSetConnectionType(client, type, &error) ||
	    !smlTransportInitialize(client, &error))
		goto error;

	/* Run the manager */
	if (!smlManagerStart(manager, &error))
		goto error;
	
	if (!smlTransportConnect(client, &error))
		goto error;

	if (!smlNotificationSend(san, client, &error))
		goto error;
	
	smlNotificationFree(san);
		
	GMainContext *context = _sessions_attach();
	loop = g_main_loop_new(context, TRUE);
	g_main_loop_run(loop);
	
	if (!smlTransportDisconnect(client, NULL, &error))
		goto error;

	/* Stop the manager */
	smlManagerStop(manager);
	
	smlTransportFinalize(client, &error);
	
	smlTransportFree(client);
	
	if (dumpinfo) {
		if (!deviceDevinf) {
			printf("Didnt receive the devinf though it was requested\n");
		} else {
			printf("Send the output below to the libsyncml developers\n");
			printf("\n========================================\n");
			printf("Man: %s\n", smlDevInfGetManufacturer(deviceDevinf));
			printf("Mod: %s\n", smlDevInfGetModel(deviceDevinf));
			printf("FirmwareVersion: %s\n", smlDevInfGetFirmwareVersion(deviceDevinf));
			printf("SoftwareVersion: %s\n", smlDevInfGetSoftwareVersion(deviceDevinf));
			printf("HardwareVersion: %s\n", smlDevInfGetHardwareVersion(deviceDevinf));
			printf("\n");
			printf("ReceiveLimit: %i\n", recvLimit);
			printf("MaxObjSize: %i\n", maxObjSize);
			printf("Connection used: OBEX Client\n");
			printf("Identifier: %s\n", identifier);
			printf("\nDatabases:\n");
			int i = 0;
			for (i = 0; i < g_list_length(dbLocations); i++) {
				printf("DB Locations: %s\n", (char *)g_list_nth_data(dbLocations, i));
				printf("DB Type: %s\n", (char *)g_list_nth_data(dbTypes, i));
				printf("DB Slow: %i\n\n", GPOINTER_TO_INT(g_list_nth_data(dbSlow, i)));
			}
			printf("Bluetooth: %s\n", type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH ? "Yes" : "Unknown"); 
			printf("Wbxml: %s\n", mimeType == SML_MIMETYPE_WBXML ? "Yes" : "No");
			printf("SyncML Version: 1.%i\n", sessionVersion - 1);
			printf("SupportsNumberOfChanges: %s\n", smlDevInfSupportsNumberOfChanges(deviceDevinf) ? "Yes" : "No");
			printf("SupportsLargeObjects: %s\n", smlDevInfSupportsLargeObjs(deviceDevinf) ? "Yes" : "No");
		}
	}
	
	g_free(identifier);
	g_free(url);
	
	return 0;

error_free_auth:
	smlAuthFree(auth);
error_free_manager:
	smlManagerFree(manager);
error:
	printf("Failed to start the client: %s\n", smlErrorPrint(&error));
	smlErrorDeref(&error);
	return -1;
}
