/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2005  Armin Bauer <armin.bauer@opensync.org>
 * Copyright (C) 2007-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 <config.h>
 
#include <libsyncml/sml_support.h>
#include <libsyncml/sml_error_internals.h>
#include <libsyncml/sml_transport_internals.h>

#include "obex_internals.h"
#include "obex_client_internals.h"
#include "obex_client_vendor_internals.h"

#include "../data_sync_api/sml_data_sync_defines.h"

#include <errno.h>

#ifdef ENABLE_BLUETOOTH_SDPLIB

#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>

#endif

#include <fcntl.h>
#ifndef WIN32
#include <sys/poll.h>
#include <sys/stat.h>
#include <termios.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <strings.h>

#ifndef WIN32

static gboolean
obex_cable_at (SmlTransportObexClientEnv *userdata,
               const gchar *cmd,
               gchar *rspbuf,
               gint rspbuflen,
               gint timeout,
               GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %s, %p, %i, %i, %p)", __func__, userdata, VA_STRING(cmd), rspbuf, rspbuflen, timeout, error);
	CHECK_ERROR_REF

	fd_set ttyset;
	struct timeval tv;
	gint fd;

	gboolean statusOnly = FALSE;

	char *answer = NULL;
	char *answer_end = NULL;
	int answer_size;

	char tmpbuf[100] = {0,};
	int actual;
	int total = 0;
	int done = 0;

	if (!strncasecmp("ATZ", cmd, 3)) {
		smlTrace(TRACE_INTERNAL, "%s: detected standard ATZ command", __func__);
		statusOnly = TRUE;
	}
	if (!strncasecmp("AT+CPROT=0", cmd, 10)) {
		smlTrace(TRACE_INTERNAL, "%s: detected standard AT+CPROT=0 command", __func__);
		statusOnly = TRUE;
	}

	/* send AT command */
	fd = userdata->fd;
	rspbuf[0] = 0;
	if(fd < 0) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			"The file descriptor for the cable AT command is not legal (%d).",
			fd);
		goto error;
	}
	if(cmd != NULL) {
		// Write command
		gint cmdlen;

		cmdlen = strlen(cmd);
		if(write(fd, cmd, cmdlen) < cmdlen) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The command cannot be written (completely) to the file descriptor.");
			goto error;
		}
	}

	/* read the answer
	 * the default answer for AT commands is: \nresult\n\nOK\n
	 */
	while(!done)  {
		FD_ZERO(&ttyset);
		FD_SET(fd, &ttyset);
		tv.tv_sec = timeout;
		tv.tv_usec = 0;
		if(select(fd+1, &ttyset, NULL, NULL, &tv))  {
			actual = read(fd, &tmpbuf[total], sizeof(tmpbuf) - total);
			if(actual < 0) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
					"read failed with return code %i.",
					actual);
				goto error;
			}
			total += actual;

			/* Answer didn't come within the length of the buffer. Cancel! */
			if(total == sizeof(tmpbuf)) {
				g_set_error(error, SML_ERROR, SML_ERROR_NOT_IMPLEMENTED,
					"The size of the answer to the AT command %s was too large for the internal buffer.",
					VA_STRING(cmd));
				goto error;
			}

			if( (answer = index(tmpbuf, '\n')) &&    /* \n infront of response */
			    (answer_end = index(answer+1, '\n')) /* \n at end of repsonse */
                          ) {
				/* check for an error */
				if (!strncasecmp("ERROR", answer+1, 5)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
						"The AT command %s failed.",
						VA_STRING(cmd));
					goto error;
				}

				/* read the trailing \nOK\n if necessary */
				if (statusOnly) {
					/* Found end of answer */
					done = 1;
				} else {
					char *ok = NULL;
					char *ok_end = NULL;
					if ( (ok = index(answer_end+1, '\n')) && /* \n infront of ok */
					     (ok_end = index(ok+1, '\n'))        /* \n at end of ok */
					   ) {
						/* Found end of answer */
						done = 1;
					}
				}
			}
		} else {
			/* Anser didn't come in time. Cancel */
			g_set_error(error, SML_ERROR, SML_ERROR_INTERNAL_IO_ERROR,
				"The read operation for the answer of the AT command was timed out.");
			goto error;
		}
	}
	smlTrace(TRACE_INTERNAL, "%s: answer: %s", __func__, VA_STRING(answer));

	// Remove heading and trailing \r
	if((*answer_end == '\r') || (*answer_end == '\n'))
		answer_end--;
	if((*answer_end == '\r') || (*answer_end == '\n'))
		answer_end--;
	if((*answer == '\r') || (*answer == '\n'))
		answer++;
	if((*answer == '\r') || (*answer == '\n'))
		answer++;

	answer_size = (answer_end) - answer +1;

	smlTrace(TRACE_INTERNAL, "%s: answer size=%d", __func__, answer_size);
	if( (answer_size) >= rspbuflen ) {
		g_set_error(error, SML_ERROR, SML_ERROR_NOT_IMPLEMENTED,
			"The size of the answer to the AT command %s was too large.",
			VA_STRING(cmd));
		goto error;
	}

	strncpy(rspbuf, answer, answer_size);
	rspbuf[answer_size] = 0;
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT, "%s - %s", __func__, (*error)->message);
	return FALSE;
}

