Diff between 855f220dae1c829b7005d8bec6ca7a89d9eb5cf8 and 6984ba9a5745a34c3206274880ffa1f93baef679

Changed Files

File Additions Deletions Status
obexd/src/bluetooth.c +299 -0 added
obexd/src/bluetooth.h +32 -0 added
obexd/src/dbus.h +36 -0 added
obexd/src/ftp.c +203 -0 added
obexd/src/logging.c +219 -0 added
obexd/src/logging.h +32 -0 added
obexd/src/main.c +221 -0 added
obexd/src/manager.c +368 -0 added
obexd/src/obex.c +765 -0 added
obexd/src/obex.h +76 -0 added
obexd/src/obexd.h +38 -0 added
obexd/src/opp.c +189 -0 added

Full Patch

diff --git a/obexd/src/bluetooth.c b/obexd/src/bluetooth.c
new file mode 100644
index 0000000..48e7936
--- /dev/null
+++ b/obexd/src/bluetooth.c
@@ -0,0 +1,299 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Nokia Corporation
+ *  Copyright (C) 2007-2008  Instituto Nokia de Tecnologia (INdT)
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "obex.h"
+
+static GSList *handles = NULL;
+static sdp_session_t *session = NULL;
+
+static void add_lang_attr(sdp_record_t *r)
+{
+	sdp_lang_attr_t base_lang;
+	sdp_list_t *langs = 0;
+
+	/* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+	base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+	base_lang.encoding = 106;
+	base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+	langs = sdp_list_append(0, &base_lang);
+	sdp_set_lang_attr(r, langs);
+	sdp_list_free(langs, 0);
+}
+
+static uint32_t register_record(const gchar *name,
+				guint16 service, guint8 channel)
+{
+	uuid_t root_uuid, uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+	sdp_list_t *root, *svclass_id, *apseq, *profiles, *aproto, *proto[3];
+	sdp_data_t *sdp_data;
+	sdp_profile_desc_t profile;
+	sdp_record_t record;
+	uint8_t formats = 0xFF;
+	int ret;
+
+	switch (service) {
+	case OBEX_OPUSH:
+		sdp_uuid16_create(&uuid, OBEX_OBJPUSH_SVCLASS_ID);
+		sdp_uuid16_create(&profile.uuid, OBEX_OBJPUSH_PROFILE_ID);
+		break;
+	case OBEX_FTP:
+		sdp_uuid16_create(&uuid, OBEX_FILETRANS_SVCLASS_ID);
+		sdp_uuid16_create(&profile.uuid, OBEX_FILETRANS_PROFILE_ID);
+		break;
+	default:
+		return 0;
+	}
+
+	/* Browse Groups */
+	memset(&record, 0, sizeof(sdp_record_t));
+	record.handle = 0xffffffff;
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(&record, root);
+	sdp_list_free(root, NULL);
+
+	/* Service Class */
+	svclass_id = sdp_list_append(NULL, &uuid);
+	sdp_set_service_classes(&record, svclass_id);
+	sdp_list_free(svclass_id, NULL);
+
+	/* Profile Descriptor */
+	profile.version = 0x0100;
+	profiles = sdp_list_append(NULL, &profile);
+	sdp_set_profile_descs(&record, profiles);
+	sdp_list_free(profiles, NULL);
+
+	/* Protocol Descriptor */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+	apseq = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+	sdp_data = sdp_data_alloc(SDP_UINT8, &channel);
+	proto[1] = sdp_list_append(proto[1], sdp_data);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+	proto[2] = sdp_list_append(NULL, &obex_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(&record, aproto);
+
+	sdp_data_free(sdp_data);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(proto[2], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(aproto, NULL);
+
+	/* Suported Repositories */
+	if (service == OBEX_OPUSH)
+		sdp_attr_add_new(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST,
+				SDP_UINT8, &formats);
+
+	/* Service Name */
+	sdp_set_info_attr(&record, name, NULL, NULL);
+
+	add_lang_attr(&record);
+
+	ret = sdp_record_register(session, &record, SDP_RECORD_PERSIST);
+
+	sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free);
+	sdp_list_free(record.pattern, free);
+
+	return (ret < 0 ? 0 : record.handle);
+}
+
+static gboolean connect_event(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+	struct sockaddr_rc raddr;
+	socklen_t alen;
+	struct server *server = user_data;
+	gchar address[18];
+	gint err, sk, nsk;
+
+	sk = g_io_channel_unix_get_fd(io);
+	alen = sizeof(raddr);
+	nsk = accept(sk, (struct sockaddr *) &raddr, &alen);
+	if (nsk < 0)
+		return TRUE;
+
+	alen = sizeof(raddr);
+	if (getpeername(nsk, (struct sockaddr *)&raddr, &alen) < 0) {
+		err = errno;
+		error("getpeername(): %s(%d)", strerror(err), err);
+		close(nsk);
+		return TRUE;
+	}
+
+	ba2str(&raddr.rc_bdaddr, address);
+	info("New connection from: %s channel: %d", address, raddr.rc_channel);
+
+	if (obex_session_start(nsk, server) < 0)
+		close(nsk);
+
+	return TRUE;
+}
+
+static void server_destroyed(gpointer user_data)
+{
+	struct server *server = user_data;
+
+	error("Server destroyed");
+
+	g_free(server->folder);
+	g_free(server);
+}
+
+static gint server_register(guint16 service, const gchar *name,
+		guint8 channel, const gchar *folder, gboolean auto_accept)
+{
+	struct sockaddr_rc laddr;
+	GIOChannel *io;
+	gint err, sk, arg;
+	struct server *server;
+	uint32_t *handle;
+
+	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+	if (sk < 0) {
+		err = errno;
+		error("socket(): %s(%d)", strerror(err), err);
+		return -err;
+	}
+
+	arg = fcntl(sk, F_GETFL);
+	if (arg < 0) {
+		err = errno;
+		goto failed;
+	}
+
+	arg |= O_NONBLOCK;
+	if (fcntl(sk, F_SETFL, arg) < 0) {
+		err = errno;
+		goto failed;
+	}
+
+	memset(&laddr, 0, sizeof(laddr));
+	laddr.rc_family = AF_BLUETOOTH;
+	bacpy(&laddr.rc_bdaddr, BDADDR_ANY);
+	laddr.rc_channel = channel;
+
+	if (bind(sk, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+		err = errno;
+		goto failed;
+	}
+
+	if (listen(sk, 10) < 0) {
+		err = errno;
+		goto failed;
+	}
+
+	handle = malloc(sizeof(uint32_t));
+	*handle = register_record(name, service, channel);
+	if (*handle == 0) {
+		g_free(handle);
+		err = EIO;
+		goto failed;
+	}
+
+	handles = g_slist_prepend(handles, handle);
+
+	server = g_malloc0(sizeof(struct server));
+	server->service = service;
+	server->folder = g_strdup(folder);
+	server->auto_accept = auto_accept;
+
+	io = g_io_channel_unix_new(sk);
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+			G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+			connect_event, server, server_destroyed);
+	g_io_channel_unref(io);
+
+	debug("Registered: %s, record handle: 0x%x, folder: %s", name, *handle, folder);
+
+	return 0;
+
+failed:
+	error("Bluetooth server register failed: %s(%d)", strerror(err), err);
+	close(sk);
+
+	return -err;
+}
+
+gint bluetooth_init(guint service, const gchar *name, const gchar *folder,
+					guint8 channel, gboolean auto_accept)
+{
+	if (!session) {
+		session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+		if (!session) {
+			gint err = errno;
+			error("sdp_connect(): %s(%d)", strerror(err), err);
+			return -err;
+		}
+	}
+
+	return server_register(service, name, channel, folder, auto_accept);
+}
+
+static void unregister_record(gpointer rec_handle, gpointer user_data)
+{
+	uint32_t *handle = rec_handle;
+
+	sdp_device_record_unregister_binary(session, BDADDR_ANY, *handle);
+	g_free(handle);
+}
+
+void bluetooth_exit(void)
+{
+	g_slist_foreach(handles, unregister_record, NULL);
+	g_slist_free(handles);
+
+	sdp_close(session);
+}
diff --git a/obexd/src/bluetooth.h b/obexd/src/bluetooth.h
new file mode 100644
index 0000000..9e7973b
--- /dev/null
+++ b/obexd/src/bluetooth.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Nokia Corporation
+ *  Copyright (C) 2007-2008  Instituto Nokia de Tecnologia (INdT)
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+gint bluetooth_init(guint service, const gchar *name, const gchar *folder,
+			guint8 channel, gboolean auto_accept);
+void bluetooth_exit(void);
diff --git a/obexd/src/dbus.h b/obexd/src/dbus.h
new file mode 100644
index 0000000..a4234c2
--- /dev/null
+++ b/obexd/src/dbus.h
@@ -0,0 +1,36 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Instituto Nokia de Tecnologia (INdT)
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void emit_transfer_started(guint32 id);
+
+void emit_transfer_completed(guint32 id, gboolean success);
+
+void emit_transfer_progress(guint32 id, guint32 total, guint32 transfered);
+
+int request_authorization(gint32 cid, int fd, const gchar *filename,
+			const gchar *type, gint32 length, gint32 time,
+			gchar **new_folder, gchar **new_name);
+
+void register_transfer(guint32 id);
+
+void unregister_transfer(guint32 id);
diff --git a/obexd/src/ftp.c b/obexd/src/ftp.c
new file mode 100644
index 0000000..04ab623
--- /dev/null
+++ b/obexd/src/ftp.c
@@ -0,0 +1,203 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Nokia Corporation
+ *  Copyright (C) 2007-2008  Instituto Nokia de Tecnologia (INdT)
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "obex.h"
+#include "logging.h"
+
+#define LST_TYPE "x-obex/folder-listing"
+#define CAP_TYPE "x-obex/capability"
+
+#define CAP_FILE CONFIGDIR "/capability.xml"
+
+static gboolean get_by_type(struct obex_session *os, gchar *type, guint32 *size)
+{
+	if (g_str_equal(type, CAP_TYPE))
+		return os_prepare_get(os, CAP_FILE, size);
+
+	return FALSE;
+}
+
+void ftp_get(obex_t *obex, obex_object_t *obj)
+{
+	obex_headerdata_t hv;
+	struct obex_session *os;
+	guint32 size;
+
+	os = OBEX_GetUserData(obex);
+	if (os == NULL)
+		return;
+
+	if (os->current_folder == NULL)
+		goto fail;
+
+	if (os->name) {
+		gboolean ret;
+		gchar *path = g_build_filename(os->current_folder,
+						os->name, NULL);
+		ret = os_prepare_get(os, path, &size);
+
+		g_free(path);
+
+		if (!ret)
+			goto fail;
+	} else if (os->type) {
+		if (!get_by_type(os, os->type, &size))
+			goto fail;
+	} else
+		goto fail;
+
+	hv.bq4 = size;
+	OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_LENGTH, hv, 4, 0);
+
+	/* Add body header */
+	hv.bs = NULL;
+	if (size == 0)
+		OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
+				hv, 0, OBEX_FL_FIT_ONE_PACKET);
+	else
+		OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
+				hv, 0, OBEX_FL_STREAM_START);
+
+	OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE,
+			OBEX_RSP_SUCCESS);
+
+	return;
+
+fail:
+	OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+
+	return;
+}
+
+void ftp_put(obex_t *obex, obex_object_t *obj)
+{
+	OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+			OBEX_RSP_NOT_IMPLEMENTED);
+}
+
+void ftp_setpath(obex_t *obex, obex_object_t *obj)
+{
+	struct obex_session *os;
+	guint8 *nonhdr;
+	gchar *fullname;
+
+	os = OBEX_GetUserData(obex);
+
+	if (OBEX_ObjectGetNonHdrData(obj, &nonhdr) != 2) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE,
+				OBEX_RSP_PRECONDITION_FAILED);
+		error("Set path failed: flag and constants not found!");
+		return;
+	}
+
+	/* Check flag "Backup" */
+	if ((nonhdr[0] & 0x01) == 0x01) {
+
+		debug("Set to parent path");
+
+		if (strcmp(os->server->folder, os->current_folder) == 0) {
+			OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+			return;
+		}
+
+		fullname = g_path_get_dirname(os->current_folder);
+		g_free(os->current_folder);
+		os->current_folder = g_strdup(fullname);
+		g_free(fullname);
+
+		debug("Set to parent path: %s", os->current_folder);
+
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+		return;
+	}
+
+	if (!os->name) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_BAD_REQUEST);
+		debug("Set path failed: name missing!");
+		return;
+	}
+
+	if (strlen(os->name) == 0) {
+		debug("Set to root");
+		g_free(os->current_folder);
+		os->current_folder = g_strdup(os->server->folder);
+
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+		return;
+	}
+
+	/* Check and set to name path */
+	if (strstr(os->name, "/") || strcmp(os->name, "..") == 0) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+		error("Set path failed: name incorrect!");
+		return;
+	}
+
+	fullname = g_build_filename(os->current_folder, os->name, NULL);
+
+	debug("Fullname: %s", fullname);
+
+	if (g_file_test(fullname, G_FILE_TEST_IS_DIR)) {
+		g_free(os->current_folder);
+		os->current_folder = g_strdup(fullname);
+
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+		goto done;
+	}
+
+	if (!g_file_test(fullname, G_FILE_TEST_EXISTS) && nonhdr[0] == 0 &&
+				mkdir(fullname, 0755) >=  0) {
+		g_free(os->current_folder);
+		os->current_folder = g_strdup(fullname);
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+		goto done;
+
+	}
+
+	OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+done:
+	g_free(fullname);
+}
diff --git a/obexd/src/logging.c b/obexd/src/logging.c
new file mode 100644
index 0000000..c1e489c
--- /dev/null
+++ b/obexd/src/logging.c
@@ -0,0 +1,219 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+#include <openobex/obex.h>
+
+static volatile int debug_enabled = 0;
+
+static inline void vinfo(const char *format, va_list ap)
+{
+	vsyslog(LOG_INFO, format, ap);
+}
+
+void info(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	vinfo(format, ap);
+
+	va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+
+	vsyslog(LOG_ERR, format, ap);
+
+	va_end(ap);
+}
+
+void debug(const char *format, ...)
+{
+	va_list ap;
+
+	if (!debug_enabled)
+		return;
+
+	va_start(ap, format);
+
+	vsyslog(LOG_DEBUG, format, ap);
+
+	va_end(ap);
+}
+
+void toggle_debug(void)
+{
+	debug_enabled = (debug_enabled + 1) % 2;
+}
+
+void enable_debug(void)
+{
+	debug_enabled = 1;
+}
+
+void disable_debug(void)
+{
+	debug_enabled = 0;
+}
+
+void start_logging(const char *ident, const char *message, ...)
+{
+	va_list ap;
+
+	openlog(ident, LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+
+	va_start(ap, message);
+
+	vinfo(message, ap);
+
+	va_end(ap);
+}
+
+void stop_logging(void)
+{
+	closelog();
+}
+
+static struct {
+	int		evt;
+	const char	*name;
+} obex_event[] = {
+	{ OBEX_EV_PROGRESS,	"PROGRESS"	},	/* Progress has been made */
+	{ OBEX_EV_REQHINT,	"REQHINT"	},	/* An incoming request is about to come */
+	{ OBEX_EV_REQ,		"REQ"		},	/* An incoming request has arrived */
+	{ OBEX_EV_REQDONE,	"REQDONE"	},	/* Request has finished */
+	{ OBEX_EV_LINKERR,	"LINKERR"	},	/* Link has been disconnected */
+	{ OBEX_EV_PARSEERR,	"PARSEERR"	},	/* Malformed data encountered */
+	{ OBEX_EV_ACCEPTHINT, 	"ACCEPTHINT"	},	/* Connection accepted */
+	{ OBEX_EV_ABORT, 	"ABORT"		},	/* Request was aborted */
+	{ OBEX_EV_STREAMEMPTY, 	"STREAMEMPTY"	},	/* Need to feed more data when sending a stream */
+	{ OBEX_EV_STREAMAVAIL,	"STREAMAVAIL"	},	/* Time to pick up data when receiving a stream */
+	{ OBEX_EV_UNEXPECTED,	"UNEXPECTED"	},	/* Unexpected data, not fatal */
+	{ OBEX_EV_REQCHECK,	"REQCHECK"	},	/* First packet of an incoming request has been parsed */
+	{ 0xFF,			NULL		},
+};
+
+/* Possible commands */
+static struct {
+	int		cmd;
+	const char	*name;
+} obex_command[] = {
+	{ OBEX_CMD_CONNECT,	"CONNECT"	},
+	{ OBEX_CMD_DISCONNECT,	"DISCONNECT"	},
+	{ OBEX_CMD_PUT,		"PUT"		},
+	{ OBEX_CMD_GET,		"GET"		},
+	{ OBEX_CMD_SETPATH,	"SETPATH"	},
+	{ OBEX_CMD_SESSION,	"SESSION"	},
+	{ OBEX_CMD_ABORT,	"ABORT"		},
+	{ OBEX_FINAL,		"FINAL"		},
+	{ 0xFF,			NULL		},
+};
+
+/* Possible Response */
+static struct {
+	int		rsp;
+	const char	*name;
+} obex_response[] = {
+	{ OBEX_RSP_CONTINUE,			"CONTINUE"		},
+	{ OBEX_RSP_SWITCH_PRO,			"SWITCH_PRO"		},
+	{ OBEX_RSP_SUCCESS,			"SUCCESS"		},
+	{ OBEX_RSP_CREATED, 			"CREATED"		},
+	{ OBEX_RSP_ACCEPTED,			"ACCEPTED"		},
+	{ OBEX_RSP_NON_AUTHORITATIVE,		"NON_AUTHORITATIVE"	},
+	{ OBEX_RSP_NO_CONTENT,			"NO_CONTENT"		},
+	{ OBEX_RSP_RESET_CONTENT,		"RESET_CONTENT"		},
+	{ OBEX_RSP_PARTIAL_CONTENT,		"PARTIAL_CONTENT"	},
+	{ OBEX_RSP_MULTIPLE_CHOICES,		"MULTIPLE_CHOICES"	},
+	{ OBEX_RSP_MOVED_PERMANENTLY,		"MOVED_PERMANENTLY"	},
+	{ OBEX_RSP_MOVED_TEMPORARILY,		"MOVED_TEMPORARILY"	},
+	{ OBEX_RSP_SEE_OTHER,			"SEE_OTHER"		},
+	{ OBEX_RSP_NOT_MODIFIED,		"NOT_MODIFIED"		},
+	{ OBEX_RSP_USE_PROXY,			"USE_PROXY"		},
+	{ OBEX_RSP_BAD_REQUEST,			"BAD_REQUEST"		},
+	{ OBEX_RSP_UNAUTHORIZED,		"UNAUTHORIZED"		},
+	{ OBEX_RSP_PAYMENT_REQUIRED,		"PAYMENT_REQUIRED"	},
+	{ OBEX_RSP_FORBIDDEN,			"FORBIDDEN"		},
+	{ OBEX_RSP_NOT_FOUND,			"NOT_FOUND"		},
+	{ OBEX_RSP_METHOD_NOT_ALLOWED,		"METHOD_NOT_ALLOWED"	},
+	{ OBEX_RSP_NOT_ACCEPTABLE,		"NOT_ACCEPTABLE"	},
+	{ OBEX_RSP_PROXY_AUTH_REQUIRED,		"PROXY_AUTH_REQUIRED"	},
+	{ OBEX_RSP_REQUEST_TIME_OUT,		"REQUEST_TIME_OUT"	},
+	{ OBEX_RSP_CONFLICT,			"CONFLICT"		},
+	{ OBEX_RSP_GONE,			"GONE"			},
+	{ OBEX_RSP_LENGTH_REQUIRED,		"LENGTH_REQUIRED"	},
+	{ OBEX_RSP_PRECONDITION_FAILED,		"PRECONDITION_FAILED"	},
+	{ OBEX_RSP_REQ_ENTITY_TOO_LARGE,	"REQ_ENTITY_TOO_LARGE"	},
+	{ OBEX_RSP_REQ_URL_TOO_LARGE,		"REQ_URL_TOO_LARGE"	},
+	{ OBEX_RSP_UNSUPPORTED_MEDIA_TYPE,	"UNSUPPORTED_MEDIA_TYPE"},
+	{ OBEX_RSP_INTERNAL_SERVER_ERROR,	"INTERNAL_SERVER_ERROR"	},
+	{ OBEX_RSP_NOT_IMPLEMENTED,		"NOT_IMPLEMENTED"	},
+	{ OBEX_RSP_BAD_GATEWAY,			"BAD_GATEWAY"		},
+	{ OBEX_RSP_SERVICE_UNAVAILABLE,		"SERVICE_UNAVAILABLE"	},
+	{ OBEX_RSP_GATEWAY_TIMEOUT,		"GATEWAY_TIMEOUT"	},
+	{ OBEX_RSP_VERSION_NOT_SUPPORTED,	"VERSION_NOT_SUPPORTED"	},
+	{ OBEX_RSP_DATABASE_FULL,		"DATABASE_FULL"		},
+	{ OBEX_RSP_DATABASE_LOCKED,		"DATABASE_LOCKED"	},
+	{ 0xFF,					NULL			},
+};
+
+void obex_debug(int evt, int cmd, int rsp)
+{
+	const char *evtstr = NULL, *cmdstr = NULL, *rspstr = NULL;
+	int i;
+
+	if (!debug_enabled)
+		return;
+
+	for (i = 0; obex_event[i].evt != 0xFF; i++) {
+		if (obex_event[i].evt != evt)
+			continue;
+		evtstr = obex_event[i].name;
+	}
+
+	for (i = 0; obex_command[i].cmd != 0xFF; i++) {
+		if (obex_command[i].cmd != cmd)
+			continue;
+		cmdstr = obex_command[i].name;
+	}
+
+	for (i = 0; obex_response[i].rsp != 0xFF; i++) {
+		if (obex_response[i].rsp != rsp)
+			continue;
+		rspstr = obex_response[i].name;
+	}
+
+	debug("%s(0x%x), %s(0x%x), %s(0x%x)", evtstr, evt, cmdstr, cmd, rspstr, rsp);
+}
diff --git a/obexd/src/logging.h b/obexd/src/logging.h
new file mode 100644
index 0000000..f30628a
--- /dev/null
+++ b/obexd/src/logging.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void info(const char *format, ...);
+void error(const char *format, ...);
+void debug(const char *format, ...);
+void obex_debug(int evt, int cmd, int rsp);
+void toggle_debug(void);
+void enable_debug(void);
+void disable_debug(void);
+void start_logging(const char *ident, const char *message, ...);
+void stop_logging(void);
diff --git a/obexd/src/main.c b/obexd/src/main.c
new file mode 100644
index 0000000..1ff0a0c
--- /dev/null
+++ b/obexd/src/main.c
@@ -0,0 +1,221 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "bluetooth.h"
+#include "obexd.h"
+#include "obex.h"
+
+#define OPUSH_CHANNEL	9
+#define FTP_CHANNEL	10
+
+#define DEFAULT_ROOT_PATH "/tmp"
+
+static GMainLoop *main_loop = NULL;
+
+static int server_start(int service, const char *root_path,
+			gboolean auto_accept)
+{
+	/* FIXME: Necessary check enabled transports(Bluetooth/USB) */
+
+	switch (service) {
+	case OBEX_OPUSH:
+		bluetooth_init(OBEX_OPUSH, "OBEX OPUSH server",
+				root_path, OPUSH_CHANNEL, auto_accept);
+		break;
+	case OBEX_FTP:
+		bluetooth_init(OBEX_FTP, "OBEX FTP server",
+				root_path, FTP_CHANNEL, auto_accept);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void server_stop()
+{
+	/* FIXME: If Bluetooth enabled */
+	bluetooth_exit();
+}
+
+static void sig_term(int sig)
+{
+	g_main_loop_quit(main_loop);
+}
+
+static void usage(void)
+{
+	printf("OBEX Server version %s\n\n", VERSION);
+
+	printf("Usage:\n"
+		"\tobexd [options] <server>\n"
+		"\n");
+
+	printf("Options:\n"
+		"\t-n, --nodaemon       Don't fork daemon to background\n"
+		"\t-d, --debug          Enable output of debug information\n"
+		"\t-r, --root <path>    Specify root folder location\n"
+		"\t-a, --auto-accept    Automatically accept push requests\n"
+		"\t-h, --help           Display help\n");
+	printf("Servers:\n"
+		"\t-o, --opp            Enable OPP server\n"
+		"\t-f, --ftp            Enable FTP server\n"
+		"\n");
+}
+
+static struct option options[] = {
+	{ "nodaemon", 0, 0, 'n' },
+	{ "debug",    0, 0, 'd' },
+	{ "ftp",      0, 0, 'f' },
+	{ "opp",      0, 0, 'o' },
+	{ "help",     0, 0, 'h' },
+	{ "root",     1, 0, 'r' },
+	{ "auto-accept", 0, 0, 'a' },
+	{ }
+};
+
+int main(int argc, char *argv[])
+{
+	DBusConnection *conn;
+	DBusError err;
+	struct sigaction sa;
+	int log_option = LOG_NDELAY | LOG_PID;
+	int opt, detach = 1, debug = 0, opush = 0, ftp = 0, auto_accept = 0;
+	const char *root_path = DEFAULT_ROOT_PATH;
+
+	while ((opt = getopt_long(argc, argv, "+ndhofr:a", options, NULL)) != EOF) {
+		switch(opt) {
+		case 'n':
+			detach = 0;
+			break;
+		case 'd':
+			debug = 1;
+			break;
+		case 'h':
+		case 'o':
+			opush = 1;
+			break;
+		case 'f':
+			ftp = 1;
+			break;
+		case 'r':
+			root_path = optarg;
+			break;
+		case 'a':
+			auto_accept = 1;
+			break;
+		default:
+			usage();
+			exit(0);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	if (!(opush || ftp)) {
+		fprintf(stderr, "No server selected (use either "
+					"--opp or --ftp or both)\n");
+		exit(1);
+	}
+
+	if (detach) {
+		if (daemon(0, 0)) {
+			perror("Can't start daemon");
+			exit(1);
+		}
+	} else
+		log_option |= LOG_PERROR;
+
+	openlog("obexd", log_option, LOG_DAEMON);
+
+	if (debug) {
+		info("Enabling debug information");
+		enable_debug();
+	}
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	dbus_error_init(&err);
+
+	conn = g_dbus_setup_bus(DBUS_BUS_SESSION, OPENOBEX_SERVICE, &err);
+	if (conn == NULL) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			fprintf(stderr, "%s\n", err.message);
+			dbus_error_free(&err);
+		} else
+			fprintf(stderr, "Can't register with session bus\n");
+		exit(1);
+	}
+
+	if (opush)
+		server_start(OBEX_OPUSH, root_path, auto_accept);
+
+	if (ftp)
+		server_start(OBEX_FTP, root_path, auto_accept);
+
+	if (!manager_init(conn))
+		goto fail;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = sig_term;
+	sigaction(SIGINT, &sa, NULL);
+	sigaction(SIGTERM, &sa, NULL);
+
+	g_main_loop_run(main_loop);
+
+	manager_cleanup();
+
+	server_stop();
+
+fail:
+	dbus_connection_unref(conn);
+
+	g_main_loop_unref(main_loop);
+
+	closelog();
+
+	return 0;
+}
diff --git a/obexd/src/manager.c b/obexd/src/manager.c
new file mode 100644
index 0000000..e165c80
--- /dev/null
+++ b/obexd/src/manager.c
@@ -0,0 +1,368 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+
+#include <string.h>
+#include <errno.h>
+#include <gdbus.h>
+#include <sys/socket.h>
+
+#include "obexd.h"
+#include "logging.h"
+
+#define TRANSFER_INTERFACE OPENOBEX_SERVICE ".Transfer"
+
+#define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */
+
+struct agent {
+	gchar		*bus_name;
+	gchar		*path;
+	gboolean	auth_pending;
+	gchar		*new_name;
+	gchar		*new_folder;
+};
+
+static struct agent *agent = NULL;
+
+static void agent_free(struct agent *agent)
+{
+	g_free(agent->new_folder);
+	g_free(agent->new_name);
+	g_free(agent->bus_name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".InvalidArguments",
+			"Invalid arguments in method call");
+}
+
+static inline DBusMessage *agent_already_exists(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".AlreadyExists",
+			"Agent already exists");
+}
+
+static inline DBusMessage *agent_does_not_exist(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".DoesNotExist",
+			"Agent does not exist");
+}
+
+static inline DBusMessage *not_authorized(DBusMessage *msg)
+{
+	return g_dbus_create_error(msg,
+			ERROR_INTERFACE ".NotAuthorized",
+			"Not authorized");
+}
+
+static void agent_disconnected(void *user_data)
+{
+	debug("Agent exited");
+	agent_free(agent);
+	agent = NULL;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	const gchar *path, *sender;
+
+	if (agent)
+		return agent_already_exists(msg);
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID))
+		return invalid_args(msg);
+
+	sender = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	agent->bus_name = g_strdup(sender);
+	agent->path = g_strdup(path);
+
+	g_dbus_add_disconnect_watch(conn, sender,
+			agent_disconnected, NULL, NULL);
+
+	debug("Agent registered");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	const gchar *path, *sender;
+
+	if (!agent)
+		return agent_does_not_exist(msg);
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID))
+		return invalid_args(msg);
+
+	if (strcmp(agent->path, path) != 0)
+		return agent_does_not_exist(msg);
+
+	sender = dbus_message_get_sender(msg);
+	if (strcmp(agent->bus_name, sender) != 0)
+		return not_authorized(msg);
+
+	agent_free(agent);
+	agent = NULL;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable manager_methods[] = {
+	{ "RegisterAgent",	"o",	"",	register_agent		},
+	{ "UnregisterAgent",	"o",	"",	unregister_agent	},
+	{ }
+};
+
+static GDBusSignalTable manager_signals[] = {
+	{ "TransferStarted", 	"o" 	},
+	{ "TransferCompleted",	"ob"	},
+	{ }
+};
+
+static GDBusMethodTable transfer_methods[] = {
+	{ "Cancel",	""	},
+	{ }
+};
+
+static GDBusSignalTable transfer_signals[] = {
+	{ "Progress",	"ii"	},
+	{ }
+};
+
+static DBusConnection *connection = NULL;
+
+gboolean manager_init(DBusConnection *conn)
+{
+	DBG("conn %p", conn);
+
+	connection = dbus_connection_ref(conn);
+	if (connection == NULL)
+		return FALSE;
+
+	return g_dbus_register_interface(connection, OPENOBEX_MANAGER_PATH,
+					OPENOBEX_MANAGER_INTERFACE,
+					manager_methods, manager_signals, NULL,
+					NULL, NULL);
+}
+
+void manager_cleanup(void)
+{
+	DBG("conn %p", connection);
+
+	g_dbus_unregister_interface(connection, OPENOBEX_MANAGER_PATH,
+						OPENOBEX_MANAGER_INTERFACE);
+
+	/* FIXME: Release agent? */
+
+	if (agent)
+		agent_free(agent);
+
+	dbus_connection_unref(connection);
+}
+
+void emit_transfer_started(guint32 id)
+{
+	gchar *path = g_strdup_printf("/transfer%u", id);
+
+	g_dbus_emit_signal(connection, OPENOBEX_MANAGER_PATH,
+			OPENOBEX_MANAGER_INTERFACE, "TransferStarted",
+			DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID);
+
+	g_free(path);
+}
+
+void emit_transfer_completed(guint32 id, gboolean success)
+{
+	gchar *path = g_strdup_printf("/transfer%u", id);
+
+	g_dbus_emit_signal(connection, OPENOBEX_MANAGER_PATH,
+			OPENOBEX_MANAGER_INTERFACE, "TransferCompleted",
+			DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_BOOLEAN, &success,
+			DBUS_TYPE_INVALID);
+
+	g_free(path);
+}
+
+void emit_transfer_progress(guint32 id, guint32 total, guint32 transfered)
+{
+	gchar *path = g_strdup_printf("/transfer%u", id);
+
+	g_dbus_emit_signal(connection, path,
+			TRANSFER_INTERFACE, "Progress",
+			DBUS_TYPE_INT32, &total,
+			DBUS_TYPE_INT32, &transfered,
+			DBUS_TYPE_INVALID);
+
+	g_free(path);
+}
+
+void register_transfer(guint32 id)
+{
+	gchar *path = g_strdup_printf("/transfer%u", id);
+
+	if (!g_dbus_register_interface(connection, path,
+				TRANSFER_INTERFACE,
+				transfer_methods, transfer_signals,
+				NULL, NULL, NULL)) {
+		error("Cannot register Transfer interface.");
+		g_free(path);
+		return;
+	}
+
+	g_free(path);
+}
+
+void unregister_transfer(guint32 id)
+{
+	gchar *path = g_strdup_printf("/transfer%u", id);
+
+	g_dbus_unregister_interface(connection, path,
+				TRANSFER_INTERFACE);
+
+	g_free(path);
+}
+
+static void agent_reply(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	const gchar *name;
+	DBusError derr;
+
+	agent->auth_pending = FALSE;
+
+	dbus_error_init(&derr);
+	if (dbus_set_error_from_message(&derr, reply)) {
+		error("Agent replied with an error: %s, %s",
+				derr.name, derr.message);
+		dbus_error_free(&derr);
+		return;
+	}
+
+	if (dbus_message_get_args(reply, NULL,
+				DBUS_TYPE_STRING, &name,
+				DBUS_TYPE_INVALID)) {
+		/* Splits folder and name */
+		const gchar *slash = strrchr(name, '/');
+		if (!slash) {
+			agent->new_name = g_strdup(name);
+			agent->new_folder = NULL;
+		} else {
+			agent->new_name = g_strdup(slash + 1);
+			agent->new_folder = g_strndup(name, slash - name);
+		}
+	}
+}
+
+int request_authorization(gint32 cid, int fd, const gchar *filename,
+			const gchar *type, gint32 length, gint32 time,
+			gchar **new_folder, gchar **new_name)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	struct sockaddr_rc addr;
+	socklen_t addrlen;
+	gchar address[18];
+	const gchar *bda = address;
+	gchar *path;
+
+	if (!agent)
+		return -1;
+
+	if (agent->auth_pending)
+		return -EPERM;
+
+	if (!new_folder || !new_name)
+		return -EINVAL;
+
+	memset(&addr, 0, sizeof(addr));
+	addrlen = sizeof(addr);
+
+	if (getpeername(fd, (struct sockaddr *) &addr, &addrlen) < 0)
+		return -1;
+
+	ba2str(&addr.rc_bdaddr, address);
+
+	path = g_strdup_printf("/transfer%d", cid);
+
+	msg = dbus_message_new_method_call(agent->bus_name, agent->path,
+					"org.openobex.Agent", "Authorize");
+
+	dbus_message_append_args(msg,
+			DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_STRING, &bda,
+			DBUS_TYPE_STRING, &filename,
+			DBUS_TYPE_STRING, &type,
+			DBUS_TYPE_INT32, &length,
+			DBUS_TYPE_INT32, &time,
+			DBUS_TYPE_INVALID);
+
+	g_free(path);
+
+	if (!dbus_connection_send_with_reply(connection,
+					msg, &call, TIMEOUT))
+		return -EPERM;
+
+	dbus_message_unref(msg);
+
+	agent->auth_pending = TRUE;
+
+	dbus_pending_call_set_notify(call, agent_reply, NULL, NULL);
+	dbus_pending_call_unref(call);
+
+	/* Workaround: process events while agent doesn't reply */
+	while (agent->auth_pending)
+		g_main_context_iteration(NULL, TRUE);
+
+	if (!agent->new_name) {
+		return -EPERM;
+	}
+
+	*new_folder = agent->new_folder;
+	*new_name = agent->new_name;
+	agent->new_folder = NULL;
+	agent->new_name = NULL;
+
+	return 0;
+}
diff --git a/obexd/src/obex.c b/obexd/src/obex.c
new file mode 100644
index 0000000..4089b9f
--- /dev/null
+++ b/obexd/src/obex.c
@@ -0,0 +1,765 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Nokia Corporation
+ *  Copyright (C) 2007-2008  Instituto Nokia de Tecnologia (INdT)
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "logging.h"
+#include "obex.h"
+#include "dbus.h"
+
+/* Default MTU's */
+#define RX_MTU 32767
+#define TX_MTU 32767
+
+#define TARGET_SIZE	16
+static const guint8 FTP_TARGET[TARGET_SIZE] = { 0xF9, 0xEC, 0x7B, 0xC4,
+					0x95, 0x3C, 0x11, 0xD2,
+					0x98, 0x4E, 0x52, 0x54,
+					0x00, 0xDC, 0x9E, 0x09 };
+
+/* Connection ID */
+static guint32 cid = 0x0000;
+
+typedef struct {
+    guint8	version;
+    guint8	flags;
+    guint16	mtu;
+} __attribute__ ((packed)) obex_connect_hdr_t;
+
+struct obex_commands opp = {
+	.get		= opp_get,
+	.chkput		= opp_chkput,
+	.put		= opp_put,
+	.setpath	= NULL,
+};
+
+struct obex_commands ftp = {
+	.get		= ftp_get,
+	.put		= ftp_put,
+	.setpath	= ftp_setpath,
+};
+
+static void obex_session_free(struct obex_session *os)
+{
+	if (os->name)
+		g_free(os->name);
+	if (os->type)
+		g_free(os->type);
+	if (os->current_folder)
+		g_free(os->current_folder);
+	if (os->buf)
+		g_free(os->buf);
+	if (os->fd > 0)
+		close(os->fd);
+	g_free(os);
+}
+
+static void cmd_connect(struct obex_session *os,
+			obex_t *obex, obex_object_t *obj)
+{
+	obex_connect_hdr_t *nonhdr;
+	obex_headerdata_t hd;
+	uint8_t *buffer;
+	guint hlen, newsize;
+	guint16 mtu;
+	guint8 hi;
+
+	if (OBEX_ObjectGetNonHdrData(obj, &buffer) != sizeof(*nonhdr)) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+		debug("Invalid OBEX CONNECT packet");
+		return;
+	}
+
+	nonhdr = (obex_connect_hdr_t *) buffer;
+	mtu = g_ntohs(nonhdr->mtu);
+	debug("Version: 0x%02x. Flags: 0x%02x  OBEX packet length: %d",
+			nonhdr->version, nonhdr->flags, mtu);
+	/* Leave space for headers */
+	newsize = mtu - 200;
+
+	os->tx_mtu = newsize;
+
+	debug("Resizing stream chunks to %d", newsize);
+
+	/* connection id will be used to track the sessions, even for OPP */
+	os->cid = ++cid;
+
+	register_transfer(os->cid);
+
+	if (os->target == NULL) {
+		/* OPP doesn't contains target or connection id. */
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+		return;
+	}
+
+	hi = hlen = 0;
+	OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen);
+
+	if (hi != OBEX_HDR_TARGET || hlen != TARGET_SIZE
+			|| memcmp(os->target, hd.bs, TARGET_SIZE) != 0) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+		return;
+	}
+
+	/* Append received UUID in WHO header */
+	OBEX_ObjectAddHeader(obex, obj,
+			OBEX_HDR_WHO, hd, TARGET_SIZE,
+			OBEX_FL_FIT_ONE_PACKET);
+	hd.bq4 = cid;
+	OBEX_ObjectAddHeader(obex, obj,
+			OBEX_HDR_CONNECTION, hd, 4,
+			OBEX_FL_FIT_ONE_PACKET);
+
+	OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+
+}
+
+static gboolean chk_cid(obex_t *obex, obex_object_t *obj, guint32 cid)
+{
+	struct obex_session *os;
+	obex_headerdata_t hd;
+	guint hlen;
+	guint8 hi;
+	gboolean ret = FALSE;
+
+	os = OBEX_GetUserData(obex);
+
+	/* OPUSH doesn't provide a connection id. */
+	if (os->target == NULL)
+		return TRUE;
+
+	while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+		if (hi == OBEX_HDR_CONNECTION && hlen == 4) {
+			ret = (hd.bq4 == cid ? TRUE : FALSE);
+			break;
+		}
+	}
+
+	OBEX_ObjectReParseHeaders(obex, obj);
+
+	if (ret == FALSE)
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_SERVICE_UNAVAILABLE,
+				OBEX_RSP_SERVICE_UNAVAILABLE);
+
+	return ret;
+}
+
+static void cmd_get(struct obex_session *os, obex_t *obex, obex_object_t *obj)
+{
+	obex_headerdata_t hd;
+	guint hlen;
+	guint8 hi;
+
+	if (!os->cmds->get) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+				OBEX_RSP_NOT_IMPLEMENTED);
+		return;
+	}
+
+	g_return_if_fail(chk_cid(obex, obj, os->cid));
+
+	if (os->type) {
+		g_free(os->type);
+		os->type = NULL;
+	}
+
+	if (os->name) {
+		g_free(os->name);
+		os->name = NULL;
+	}
+
+	if (os->buf) {
+		g_free(os->buf);
+		os->buf = NULL;
+	}
+
+	while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+		switch (hi) {
+		case OBEX_HDR_NAME:
+			if (os->name) {
+				debug("Ignoring multiple name headers");
+				break;
+			}
+
+			if (hlen == 0)
+				continue;
+
+			os->name = g_convert((const gchar *) hd.bs, hlen,
+					"UTF8", "UTF16BE", NULL, NULL, NULL);
+			debug("OBEX_HDR_NAME: %s", os->name);
+			break;
+		case OBEX_HDR_TYPE:
+			if (os->type) {
+				debug("Ignoring multiple type headers");
+				break;
+			}
+
+			if (hlen == 0)
+				continue;
+
+			/* Ensure null termination */
+			if (hd.bs[hlen - 1] != '\0')
+				break;
+
+			if (!g_utf8_validate((const gchar *) hd.bs, -1, NULL)) {
+				debug("Invalid type header: %s", hd.bs);
+				break;
+			}
+
+			/* FIXME: x-obex/folder-listing - type is mandatory */
+
+			os->type = g_strndup((const gchar *) hd.bs, hlen);
+			debug("OBEX_HDR_TYPE: %s", os->type);
+			break;
+		}
+	}
+
+	os->cmds->get(obex, obj);
+}
+
+static void cmd_setpath(struct obex_session *os,
+			obex_t *obex, obex_object_t *obj)
+{
+	obex_headerdata_t hd;
+	guint32 hlen;
+	guint8 hi;
+
+	if (!os->cmds->setpath) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+				OBEX_RSP_NOT_IMPLEMENTED);
+		return;
+	}
+
+	g_return_if_fail(chk_cid(obex, obj, os->cid));
+
+	if (os->name) {
+		g_free(os->name);
+		os->name = NULL;
+	}
+
+	while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+		if (hi == OBEX_HDR_NAME) {
+			if (os->name) {
+				debug("Ignoring multiple name headers");
+				break;
+			}
+
+			/*
+			 * This is because OBEX_UnicodeToChar() accesses
+			 * the string even if its size is zero
+			 */
+			if (hlen == 0) {
+				os->name = g_strdup("");
+				break;
+			}
+
+			os->name = g_convert((const gchar *) hd.bs, hlen,
+					"UTF8", "UTF16BE", NULL, NULL, NULL);
+
+			debug("Set path name: %s", os->name);
+			break;
+		}
+	}
+
+	if (!os->name) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_BAD_REQUEST,
+				OBEX_RSP_BAD_REQUEST);
+		return;
+	}
+
+	os->cmds->setpath(obex, obj);
+}
+
+gboolean os_prepare_get(struct obex_session *os, gchar *file, guint32 *size)
+{
+	gint fd;
+	struct stat stats;
+
+	fd = open(file, O_RDONLY);
+	if (fd < 0)
+		goto fail;
+
+	if (fstat(fd, &stats))
+		goto fail;
+
+	os->fd = fd;
+	os->offset = 0;
+
+	if (stats.st_size > 0)
+		os->buf = g_new0(guint8, os->tx_mtu);
+
+	*size = stats.st_size;
+
+	return TRUE;
+
+fail:
+	if (fd >= 0)
+		close(fd);
+
+	return FALSE;
+}
+
+static gint obex_write(struct obex_session *os,
+			obex_t *obex, obex_object_t *obj)
+{
+	obex_headerdata_t hd;
+	gint32 len;
+
+	debug("obex_write name: %s type: %s tx_mtu: %d fd: %d",
+			os->name, os->type, os->tx_mtu, os->fd);
+
+	if (os->fd < 0)
+		return -EIO;
+
+	len = read(os->fd, os->buf, os->tx_mtu);
+	if (len < 0) {
+		gint err = errno;
+		error("read(): %s (%d)", strerror(err), err);
+		g_free(os->buf);
+		return -err;
+	}
+
+	os->offset += len;
+
+	if (len == 0) {
+		OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY, hd, 0,
+					OBEX_FL_STREAM_DATAEND);
+		g_free(os->buf);
+		os->buf = NULL;
+		return len;
+	}
+
+	hd.bs = os->buf;
+	OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY, hd, len,
+				OBEX_FL_STREAM_DATA);
+
+	return len;
+}
+
+static gint obex_read(struct obex_session *os,
+			obex_t *obex, obex_object_t *obj)
+{
+	gint size;
+	gint32 len = 0;
+	const guint8 *buffer;
+
+	size = OBEX_ObjectReadStream(obex, obj, &buffer);
+	if (size < 0) {
+		error("Error on OBEX stream");
+		return -EIO;
+	}
+
+	if (size > os->rx_mtu) {
+		error("Received more data than RX_MAX");
+		return -EIO;
+	}
+
+	if (os->fd < 0 && size > 0) {
+		if (os->buf) {
+			error("Got more data but there is still a pending buffer");
+			return -EIO;
+		}
+
+		os->buf = g_malloc0(os->rx_mtu);
+		memcpy(os->buf, buffer, size);
+		os->offset = size;
+
+		debug("Stored %u bytes into temporary buffer", size);
+
+		return 0;
+	}
+
+	while (len < size) {
+		gint w;
+
+		w = write(os->fd, buffer + len, size - len);
+		if (w < 0) {
+			gint err = errno;
+			if (err == EINTR)
+				continue;
+			else
+				return -err;
+		}
+
+		len += w;
+	}
+
+	os->offset += len;
+
+	return 0;
+}
+
+static gboolean check_put(obex_t *obex, obex_object_t *obj)
+{
+	struct obex_session *os;
+	struct statvfs buf;
+	obex_headerdata_t hd;
+	guint hlen;
+	guint8 hi;
+	guint64 free;
+
+	os = OBEX_GetUserData(obex);
+
+	if (os->type) {
+		g_free(os->type);
+		os->type = NULL;
+	}
+
+	if (os->name) {
+		g_free(os->name);
+		os->name = NULL;
+	}
+
+	while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+		switch (hi) {
+		case OBEX_HDR_NAME:
+			if (os->name) {
+				debug("Ignoring multiple name headers");
+				break;
+			}
+
+			if (hlen == 0)
+				continue;
+
+			os->name = g_convert((const gchar *) hd.bs, hlen,
+					"UTF8", "UTF16BE", NULL, NULL, NULL);
+			debug("OBEX_HDR_NAME: %s", os->name);
+			break;
+
+		case OBEX_HDR_TYPE:
+			if (os->type) {
+				debug("Ignoring multiple type headers");
+				break;
+			}
+
+			if (hlen == 0)
+				continue;
+
+			/* Ensure null termination */
+			if (hd.bs[hlen - 1] != '\0')
+				break;
+
+			if (!g_utf8_validate((const gchar *) hd.bs, -1, NULL)) {
+				debug("Invalid type header: %s", hd.bs);
+				break;
+			}
+
+			os->type = g_strndup((const gchar *) hd.bs, hlen);
+			debug("OBEX_HDR_TYPE: %s", os->type);
+			break;
+
+		case OBEX_HDR_BODY:
+			os->size = -1;
+			break;
+
+		case OBEX_HDR_LENGTH:
+			os->size = hd.bq4;
+			debug("OBEX_HDR_LENGTH: %d", os->size);
+			break;
+		}
+	}
+
+	OBEX_ObjectReParseHeaders(obex, obj);
+
+	if (!os->name) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_BAD_REQUEST,
+				OBEX_RSP_BAD_REQUEST);
+		g_free(os->type);
+		os->type = NULL;
+		return FALSE;
+	}
+
+	if (!os->cmds->chkput)
+		goto done;
+
+	if (os->cmds->chkput(obex, obj) < 0) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+		return FALSE;
+	}
+
+	if (fstatvfs(os->fd, &buf) < 0) {
+		int err = errno;
+		error("fstatvfs(): %s(%d)", strerror(err), err);
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+		return FALSE;
+	}
+
+	free = buf.f_bsize * buf.f_bavail;
+	debug("Free space in disk: %lu", free);
+	if (os->size > free) {
+		debug("Free disk space not available");
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+		return FALSE;
+	}
+
+done:
+	os->checked = TRUE;
+
+	return TRUE;
+}
+
+static void cmd_put(struct obex_session *os, obex_t *obex, obex_object_t *obj)
+{
+	if (!os->cmds->put) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+				OBEX_RSP_NOT_IMPLEMENTED);
+		return;
+	}
+
+	g_return_if_fail(chk_cid(obex, obj, os->cid));
+
+	if (!os->checked) {
+		if (!check_put(obex, obj))
+			return;
+	}
+
+	os->cmds->put(obex, obj);
+}
+
+static void obex_event(obex_t *obex, obex_object_t *obj, gint mode,
+					gint evt, gint cmd, gint rsp)
+{
+	struct obex_session *os;
+
+	obex_debug(evt, cmd, rsp);
+
+	os = OBEX_GetUserData(obex);
+
+	switch (evt) {
+	case OBEX_EV_PROGRESS:
+		emit_transfer_progress(os->cid, os->size, os->offset);
+		break;
+	case OBEX_EV_ABORT:
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+		break;
+	case OBEX_EV_REQDONE:
+		switch (cmd) {
+		case OBEX_CMD_DISCONNECT:
+			OBEX_TransportDisconnect(obex);
+			break;
+		case OBEX_CMD_PUT:
+		case OBEX_CMD_GET:
+			emit_transfer_completed(os->cid,
+						os->offset == os->size);
+			if (os->fd >= 0) {
+				close(os->fd);
+				os->fd = -1;
+			}
+			g_free(os->buf);
+			os->buf = NULL;
+			os->offset = 0;
+			break;
+		default:
+			break;
+		}
+		break;
+	case OBEX_EV_REQHINT:
+		switch (cmd) {
+		case OBEX_CMD_PUT:
+			os->checked = FALSE;
+			OBEX_ObjectReadStream(obex, obj, NULL);
+		case OBEX_CMD_GET:
+		case OBEX_CMD_SETPATH:
+		case OBEX_CMD_CONNECT:
+		case OBEX_CMD_DISCONNECT:
+			OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE,
+					OBEX_RSP_SUCCESS);
+			break;
+		default:
+			OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
+					OBEX_RSP_NOT_IMPLEMENTED);
+			break;
+		}
+		break;
+	case OBEX_EV_REQCHECK:
+		switch (cmd) {
+		case OBEX_CMD_PUT:
+			if (os->cmds->put)
+				check_put(obex, obj);
+			break;
+		default:
+			break;
+		}
+		break;
+	case OBEX_EV_REQ:
+		switch (cmd) {
+		case OBEX_CMD_DISCONNECT:
+			break;
+		case OBEX_CMD_CONNECT:
+			cmd_connect(os, obex, obj);
+			break;
+		case OBEX_CMD_SETPATH:
+			cmd_setpath(os, obex, obj);
+			break;
+		case OBEX_CMD_GET:
+			cmd_get(os, obex, obj);
+			break;
+		case OBEX_CMD_PUT:
+			cmd_put(os, obex, obj);
+			break;
+		default:
+			debug("Unknown request: 0x%X", cmd);
+			OBEX_ObjectSetRsp(obj,
+				OBEX_RSP_NOT_IMPLEMENTED, OBEX_RSP_NOT_IMPLEMENTED);
+			break;
+		}
+		break;
+	case OBEX_EV_STREAMAVAIL:
+		if (obex_read(os, obex, obj) < 0) {
+			debug("error obex_read()");
+			OBEX_CancelRequest(obex, 1);
+		}
+
+		break;
+	case OBEX_EV_STREAMEMPTY:
+		obex_write(os, obex, obj);
+		break;
+	case OBEX_EV_LINKERR:
+		break;
+	case OBEX_EV_PARSEERR:
+		break;
+	case OBEX_EV_UNEXPECTED:
+		break;
+
+	default:
+		debug("Unknown evt %d", evt);
+		break;
+	}
+}
+
+static void obex_handle_destroy(gpointer user_data)
+{
+	struct obex_session *os;
+	obex_t *obex = user_data;
+
+	os = OBEX_GetUserData(obex);
+
+	/* Got an error during a transfer. */
+	if (os->fd >= 0)
+		emit_transfer_completed(os->cid, os->offset == os->size);
+
+	unregister_transfer(os->cid);
+
+	obex_session_free(os);
+
+	OBEX_Cleanup(obex);
+}
+
+static gboolean obex_handle_input(GIOChannel *io,
+				GIOCondition cond, gpointer user_data)
+{
+	obex_t *obex = user_data;
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		return FALSE;
+
+	if (OBEX_HandleInput(obex, 1) < 0) {
+		error("Handle input error");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gint obex_session_start(gint fd, struct server *server)
+{
+	struct obex_session *os;
+	GIOChannel *io;
+	obex_t *obex;
+	gint ret;
+
+	os = g_new0(struct obex_session, 1);
+	switch (server->service) {
+	case OBEX_OPUSH:
+		os->target = NULL;
+		os->cmds = &opp;
+		break;
+	case OBEX_FTP:
+		os->target = FTP_TARGET;
+		os->cmds = &ftp;
+		break;
+	default:
+		g_free(os);
+		debug("Invalid OBEX server");
+		return -EINVAL;
+	}
+
+	os->current_folder = g_strdup(server->folder);
+	os->server = server;
+	os->rx_mtu = RX_MTU;
+	os->tx_mtu = TX_MTU;
+	os->fd = -1;
+
+	obex = OBEX_Init(OBEX_TRANS_FD, obex_event, 0);
+	if (!obex) {
+		obex_session_free(os);
+		return -EIO;
+	}
+
+	OBEX_SetUserData(obex, os);
+
+	OBEX_SetTransportMTU(obex, os->rx_mtu, os->tx_mtu);
+
+	ret = FdOBEX_TransportSetup(obex, fd, fd, 0);
+	if (ret < 0) {
+		obex_session_free(os);
+		OBEX_Cleanup(obex);
+		return ret;
+	}
+
+	io = g_io_channel_unix_new(fd);
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+			G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+			obex_handle_input, obex, obex_handle_destroy);
+	g_io_channel_unref(io);
+
+	return 0;
+}
+
+gint obex_session_stop()
+{
+	return 0;
+}
diff --git a/obexd/src/obex.h b/obexd/src/obex.h
new file mode 100644
index 0000000..bd8868f
--- /dev/null
+++ b/obexd/src/obex.h
@@ -0,0 +1,76 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Nokia Corporation
+ *  Copyright (C) 2007-2008  Instituto Nokia de Tecnologia (INdT)
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#define OBEX_OPUSH	0x00
+#define OBEX_FTP	0x01
+
+struct obex_commands {
+	void (*get) (obex_t *obex, obex_object_t *obj);
+	gint (*chkput) (obex_t *obex, obex_object_t *obj);
+	void (*put) (obex_t *obex, obex_object_t *obj);
+	void (*setpath) (obex_t *obex, obex_object_t *obj);
+};
+
+struct server {
+	guint16		service;
+	gboolean	auto_accept;
+	gchar		*folder;
+};
+
+struct obex_session {
+	guint32		cid;
+	guint16		tx_mtu;
+	guint16		rx_mtu;
+	gchar		*name;
+	gchar		*type;
+	gchar		*current_folder;
+	guint8		*buf;
+	gint32		offset;
+	gint32		size;
+	gint		fd;
+	const guint8	*target;
+	struct obex_commands *cmds;
+	struct server *server;
+	gboolean	checked;
+};
+
+gint obex_session_start(gint fd, struct server *server);
+gint obex_session_stop();
+
+gint opp_chkput(obex_t *obex, obex_object_t *obj);
+void opp_put(obex_t *obex, obex_object_t *obj);
+void opp_get(obex_t *obex, obex_object_t *obj);
+
+void ftp_get(obex_t *obex, obex_object_t *obj);
+void ftp_put(obex_t *obex, obex_object_t *obj);
+void ftp_setpath(obex_t *obex, obex_object_t *obj);
+
+gboolean os_prepare_get(struct obex_session *os, gchar *file, guint32 *size);
diff --git a/obexd/src/obexd.h b/obexd/src/obexd.h
new file mode 100644
index 0000000..7baac91
--- /dev/null
+++ b/obexd/src/obexd.h
@@ -0,0 +1,38 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+
+#define DBG(fmt, arg...)  printf("%s: " fmt "\n" , __FUNCTION__ , ## arg)
+//#define DBG(fmt, arg...)
+
+#include <dbus/dbus.h>
+
+#define OPENOBEX_SERVICE  "org.openobex"
+
+#define OPENOBEX_MANAGER_PATH       "/"
+#define OPENOBEX_MANAGER_INTERFACE  OPENOBEX_SERVICE ".Manager"
+#define ERROR_INTERFACE		OPENOBEX_SERVICE ".Error"
+
+gboolean manager_init(DBusConnection *conn);
+void manager_cleanup(void);
diff --git a/obexd/src/opp.c b/obexd/src/opp.c
new file mode 100644
index 0000000..70e6fe6
--- /dev/null
+++ b/obexd/src/opp.c
@@ -0,0 +1,189 @@
+/*
+ *
+ *  OBEX Server
+ *
+ *  Copyright (C) 2007-2008  Nokia Corporation
+ *  Copyright (C) 2007-2008  Instituto Nokia de Tecnologia (INdT)
+ *  Copyright (C) 2007-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include <glib.h>
+
+#include "logging.h"
+#include "dbus.h"
+#include "obex.h"
+
+#define VCARD_TYPE "text/x-vcard"
+#define VCARD_FILE CONFIGDIR "/vcard.vcf"
+
+gint opp_chkput(obex_t *obex, obex_object_t *obj)
+{
+	struct obex_session *os;
+	gchar *new_folder, *new_name, *path;
+	gint32 time, len = 0;
+	gint ret;
+
+	os = OBEX_GetUserData(obex);
+	if (os == NULL)
+		return -EINVAL;
+
+	if (!os->size)
+		return -EINVAL;
+
+	if (os->server->auto_accept)
+		goto skip_auth;
+
+	time = 0;
+	ret = request_authorization(os->cid, OBEX_GetFD(obex),
+					os->name ? os->name : "",
+					os->type ? os->type : "",
+					os->size, time, &new_folder,
+				       	&new_name);
+
+	if (ret < 0)
+		return -EPERM;
+
+	if (new_folder) {
+		g_free(os->current_folder);
+		os->current_folder = new_folder;
+	}
+
+	if (new_name) {
+		g_free(os->name);
+		os->name = new_name;
+	}
+
+skip_auth:
+	path = g_build_filename(os->current_folder, os->name, NULL);
+
+	os->fd = open(path, O_WRONLY | O_CREAT, 0600);
+	if (os->fd < 0) {
+		error("open(%s): %s (%d)", path, strerror(errno), errno);
+		g_free(path);
+		return -EPERM;
+	}
+
+	g_free(path);
+
+	emit_transfer_started(os->cid);
+
+	if (!os->buf) {
+		debug("PUT request authorized, no buffered data");
+		return 0;
+	}
+
+	while (len < os->offset) {
+		gint w;
+
+		w = write(os->fd, os->buf + len, os->offset - len);
+		if (w < 0) {
+			gint err = errno;
+			if (err == EINTR)
+				continue;
+			else
+				return -err;
+		}
+
+		len += w;
+	}
+
+	return 0;
+}
+
+void opp_put(obex_t *obex, obex_object_t *obj)
+{
+	struct obex_session *os;
+
+	os = OBEX_GetUserData(obex);
+	if (os == NULL)
+		return;
+
+	if (os->current_folder == NULL) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+		return;
+	}
+
+	if (os->name == NULL) {
+		OBEX_ObjectSetRsp(obj, OBEX_RSP_BAD_REQUEST, OBEX_RSP_BAD_REQUEST);
+		return;
+	}
+
+	OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+}
+
+void opp_get(obex_t *obex, obex_object_t *obj)
+{
+	struct obex_session *os;
+	obex_headerdata_t hv;
+	guint32 size;
+
+	os = OBEX_GetUserData(obex);
+	if (os == NULL)
+		return;
+
+	if (os->name)
+		goto fail;
+
+	if (os->type == NULL)
+		goto fail;
+
+	if (g_str_equal(os->type, VCARD_TYPE)) {
+		if (!os_prepare_get(os, VCARD_FILE, &size))
+			goto fail;
+	} else
+		goto fail;
+
+
+	hv.bq4 = size;
+	OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_LENGTH, hv, 4, 0);
+
+	/* Add body header */
+	hv.bs = NULL;
+	if (size == 0)
+		OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
+				hv, 0, OBEX_FL_FIT_ONE_PACKET);
+	else
+		OBEX_ObjectAddHeader (obex, obj, OBEX_HDR_BODY,
+				hv, 0, OBEX_FL_STREAM_START);
+
+	OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+
+	return;
+
+fail:
+	OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+
+	return;
+}