static int smlTransportObexClientCableDisconnect(obex_t *handle, gpointer ud) {

	smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, handle, ud);

	SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
	if (userdata->fd >= 0) {
		// Send a break to get out of OBEX-mode
#ifdef TCSBRKP
		if(ioctl(userdata->fd, TCSBRKP, 0) < 0) {
#elif defined(TCSBRK) 
		if(ioctl(userdata->fd, TCSBRK, 0) < 0) { 
#else 
		if(tcsendbreak(userdata->fd, 0) < 0) { 
#endif /* TCSBRKP */ 
			smlTrace(TRACE_INTERNAL, "%s: Unable to send break!", __func__);
		}

		tcsetattr(userdata->fd, TCSANOW, &userdata->oldtio);
		close(userdata->fd);
	}

	return 0;
}

SmlTransportObexVendorType smlTransportObexClientGetVendor(const gchar *manufacturer)
{
	smlTrace(TRACE_ENTRY, "%s(%s)", __func__, VA_STRING(manufacturer));
	smlAssert(manufacturer);
	SmlTransportObexVendorType vendor = SML_OBEX_VENDOR_UNKNOWN;

	char *big = g_ascii_strup(manufacturer, -1);

	if (strstr(big, "SAMSUNG") != NULL) {
		smlTrace(TRACE_INTERNAL, "%s - Samsung found.",  __func__);
		vendor = SML_OBEX_VENDOR_SAMSUNG;
	}

	smlSafeCFree(&big);

	smlTrace(TRACE_EXIT, "%s - %d", __func__, vendor);
	return vendor;
}

static gint smlTransportObexClientCableConnect(obex_t *handle, gpointer ud)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);

	GError *error = NULL;
	SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
	struct termios newtio;
	char rspbuf[201];
	rspbuf[sizeof(rspbuf)-1] = 0; /* sanitize for %s in printf format */

	userdata->fd = open(userdata->path, O_RDWR|O_NONBLOCK|O_NOCTTY);
	if (userdata->fd < 0) {
		g_set_error(&error, SML_ERROR, SML_ERROR_INTERNAL_IO_ERROR,
			"A valid file descriptor must be non-negative.");
		goto error;
	}

	tcgetattr(userdata->fd, &userdata->oldtio);
	bzero(&newtio, sizeof(struct termios));
	newtio.c_cflag = B115200 | CLOCAL | CS8 | CREAD | CRTSCTS;
	newtio.c_cc[VMIN] = 1;
	newtio.c_cc[VTIME] = 0;
	newtio.c_iflag = IGNPAR; 
	newtio.c_oflag = 0; 
	tcflush(userdata->fd, TCIFLUSH);
	tcsetattr(userdata->fd, TCSANOW, &newtio);

	/* init the communication line */
	if (!obex_cable_at(userdata, "ATZ\r", rspbuf, sizeof(rspbuf) - 1, 1, &error))
		goto error;

	if (strcasecmp("OK", rspbuf)) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
			"The ATZ command was answered with %s.",
			rspbuf);
		goto error;
	}

	if (!userdata->at_command && !userdata->manufacturer)
	{
		/* get the manufacturer */
		if (!obex_cable_at(userdata, "AT+CGMI\r", rspbuf, sizeof(rspbuf), 1, &error)) {
			smlTrace(TRACE_ERROR, "%s: Comm-error sending AT+CGMI\\r", __func__);
			goto error;
		}
		userdata->manufacturer = g_strdup(rspbuf);
		smlTrace(TRACE_ERROR, "%s: manufacturer %s", __func__, VA_STRING(userdata->manufacturer));
	}
	if (!userdata->at_command && !userdata->model)
	{
		/* get the model */
		if (!obex_cable_at(userdata, "AT+CGMM\r", rspbuf, sizeof(rspbuf), 1, &error)) {
			smlTrace(TRACE_ERROR, "%s: Comm-error sending AT+CGMM\\r", __func__);
			goto error;
		}
		userdata->model = g_strdup(rspbuf);
		smlTrace(TRACE_ERROR, "%s: model %s", __func__, VA_STRING(userdata->model));
	}
	if (!userdata->at_command)
	{
		/* start vendor specific handling */
		SmlTransportObexVendorType vendor = SML_OBEX_VENDOR_UNKNOWN;
		if (userdata->manufacturer) {
			smlTrace(TRACE_INTERNAL, "%s: Try to find an appropriate AT command", __func__);
			vendor = smlTransportObexClientGetVendor(userdata->manufacturer);
		}
		if (vendor) {
			switch(vendor) {
				case SML_OBEX_VENDOR_SAMSUNG:
					smlTrace(TRACE_INTERNAL, "%s: Samsung detected", __func__);
					if (!smlTransportObexVendorSamsungInit(userdata)) {
						g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
							"The intialization for Samsung failed.");
						goto error;
					}
					break;
				default:
					smlTrace(TRACE_ERROR, "%s: Ups, vendor defined but not handled.", __func__);
					break;
			}
		}
	}
	if (!userdata->at_command) {
		/* setup default */
		smlTrace(TRACE_INTERNAL, "%s: Setting up default AT command AT+CPROT=0\\r", __func__);
		userdata->at_command = g_strdup("AT+CPROT=0\r");
	}

	/* start the synchronization */
	if (!obex_cable_at(userdata, userdata->at_command, rspbuf, sizeof(rspbuf) - 1, 1, &error))
		goto error;

	if (strcasecmp("CONNECT", rspbuf)) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
			"The command AT+CPROT=0 failed with answer %s.",
			rspbuf);
		goto error;
	}
//	fcntl(userdata->fd, F_SETFL, O_NONBLOCK);
	return 0;
error:
	perror(error->message);
	smlTransportObexClientCableDisconnect(handle, userdata);
	smlTrace(TRACE_ERROR, "%s - %s", __func__, error->message);
	g_error_free(error);
	return -1;
}

static gint smlTransportObexClientCableWrite(obex_t *handle, gpointer ud, guint8 *buf, gint buflen) {

	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %i)", __func__, handle, ud, buf, buflen);
	SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
	int written = 0;
	int ret = 0;

	while (ret >= 0 && written < buflen) {
		ret = write(userdata->fd, buf + written, buflen - written);
		if (ret >= 0)
			written += ret;
	}

	smlTrace(TRACE_EXIT, "%s: %i", __func__, written);
	return written;
}

gint obex_cable_handleinput(obex_t *handle, gpointer ud, gint timeout) {
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %i)", __func__, handle, ud, timeout);
	struct timeval to;
	fd_set readfds;
	char buf[512];
	SmlTransportObexClientEnv *userdata;
	int ret = 0;
	int actual = 0;

	userdata = (SmlTransportObexClientEnv*) ud;
	FD_ZERO(&readfds);
	FD_SET(userdata->fd, &readfds);
	to.tv_sec = timeout;
	to.tv_usec = 0;


	smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
	ret = select(userdata->fd + 1, &readfds, NULL, NULL, &to); 
	
	if (ret < 1)
		goto error;
	
	smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
	if ((actual = read(userdata->fd, buf, sizeof(buf))) <= 0)
		goto end;

	smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
	OBEX_CustomDataFeed(handle, (unsigned char *) buf, actual);

end:
	smlTrace(TRACE_EXIT, "%s(%p, %p, %i)", __func__, handle, ud, timeout);
	return actual;

error:

	smlTrace(TRACE_EXIT_ERROR, "%s: %i", __func__, ret);
	return ret;

}

#endif

static void smlTransportObexClientEvent(obex_t *handle, obex_object_t *object, gint mode, gint event, gint obex_cmd, gint obex_rsp)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %i, %i, %i)", __func__, handle, object, mode, event, obex_cmd, obex_rsp);
	SmlTransportObexClientEnv *env = OBEX_GetUserData(handle);
	GError *error = NULL;
	
	switch (event)  {
		case OBEX_EV_PROGRESS:
			smlTrace(TRACE_INTERNAL, "%s: Progress", __func__);
			break;
		case OBEX_EV_REQDONE:
			smlTrace(TRACE_INTERNAL, "%s: Request Done", __func__);
			env->busy = FALSE;
			
			if (obex_rsp != OBEX_RSP_SUCCESS) {
				switch(obex_cmd) {
					case OBEX_CMD_CONNECT:
						g_set_error(&error, SML_ERROR, SML_ERROR_INTERNAL_MISCONFIGURATION,
							"The OBEX client cannot connect to the configured device or resource (%s - 0x%x).",
							OBEX_ResponseToString(obex_rsp),
							obex_rsp);
						break;
					default:
						g_set_error(&error, SML_ERROR, SML_ERROR_INTERNAL_IO_ERROR,
							"%s (0x%x)",
							OBEX_ResponseToString(obex_rsp),
							obex_rsp);
						break;
				}
				goto error;
			}
			
			/* Get the connection id if we connected */
			switch (obex_cmd) {
				case OBEX_CMD_CONNECT:;
					uint8_t headertype = 0;
					obex_headerdata_t header;
					uint32_t len;
					char *who = NULL;
					
					while (OBEX_ObjectGetNextHeader(env->obexhandle, object, &headertype, &header, &len)) {
						smlTrace(TRACE_INTERNAL, "%s: Next header %i, %d, %p", __func__, headertype, header.bq4, header.bs);
						switch (headertype) {
							case OBEX_HDR_CONNECTION:
								smlTrace(TRACE_INTERNAL, "%s: Found connection number: %d", __func__, header.bq4);
								env->connection_id = header.bq4;
								break;
							case OBEX_HDR_WHO:
								who = g_strndup((char *)header.bs, len);
								smlTrace(TRACE_INTERNAL, "%s: Found who: %s", __func__, VA_STRING(who));
								break;
							default:
								smlTrace(TRACE_INTERNAL, "%s: Unknown header", __func__);
						}
					}
					
					if (!env->connection_id) {
						g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Missing connection id");
						smlSafeCFree(&who);
						goto error;
					}
					
					smlTrace(TRACE_INTERNAL, "%s: Got who: %s", __func__, VA_STRING(who));
					if (!who || strcmp(who, "SYNCML-SYNC")) {
						g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Missing or wrong who response");
						smlSafeCFree(&who);
						goto error;
					}
					smlSafeCFree(&who);
					
					env->state = SML_TRANSPORT_CONNECTED;
					smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL);
					break;
				case OBEX_CMD_DISCONNECT:;
					if (SML_TRANSPORT_CONNECTED <= env->state &&
					    env->state < SML_TRANSPORT_DISCONNECTED)
					{
						env->busy = FALSE;
						OBEX_TransportDisconnect(env->obexhandle);
						env->state = SML_TRANSPORT_DISCONNECTED;
						smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
					}
					break;
				case OBEX_CMD_GET:;
					smlTrace(TRACE_INTERNAL, "%s: Got GET command done", __func__);
					uint32_t length = 0;
					char *body = NULL;
					
					while (OBEX_ObjectGetNextHeader(handle, object, &headertype, &header, &len)) {
						smlTrace(TRACE_INTERNAL, "%s: Next header %i, %d, %p, len %i", __func__, headertype, header.bq4, header.bs, len);
						switch (headertype) {
							case OBEX_HDR_LENGTH:
								smlTrace(TRACE_INTERNAL, "%s: Found length: %d", __func__, header.bq4);
								length = header.bq4;
								break;
							case OBEX_HDR_BODY:
								if (!length) {
									smlTrace(TRACE_INTERNAL, "%s: Length not given. Calculating it to: %i", __func__, len);
									length = len;
								}
								
								if (!length) {
									g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Got zero length!");
									goto error;
								}
								
								/* We need one byte more than the response length
								 * because the data can be a native XML message.
								 * If the data is a native XML message then it is
								 * sometimes directly used as string.
								 *
								 * The string is automatically NULL terminated
								 * because smlTryMalloc0 fills the new memory with NULLs.
								 */
								body = smlTryMalloc0(length + 1, &error);
								if (!body)
									goto error;
								
								memcpy(body, header.bs, length);
								break;
							default:
								smlTrace(TRACE_INTERNAL, "%s: Unknown header", __func__);
						}
					}
					
					if (!length) {
						g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Missing length");
						goto error;
					}
					
					if (!body) {
						g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Missing body");
						goto error;
					}
					
					SmlTransportData *tspdata = smlTransportDataNew(body, length, env->mimetype, TRUE, &error);
					if (!tspdata)
						goto error;
					
					smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DATA, tspdata, NULL);
					smlTransportDataDeref(tspdata);
					break;
			}
			break;
		case OBEX_EV_LINKERR:
			if (obex_rsp == 0)
			{
				/* This is a disconnect. */
				if (SML_TRANSPORT_CONNECTED <= env->state &&
				    env->state < SML_TRANSPORT_DISCONNECTED)
				{
					env->busy = FALSE;
					OBEX_TransportDisconnect(env->obexhandle);
					env->state = SML_TRANSPORT_DISCONNECTED;
					smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
				}
			} else {
				/* This is a normal error. */
				g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
					"Link Error: %s - 0x%x",
					OBEX_ResponseToString(obex_rsp), obex_rsp);
				smlTrace(TRACE_INTERNAL, "%s - %s",
					__func__, error->message);
				goto error;
			}
			break;
		case OBEX_EV_STREAMEMPTY:
			smlTrace(TRACE_INTERNAL, "%s: Empty Stream", __func__);
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

error:
	smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, error->message);
	g_error_free(error);
	env->busy = FALSE;
	env->error = TRUE;
	return;
}

static gboolean
smlTransportObexClientSetConfigOption (SmlTransport *tsp,
                                       const gchar *name,
                                       const gchar *value,
                                       GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, tsp, VA_STRING(name), VA_STRING(value), error);
	CHECK_ERROR_REF
	smlAssert(tsp);
	smlAssert(tsp->transport_data);
        smlAssert(name); /* should be checked by smlTransportSetConfigOption */
	SmlTransportObexClientEnv *env = tsp->transport_data;

	/* This transport does not accept NULL as value. */
	if (!value)
	{
		g_set_error(error, SML_ERROR, SML_ERROR_INTERNAL_MISCONFIGURATION,
			"The configuration option %s requires a value.", name);
		goto error;
	}

	if (!strcmp(name, SML_TRANSPORT_CONFIG_BLUETOOTH_CHANNEL)) {
		env->port = atoi(value);
		smlTrace(TRACE_INTERNAL, "%s: Bluetooth channel %i detected", __func__, env->port);
	} else if (!strcmp(name, SML_TRANSPORT_CONFIG_BLUETOOTH_ADDRESS)) {
		/* This is the MAC of the remote device. */
		env->path = g_strdup(value);
		smlTrace(TRACE_INTERNAL, "%s: Bluetooth MAC %s detected", __func__, VA_STRING(env->path));
	} else if (!strcmp(name, SML_TRANSPORT_CONFIG_PORT)) {
		if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH)
			g_warning("Please use %s instead of %s.",
				SML_TRANSPORT_CONFIG_BLUETOOTH_CHANNEL,
				SML_TRANSPORT_CONFIG_PORT);
		env->port = atoi(value);
		smlTrace(TRACE_INTERNAL, "%s: Port or Bluetooth channel %i detected",
			__func__, env->port);
	} else if (!strcmp(name, SML_TRANSPORT_CONFIG_URL)) {
		if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH)
			g_warning("Please use %s instead of %s.",
				SML_TRANSPORT_CONFIG_BLUETOOTH_ADDRESS,
				SML_TRANSPORT_CONFIG_URL);
		env->path = g_strdup(value);
		smlTrace(TRACE_INTERNAL, "%s: URL or Bluetooth MAC %s detected",
			__func__, VA_STRING(env->path));
	} else if (!strcmp(name, SML_TRANSPORT_CONFIG_IRDA_SERVICE)) {
		if (env->irda_service)
			smlSafeCFree(&(env->irda_service));
		env->irda_service = g_strdup(value);
		smlTrace(TRACE_INTERNAL, "%s: IrDA service %s detected", __func__, VA_STRING(env->irda_service));
	} else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_COMMAND)) {
		if (env->at_command)
			smlSafeCFree(&(env->at_command));
		env->at_command = g_strdup(value);
		smlTrace(TRACE_INTERNAL, "%s: AT command %s detected", __func__, VA_STRING(env->at_command));
	} else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_MANUFACTURER)) {
		if (env->manufacturer)
			smlSafeCFree(&(env->manufacturer));
		env->manufacturer = g_strdup(value);
		smlTrace(TRACE_INTERNAL, "%s: AT manufacturer %s detected", __func__, VA_STRING(env->manufacturer));
	} else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_MODEL)) {
		if (env->model)
			smlSafeCFree(&(env->model));
		env->model = g_strdup(value);
		smlTrace(TRACE_INTERNAL, "%s: AT model %s detected", __func__, VA_STRING(env->model));
	} else if (!strcmp(name, SML_TRANSPORT_CONFIG_DATASTORE)) {
		/* This is needed for vendors which signal the datastore with AT commands. */
		SmlTransportObexDatastoreType datastore = SML_TRANSPORT_OBEX_DATASTORE_UNKNOWN;
		if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_EVENT, value))
			datastore = SML_TRANSPORT_OBEX_DATASTORE_EVENT;
		if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_TODO, value))
			datastore = SML_TRANSPORT_OBEX_DATASTORE_TODO;
		if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_CONTACT, value))
			datastore = SML_TRANSPORT_OBEX_DATASTORE_CONTACT;
		if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_NOTE, value))
			datastore = SML_TRANSPORT_OBEX_DATASTORE_NOTE;
		if (!datastore) {
			g_set_error(error, SML_ERROR, SML_ERROR_INTERNAL_MISCONFIGURATION,
			            "Unknown datastore %s found.", value);
			goto error;
		}
		SmlTransportObexDatastoreType *type = smlTryMalloc0(sizeof(SmlTransportObexDatastoreType), error);
		if (!type)
			goto error;
		*type = datastore;
		env->datastores = g_list_append(env->datastores, type);
		smlTrace(TRACE_INTERNAL, "%s: Datastore %i detected", __func__, datastore);
	} else {
		g_set_error(error, SML_ERROR, SML_ERROR_INTERNAL_MISCONFIGURATION,
		            "Unknown parameter %s found.", name);
		goto error;
	}

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

static gboolean
smlTransportObexClientSetConnectionType (SmlTransport *tsp,
                                         SmlTransportConnectionType type,
                                         GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, tsp, type, error);
	CHECK_ERROR_REF
	smlAssert(tsp);
	smlAssert(tsp->transport_data);
	SmlTransportObexClientEnv *env = tsp->transport_data;

	env->type = type;

	switch (env->type) {
		case SML_TRANSPORT_CONNECTION_TYPE_NET:
			env->obexhandle = OBEX_Init(OBEX_TRANS_FD, smlTransportObexClientEvent, 0);
			break;
		case SML_TRANSPORT_CONNECTION_TYPE_IRDA:
			env->obexhandle = OBEX_Init(OBEX_TRANS_IRDA, smlTransportObexClientEvent, 0);
			break;
		case SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH:
			env->obexhandle = OBEX_Init(OBEX_TRANS_BLUETOOTH, smlTransportObexClientEvent, 0);
			break;
		case SML_TRANSPORT_CONNECTION_TYPE_SERIAL:
			env->obexhandle = OBEX_Init(OBEX_TRANS_CUST, smlTransportObexClientEvent, 0);	
			break;
		case SML_TRANSPORT_CONNECTION_TYPE_USB:
			env->obexhandle = OBEX_Init(OBEX_TRANS_USB, smlTransportObexClientEvent, 0);
			break;
		default:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown obex type");
			smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
			return FALSE;
	}
	
	if (!env->obexhandle) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to open connection");
		smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
		return FALSE;
	}

#ifndef WIN32
	if (env->type == SML_TRANSPORT_CONNECTION_TYPE_SERIAL) {
		obex_ctrans_t cabletrans = {
					smlTransportObexClientCableConnect, 
					smlTransportObexClientCableDisconnect,
					NULL,
					smlTransportObexClientCableWrite,
					obex_cable_handleinput,
					env};
		OBEX_RegisterCTransport(env->obexhandle, &cabletrans);
	}
#endif

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

static gboolean
smlTransportObexClientInit (SmlTransport *tsp,
                            GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
	CHECK_ERROR_REF
	smlAssert(tsp);
	smlAssert(tsp->transport_data);
	SmlTransportObexClientEnv *env = tsp->transport_data;	
	
	OBEX_SetUserData(env->obexhandle, env);

	switch (env->type) {
		case SML_TRANSPORT_CONNECTION_TYPE_NET:
			if (!env->path) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
					"The hostname or address is missing.");
				goto error;
			}
			break;
		case SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH:
			if (!env->path) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
					"The bluetooth address is missing.");
				goto error;
			}
#ifndef ENABLE_BLUETOOTH_SDPLIB
			if (!env->port) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
					"The bluetooth channel is missing.");
				goto error;
			}
#endif
			break;
		default:
			/* nothing to check here */
			break;
	}

	env->state = SML_TRANSPORT_INITIALIZED;

	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
error:
	OBEX_Cleanup(env->obexhandle);
	if (env->path)
		smlSafeCFree(&(env->path));
	if (env->at_command)
		smlSafeCFree(&(env->at_command));
	if (env->manufacturer)
		smlSafeCFree(&(env->manufacturer));
	if (env->model)
		smlSafeCFree(&(env->model));
	smlSafeFree((gpointer *)&env);
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
smlTransportObexClientFinalize (void *data,
                                GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, error);
	CHECK_ERROR_REF
	smlAssert(data);
	SmlTransportObexClientEnv *env = data;
	
	smlAssert(env->tsp);
	if (env->path) smlSafeCFree(&(env->path));
	
	OBEX_Cleanup(env->obexhandle);
	
	if (env->irda_service)
		smlSafeCFree(&(env->irda_service));
	if (env->at_command)
		smlSafeCFree(&(env->at_command));
	if (env->manufacturer)
		smlSafeCFree(&(env->manufacturer));
	if (env->model)
		smlSafeCFree(&(env->model));
	while (env->datastores) {
		SmlTransportObexDatastoreType *type = env->datastores->data;
		env->datastores = g_list_remove(env->datastores, type);
		smlSafeFree((gpointer *) &type);
	}

	smlSafeFree((gpointer *)&env);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
}

static void smlTransportObexClientConnect(void *data)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
	smlAssert(data);
	SmlTransportObexClientEnv *env = data;
	int fd = 0;
	GError *error = NULL;
	unsigned int obex_intf_cnt;
	obex_interface_t *obex_intf;
	
	if (env->type == SML_TRANSPORT_CONNECTION_TYPE_NET) {
		smlTrace(TRACE_INTERNAL, "%s: connecting to inet address %s:%d",
			__func__, VA_STRING(env->path), env->port);
		struct sockaddr_in addr;
		memset(&addr, 0, sizeof(addr));
		addr.sin_family = AF_INET;
		addr.sin_port = g_htons(env->port);
		
		struct hostent *hostinfo = gethostbyname (env->path);
		if (!hostinfo) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unknown host %s", env->path);
			goto error;
		}
		addr.sin_addr = *(struct in_addr *) hostinfo->h_addr_list[0];
		
		fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (fd < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Cannot create socket: %s", strerror(errno));
			goto error;
		}
			
		smlTrace(TRACE_INTERNAL, "%s: socket %i", __func__, fd);
		
		char *addrstr = inet_ntoa(addr.sin_addr);
		smlTrace(TRACE_INTERNAL, "%s: peer addr = %d %s %i", __func__, hostinfo->h_addr_list[0], VA_STRING(addrstr), env->port);

		if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Cannot connect socket: %s", strerror(errno));
			goto error_close;
		}
		smlTrace(TRACE_INTERNAL, "%s: connect done", __func__);

	} else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_USB) {
		smlTrace(TRACE_INTERNAL, "%s: connecting to usb interface %i", __func__, env->port);
		
		obex_intf_cnt = OBEX_FindInterfaces(env->obexhandle, &obex_intf);
		smlTrace(TRACE_INTERNAL, "%s: found %i interfaces", __func__, obex_intf_cnt);
		
		if (obex_intf_cnt <= 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "There are no valid USB interfaces.");
			goto error;
		} else if (env->port >= obex_intf_cnt) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to find the USB interface number %i", env->port);
			goto error;
		} else {
			if (GET_OBEX_RESULT(OBEX_InterfaceConnect(env->obexhandle, &obex_intf[env->port])) < 0) {
				g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
					"The interface cannot be connected. %s (%i).",
					strerror(errno), errno);
				goto error;
			}
		}
		smlTrace(TRACE_INTERNAL, "%s: usb connect done", __func__);
	} else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH) {
#ifdef ENABLE_BLUETOOTH
		smlTrace(TRACE_INTERNAL, "%s: connecting to bluetooth device %s channel %i", __func__, VA_STRING(env->path), env->port);

		uint8_t channel = env->port;
		bdaddr_t bdaddr;
		str2ba(env->path, &bdaddr);

#ifdef ENABLE_BLUETOOTH_SDPLIB
		if (channel == 0) {
			/* FIXME: Is this code really memory leak free !? */

			sdp_session_t *sdp_session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
			if (!sdp_session) {
				g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "The Bluetooth connect to search for the channel failed.");
				goto error;
			}
			unsigned char syncml_client_uuid[] = {
					0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02};
			uuid_t uuid;
			uint32_t range = SDP_ATTR_PROTO_DESC_LIST;
			sdp_uuid128_create(&uuid, syncml_client_uuid);
			sdp_list_t *attribute = sdp_list_append(0, &range);
			sdp_list_t *class = sdp_list_append(0, &uuid);

			sdp_list_t *list;
			if (sdp_service_search_attr_req(sdp_session, class, SDP_ATTR_REQ_INDIVIDUAL, attribute, &list) < 0) {
				sdp_close(sdp_session);
        			sdp_list_free(attribute, 0);
        			sdp_list_free(class, 0);
				
				g_set_error(
					&error, SML_ERROR, SML_ERROR_GENERIC,
					"The service search on the Bluetooth device failed.");
				goto error;
			}
			sdp_list_free(attribute, 0);
			sdp_list_free(class, 0);

			sdp_list_t *iterator;
			for(iterator = list; iterator; iterator = iterator->next) {
				sdp_record_t *item = (sdp_record_t *) iterator->data;
				sdp_list_t *protos = NULL;

				sdp_get_access_protos(item, &protos);
				if(protos) {
					channel = sdp_get_proto_port(protos, RFCOMM_UUID);
				}
				sdp_record_free(item);
			}
			sdp_list_free(list, 0);
			sdp_close(sdp_session);
			sleep(1);
			smlTrace(TRACE_INTERNAL,
				"%s: SDP SyncML channel for %s: %d",
				__func__, env->path, channel);
			
		}
#endif

		if (GET_OBEX_RESULT(BtOBEX_TransportConnect(env->obexhandle, BDADDR_ANY, &bdaddr, channel)) < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The Bluetooth connect failed. %s (%i).",
				strerror(errno), errno);
			goto error;
		}

		smlTrace(TRACE_INTERNAL, "%s: bluetooth connect done", __func__);
#else
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Bluetooth not enabled");
		goto error;
#endif
	} else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_IRDA) {
		smlTrace(TRACE_INTERNAL, "%s: connecting to IrDA service %s", __func__, env->irda_service ? env->irda_service : "OBEX");

		if (GET_OBEX_RESULT(IrOBEX_TransportConnect(env->obexhandle, env->irda_service)) < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The IrDA connect failed. %s (%i).",
				strerror(errno), errno);
			goto error;
		}

		smlTrace(TRACE_INTERNAL, "%s: IrDA connect done", __func__);
 	} else {
#ifndef WIN32
		struct termios tio;
	    memset(&tio, 0, sizeof(tio));
	
		/* Open the file descriptor */
		fd = open(env->path, O_RDWR | O_NOCTTY);
	    if (fd == -1) {
	    	g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to open %s: %s", env->path, strerror(errno));
	    	goto error;
	    }
	    
	    /* Lock it*/
	    if (lockf(fd, F_TLOCK, 0)) {
	    	g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to lock %s: %s", env->path, strerror(errno));
			goto error_close;
	    }
	    
	    if (tcgetattr(fd, &tio)) {
	    	g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to get attr from %s", env->path);
			goto error_unlock;
	    }
	    
	    /* Make the transmission raw (8 bit clean, no echo etc) */
	    /* Set the speed to a higher value (9600 would be default) */

#if defined(sun) && defined(__SVR4)
	    tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
	    tio.c_oflag &= ~OPOST;
	    tio.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
	    tio.c_cflag &= ~(CSIZE|PARENB);
	    tio.c_cflag |= CS8;
#else	    
	    cfmakeraw(&tio);	    
#endif	    

	    cfsetispeed(&tio, B115200);
	    cfsetospeed(&tio, B115200);
	
	    if (tcsetattr(fd, TCSANOW, &tio)) {
	    	g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to set attr from %s", env->path);
			goto error_unlock;
	    }
	    tcflush(fd, TCIFLUSH);
#endif
	}
	
	if (env->type != SML_TRANSPORT_CONNECTION_TYPE_USB
	 && env->type != SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH
	 && env->type != SML_TRANSPORT_CONNECTION_TYPE_IRDA) {
		/* Start the obex transport */
		if (GET_OBEX_RESULT(FdOBEX_TransportSetup(env->obexhandle, fd, fd, 4096)) < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The OBEX transport setup failed. %s (%i).",
				strerror(errno), errno);
			goto error_unlock;
		}
	}
	
	/* Make a new connect object */
	obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_CONNECT);
	if (!obj) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new connect object");
		goto error_transport_close;
	}

	/* Now add the header for the sync target */
	obex_headerdata_t header;
	header.bs = (unsigned char *)"SYNCML-SYNC";
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TARGET, header, strlen((char *)header.bs), OBEX_FL_FIT_ONE_PACKET);
    
	env->busy = TRUE;
	/* Now we need to send the request */
	if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
			"The OBEX request failed. %s (%i).",
			strerror(errno), errno);
		goto error_free_obj;
	}
    
	if (env->error) {
		smlTrace(TRACE_EXIT, "%s: Unable to send connect request. Bailing out", __func__);
		return;
	}
	
	/* Lets see if it was successfull */
	while (env->busy) {
		int result = OBEX_HandleInput(env->obexhandle, 20);
		if (result < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The connect request was not successful (%i).",
				result);
			goto error;
		} else if (result == 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The connect request was timed out.");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

error_free_obj:
	OBEX_ObjectDelete(env->obexhandle, obj);
error_transport_close:
	OBEX_Cleanup(env->obexhandle);
error_unlock:
#ifndef WIN32
	if (!lockf(fd, F_ULOCK, 0))
		smlTrace(TRACE_ERROR, "%s: error_unlock failed.", __func__);
#endif
error_close:
	close(fd);
error:
	smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, error->message);
	g_error_free(error);
}

static void smlTransportObexClientDisconnect(void *data, void *linkdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, linkdata);
	smlAssert(data);
	SmlTransportObexClientEnv *env = data;
	GError *error = NULL;

	if (env->state < SML_TRANSPORT_CONNECTED) {
		/* This is a bug. If the transport is not connected
		 * then there is no reason for a disconnect.
		 * Nevertheless this mistake should not be fatal.
		 */
		smlTrace(TRACE_EXIT_ERROR,
			"%s - disconnect called on a never connected transport",
			__func__);
		g_warning("%s called on never connected OBEX connection.", __func__);
		return;
	}

	if (env->state == SML_TRANSPORT_DISCONNECTED) {
		/* This is a bug. If the transport is disconnected
		 * then there is no reason for a second disconnect.
		 * Nevertheless this mistake should not be fatal.
		 */
		smlTrace(TRACE_EXIT_ERROR,
			"%s - disconnect called on an already disconnected transport",
			__func__);
		g_warning("%s called on disconnected OBEX connection.", __func__);
		return;
	}
	
	env->error = FALSE;

	/* Make a new disconnect object */
	obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_DISCONNECT);
	if (!obj) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new disconnect object");
		goto error;
	}
	
	/* Add the connection id */
	obex_headerdata_t header;
	header.bq4 = env->connection_id;
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);

	env->busy = TRUE;
	/* Now we need to send the request */
	if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
			"The OBEX request cannot be sent. %s (%i).",
			strerror(errno), errno);
		goto error_free_obj;
	}
	
	if (env->error) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to send disconnect request. Bailing out");
		goto error;
	}
    
	/* Lets see if it was successfull. We wait a certain period of
	 * time (3 sec) for the answer to the disconnect. If we dont receive
	 * the answer we just disconnect anyways. Some phones never answer
	 * with a A0 (success) to the disconnect request, so we must have
	 * a timeout here */
	int maxLoop = 3;
	int i = 0;
	while (env->busy) {
		smlTrace(TRACE_INTERNAL, "%s: Disconnect loop %i", __func__, i);
		int result = OBEX_HandleInput(env->obexhandle, 1);
		/* timeout and success are not relevant */
		if (result < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The disconnect request was not accepted (%i).",
				result);
			goto error;
		} else if (result == 0 && !env->busy && env->state != SML_TRANSPORT_DISCONNECTED) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The disconnect request was timed out.");
			goto error;
		}

		if (i == maxLoop) {
			smlTrace(TRACE_INTERNAL, "%s: Did not receive a response to our disconnect", __func__);
			break;
		}
		i++;
	}

	/* Note: The disconnect is signaled by OBEX_EV_LINKERR or OBEX_CMD_DISCONNECT.
	 *       We only signal disconnect here if a phone does not answer. */
	if (env->state < SML_TRANSPORT_DISCONNECTED)
	{
		env->busy = FALSE;
		OBEX_TransportDisconnect(env->obexhandle);
		env->state = SML_TRANSPORT_DISCONNECTED;
		smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
	
error_free_obj:
	OBEX_ObjectDelete(env->obexhandle, obj);
error:
	smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, error->message);
	g_error_free(error);
}

static void
smlTransportObexClientSend (void *userdata,
                            void *link_,
                            SmlTransportData *data,
                            GError *error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, userdata, link_, data, error);
	smlAssert(error || data);
	smlAssert(userdata);
	SmlTransportObexClientEnv *env = userdata;
	smlAssert(env);
	smlTrace(TRACE_INTERNAL, "%s: tsp %p)", __func__, env->tsp);
	
	if (error)
		goto error;
	
	env->error = FALSE;
	env->mimetype = data->type;
	
	/* Make a new put command */
	obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_PUT);
	if (!obj) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new put object");
		goto error;
	}

	/* Now add the header for the put target */
	obex_headerdata_t header;
	
	smlTrace(TRACE_INTERNAL, "%s: Adding connection id %i", __func__, env->connection_id);
	
	/* Add the connection id */
	header.bq4 = env->connection_id;
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);
	
	const char *target = NULL;
	switch (data->type) {
		case SML_MIMETYPE_WBXML:
			target = SML_ELEMENT_WBXML;
			break;
		case SML_MIMETYPE_XML:
			target = SML_ELEMENT_XML;
			break;
		case SML_MIMETYPE_SAN:
			target = SML_ELEMENT_SAN;
			break;
		default:
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unknown mime type");
			goto error_free_obj;
	}
	smlTrace(TRACE_INTERNAL, "%s: Target %s", __func__, VA_STRING(target));
	
	/* Now convert to unicode. It requires 2 bytes per char + end*/
	/*unsigned char *unicode = g_malloc0(2 * strlen(target) + 2);

	unsigned int unicodesize = OBEX_CharToUnicode(unicode, (const unsigned char *)target, 2 * strlen(target) + 2);
	if (unicodesize == -1) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "unable to convert to unicode");
		goto error_free_obj;
	}*/
	
	/* Now add the target mime type */
	/*header.bs = (unsigned char *)unicode;
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, unicodesize, 0);*/
	header.bs = (unsigned char *)target;
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, strlen(target) + 1, 0);
	
	/* Now the data and size */
	header.bq4 = (uint32_t)data->size;
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_LENGTH, header, sizeof(uint32_t), 0);
	
	header.bs = (unsigned char *)data->data;
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_BODY, header, data->size, 0);
	
	env->busy = TRUE;
	/* Now we need to send the request */
	if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
			"The OBEX request cannot be sent. %s (%i).",
			strerror(errno), errno);
		goto error_free_obj;
	}
    
	/* Lets see if it was successfull */
	while (env->busy) {
		int result = OBEX_HandleInput(env->obexhandle, 20);
		if (result < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The request was not successful (%d).",
				result);
			goto error;
		} else if (result == 0 && !env->busy) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The request was timed out.");
			goto error;
		}
	}
	
	if (env->error) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to send put request. Bailing out");
		goto error;
	}
	
	smlTrace(TRACE_INTERNAL, "%s: Done sending the put request", __func__);
	
	if (!data->needsAnswer) {
		smlTrace(TRACE_EXIT, "%s: No answer is needed", __func__);
		return;
	}
	
	/* Make a new get command */
	obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_GET);
	if (!obj) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new get object");
		goto error;
	}

	/* Now add the header for the get target */
	smlTrace(TRACE_INTERNAL, "%s: Adding connection id %i", __func__, env->connection_id);
	
	/* Add the connection id */
	header.bq4 = env->connection_id;
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);

	/* If smlTransportData.get_type is set, switch the mimetype for the GET command.
	   This is needed for SyncML 1.2 SAN notificion:

	   If this obex send sequence sends SyncML 1.2 SAN notification, switch the
	   mimetype back to regular mimetype (not the SAN mimetype). The mimtype for the
	   regular sync got stored in smlTransportData.type_get */

	if (data->type_get != SML_MIMETYPE_UNKNOWN)
	{
		switch (data->type_get) {
			case SML_MIMETYPE_WBXML:
				target = SML_ELEMENT_WBXML;
				break;
			case SML_MIMETYPE_XML:
				target = SML_ELEMENT_XML;
				break;
			case SML_MIMETYPE_SAN:
				target = SML_ELEMENT_SAN;
				break;
			default:
				g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC, "Unknown mime type");
				goto error_free_obj;
		}

		/* After this point, the entire conversion will be XML or WBXML. No more SAN mimetype. */
		env->mimetype = data->type_get;

		smlTrace(TRACE_INTERNAL, "%s: Switch to new target: %s", __func__, VA_STRING(target));
	}

	/* Now add the target mime type */
	//header.bs = (unsigned char *)unicode;
	//OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, unicodesize, 0);

	header.bs = (unsigned char *)target;
	OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, strlen(target) + 1, 0);
	
	env->busy = TRUE;
	/* Now we need to send the request */
	if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
		g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
			"The OBEX request cannot be sent. %s (%i).",
			strerror(errno), errno);
		goto error_free_obj;
	}
    
	/* Lets see if it was successfull */
	smlTrace(TRACE_INTERNAL, "%s: Wait for the answer ...", __func__);
	while (env->busy) {
		int result = OBEX_HandleInput(env->obexhandle, 20);
		if (result < 0) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The answer was not received successfully (%i).",
				result);
			goto error;
		} else if (result == 0 && !env->busy) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
				"The answer was timed out.");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
	
error_free_obj:
	OBEX_ObjectDelete(env->obexhandle, obj);
error:
	smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, error->message);
	g_error_free(error);
	return;
}

gboolean
smlTransportObexClientNew (SmlTransport *tsp,
                           GError **error)
{
	CHECK_ERROR_REF
	smlAssert(tsp);

	tsp->functions.set_config_option = smlTransportObexClientSetConfigOption;
	tsp->functions.set_connection_type = smlTransportObexClientSetConnectionType;
	tsp->functions.initialize = smlTransportObexClientInit;
	tsp->functions.finalize = smlTransportObexClientFinalize;
	tsp->functions.connect = smlTransportObexClientConnect;
	tsp->functions.disconnect = smlTransportObexClientDisconnect;
	tsp->functions.send = smlTransportObexClientSend;

	SmlTransportObexClientEnv *env = smlTryMalloc0(sizeof(SmlTransportObexClientEnv), error);
	if (!env)
		return FALSE;
	tsp->transport_data = env;
	env->tsp = tsp;
	env->state = SML_TRANSPORT_UNINITIALIZED;

	return TRUE;
}

