Diff between 296e697160904a8840747008a5c02a996bd51c1a and 9ce74b2d68a43e17a56bf63885e9e5fa266d7466

Changed Files

File Additions Deletions Status
obexd/client/main.c +98 -11 modified
obexd/client/pbap.c +1 -0 modified
obexd/client/session.c +420 -806 modified
obexd/client/session.h +18 -25 modified
obexd/client/sync.c +1 -0 modified
obexd/client/transfer.c +505 -0 added
obexd/client/transfer.h +67 -0 added

Full Patch

diff --git a/obexd/client/main.c b/obexd/client/main.c
index 9a7e8e6..aff7a99 100644
--- a/obexd/client/main.c
+++ b/obexd/client/main.c
@@ -35,6 +35,7 @@
 #include <gdbus.h>
 
 #include "logging.h"
+#include "transfer.h"
 #include "session.h"
 
 #define CLIENT_SERVICE  "org.openobex.client"
@@ -50,6 +51,22 @@ struct send_data {
 	GPtrArray *files;
 };
 
+static GSList *sessions = NULL;
+
+static void shutdown_session(struct session_data *session)
+{
+	sessions = g_slist_remove(sessions, session);
+	session_shutdown(session);
+	session_unref(session);
+}
+
+static void owner_exit(DBusConnection *connection, void *user_data)
+{
+	struct session_data *session = user_data;
+
+	shutdown_session(session);
+}
+
 static void create_callback(struct session_data *session, void *user_data)
 {
 	struct send_data *data = user_data;
@@ -59,13 +76,13 @@ static void create_callback(struct session_data *session, void *user_data)
 		DBusMessage *error = g_dbus_create_error(data->message,
 					"org.openobex.Error.Failed", NULL);
 		g_dbus_send_message(data->connection, error);
+		shutdown_session(session);
 		goto done;
 	}
 
-	session->owner = g_strdup(data->sender);
-
 	if (session->target != NULL) {
 		session_register(session);
+		session_set_owner(session, data->sender, owner_exit);
 		g_dbus_send_reply(data->connection, data->message,
 				DBUS_TYPE_OBJECT_PATH, &session->path,
 				DBUS_TYPE_INVALID);
@@ -88,6 +105,10 @@ static void create_callback(struct session_data *session, void *user_data)
 		g_free(basename);
 	}
 
+	/* No need to keep a reference for SendFiles */
+	sessions = g_slist_remove(sessions, session);
+	session_unref(session);
+
 done:
 	if (data->files)
 		g_ptr_array_free(data->files, TRUE);
@@ -137,6 +158,7 @@ static DBusMessage *send_files(DBusConnection *connection,
 					DBusMessage *message, void *user_data)
 {
 	DBusMessageIter iter, array;
+	struct session_data *session;
 	GPtrArray *files;
 	struct send_data *data;
 	const char *agent, *source = NULL, *dest = NULL, *target = NULL;
@@ -189,9 +211,12 @@ static DBusMessage *send_files(DBusConnection *connection,
 	data->agent = g_strdup(agent);
 	data->files = files;
 
-	if (session_create(source, dest, "OPP", channel, create_callback,
-				data) == 0)
+	session = session_create(source, dest, "OPP", channel, create_callback,
+				data);
+	if (session != NULL) {
+		sessions = g_slist_append(sessions, session);
 		return NULL;
+	}
 
 	g_ptr_array_free(data->files, TRUE);
 	dbus_message_unref(data->message);
@@ -210,6 +235,7 @@ static void pull_complete_callback(struct session_data *session,
 
 	g_dbus_send_reply(data->connection, data->message, DBUS_TYPE_INVALID);
 
+	shutdown_session(session);
 	dbus_message_unref(data->message);
 	dbus_connection_unref(data->connection);
 	g_free(data->sender);
@@ -225,9 +251,11 @@ static void pull_session_callback(struct session_data *session,
 		DBusMessage *error = g_dbus_create_error(data->message,
 					"org.openobex.Error.Failed", NULL);
 		g_dbus_send_message(data->connection, error);
+		shutdown_session(session);
 		goto done;
 	}
 
+	session_set_owner(session, data->sender, owner_exit);
 	g_dbus_send_reply(data->connection, data->message, DBUS_TYPE_INVALID);
 
 	session_pull(session, "text/x-vcard", "/tmp/x.vcf",
@@ -246,6 +274,7 @@ static DBusMessage *pull_business_card(DBusConnection *connection,
 					DBusMessage *message, void *user_data)
 {
 	DBusMessageIter iter, dict;
+	struct session_data *session;
 	struct send_data *data;
 	const char *source = NULL, *dest = NULL, *target = NULL;
 	uint8_t channel = 0;
@@ -267,9 +296,12 @@ static DBusMessage *pull_business_card(DBusConnection *connection,
 	data->message = dbus_message_ref(message);
 	data->sender = g_strdup(dbus_message_get_sender(message));
 
-	if (session_create(source, dest, "OPP", channel,
-					pull_session_callback, data) == 0)
+	session = session_create(source, dest, "OPP", channel,
+					pull_session_callback, data);
+	if (session != NULL) {
+		sessions = g_slist_append(sessions, session);
 		return NULL;
+	}
 
 	dbus_message_unref(data->message);
 	dbus_connection_unref(data->connection);
@@ -285,10 +317,25 @@ static DBusMessage *exchange_business_cards(DBusConnection *connection,
 	return g_dbus_create_error(message, "org.openobex.Error.Failed", NULL);
 }
 
+static struct session_data *find_session(const char *path)
+{
+	GSList *l;
+
+	for (l = sessions; l; l = l->next) {
+		struct session_data *session = l->data;
+
+		if (g_str_equal(session->path, path) == TRUE)
+			return session;
+	}
+
+	return NULL;
+}
+
 static DBusMessage *create_session(DBusConnection *connection,
 					DBusMessage *message, void *user_data)
 {
 	DBusMessageIter iter, dict;
+	struct session_data *session;
 	struct send_data *data;
 	const char *source = NULL, *dest = NULL, *target = NULL;
 	uint8_t channel = 0;
@@ -310,9 +357,12 @@ static DBusMessage *create_session(DBusConnection *connection,
 	data->message = dbus_message_ref(message);
 	data->sender = g_strdup(dbus_message_get_sender(message));
 
-	if (session_create(source, dest, target, channel, create_callback,
-				data) == 0)
+	session = session_create(source, dest, target, channel,
+					create_callback, data);
+	if (session != NULL) {
+		sessions = g_slist_append(sessions, session);
 		return NULL;
+	}
 
 	dbus_message_unref(data->message);
 	dbus_connection_unref(data->connection);
@@ -322,6 +372,34 @@ static DBusMessage *create_session(DBusConnection *connection,
 	return g_dbus_create_error(message, "org.openobex.Error.Failed", NULL);
 }
 
+static DBusMessage *remove_session(DBusConnection *connection,
+				DBusMessage *message, void *user_data)
+{
+	struct session_data *session;
+	const gchar *sender, *path;
+
+	if (dbus_message_get_args(message, NULL,
+			DBUS_TYPE_STRING, &path,
+			DBUS_TYPE_INVALID) == FALSE)
+		return g_dbus_create_error(message,
+				"org.openobex.Error.InvalidArguments", NULL);
+
+	session = find_session(path);
+	if (session == NULL)
+		return g_dbus_create_error(message,
+				"org.openobex.Error.InvalidArguments", NULL);
+
+	sender = dbus_message_get_sender(message);
+	if (g_str_equal(sender, session->owner) == FALSE)
+		return g_dbus_create_error(message,
+				"org.openobex.Error.NotAuthorized",
+				"Not Authorized");
+
+	shutdown_session(session);
+
+	return dbus_message_new_method_return(message);
+}
+
 static void capabilities_complete_callback(struct session_data *session,
 							void *user_data)
 {
@@ -329,7 +407,7 @@ static void capabilities_complete_callback(struct session_data *session,
 	struct send_data *data = user_data;
 	char *capabilities;
 
-	if (session->obex == NULL) {
+	if (transfer->filled == 0) {
 		DBusMessage *error = g_dbus_create_error(data->message,
 					"org.openobex.Error.Failed", NULL);
 		g_dbus_send_message(data->connection, error);
@@ -346,6 +424,7 @@ static void capabilities_complete_callback(struct session_data *session,
 
 done:
 
+	shutdown_session(session);
 	dbus_message_unref(data->message);
 	dbus_connection_unref(data->connection);
 	g_free(data->sender);
@@ -361,9 +440,11 @@ static void capability_session_callback(struct session_data *session,
 		DBusMessage *error = g_dbus_create_error(data->message,
 					"org.openobex.Error.Failed", NULL);
 		g_dbus_send_message(data->connection, error);
+		shutdown_session(session);
 		goto done;
 	}
 
+	session_set_owner(session, data->sender, owner_exit);
 	session_pull(session, "x-obex/capability", NULL,
 				capabilities_complete_callback, data);
 
@@ -380,6 +461,7 @@ static DBusMessage *get_capabilities(DBusConnection *connection,
 					DBusMessage *message, void *user_data)
 {
 	DBusMessageIter iter, dict;
+	struct session_data *session;
 	struct send_data *data;
 	const char *source = NULL, *dest = NULL, *target = NULL;
 	uint8_t channel = 0;
@@ -404,9 +486,12 @@ static DBusMessage *get_capabilities(DBusConnection *connection,
 	if (!target)
 		target = "OPP";
 
-	if (session_create(source, dest, target, channel, capability_session_callback, 
-				data) == 0)
+	session = session_create(source, dest, target, channel,
+				capability_session_callback, data);
+	if (session != NULL) {
+		sessions = g_slist_append(sessions, session);
 		return NULL;
+	}
 
 	dbus_message_unref(data->message);
 	dbus_connection_unref(data->connection);
@@ -425,6 +510,8 @@ static GDBusMethodTable client_methods[] = {
 						G_DBUS_METHOD_FLAG_ASYNC },
 	{ "CreateSession", "a{sv}", "o", create_session,
 						G_DBUS_METHOD_FLAG_ASYNC },
+	{ "RemoveSession", "o", "", remove_session,
+						G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetCapabilities", "a{sv}", "s", get_capabilities,
 						G_DBUS_METHOD_FLAG_ASYNC },
 	{ }
diff --git a/obexd/client/pbap.c b/obexd/client/pbap.c
index 15e1d47..cb3def4 100644
--- a/obexd/client/pbap.c
+++ b/obexd/client/pbap.c
@@ -31,6 +31,7 @@
 #include <gdbus.h>
 
 #include "logging.h"
+#include "transfer.h"
 #include "session.h"
 #include "pbap.h"
 
diff --git a/obexd/client/session.c b/obexd/client/session.c
index 3f41bfa..40e930f 100644
--- a/obexd/client/session.c
+++ b/obexd/client/session.c
@@ -33,6 +33,7 @@
 
 #include <glib.h>
 #include <gdbus.h>
+#include <gw-obex.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/rfcomm.h>
@@ -42,24 +43,16 @@
 #include "logging.h"
 #include "pbap.h"
 #include "sync.h"
+#include "transfer.h"
 #include "session.h"
 
 #define AGENT_INTERFACE  "org.openobex.Agent"
 
-#define TRANSFER_INTERFACE  "org.openobex.Transfer"
-#define TRANSFER_BASEPATH   "/org/openobex"
-
 #define SESSION_INTERFACE  "org.openobex.Session"
 #define SESSION_BASEPATH   "/org/openobex"
 
 #define FTP_INTERFACE  "org.openobex.FileTransfer"
 
-#define DEFAULT_BUFFER_SIZE 4096
-
-typedef int (*transfer_callback_t) (struct transfer_data *session, void *data);
-
-static void finalize_transfer(struct transfer_data *transfer);
-
 static guint64 counter = 0;
 
 static unsigned char pcsuite_uuid[] = { 0x00, 0x00, 0x50, 0x05, 0x00, 0x00,
@@ -73,20 +66,27 @@ struct callback_data {
 	void *data;
 };
 
-struct transfer_request {
-	DBusPendingCall *call;
-	transfer_callback_t callback;
+struct session_callback {
+	session_callback_t func;
 	void *data;
 };
 
-struct transfer_params {
-	const guint8 *data;
-	gint size;
+struct agent_pending {
+	DBusPendingCall *call;
 	session_callback_t cb;
-	void *user_data;
+	struct transfer_data *transfer;
 };
 
-static struct session_data *session_ref(struct session_data *session)
+struct agent_data {
+	char *name;
+	char *path;
+	guint watch;
+	struct agent_pending *pending;
+};
+
+static void session_prepare_put(struct session_data *session, void *data);
+
+struct session_data *session_ref(struct session_data *session)
 {
 	g_atomic_int_inc(&session->refcount);
 
@@ -95,60 +95,100 @@ static struct session_data *session_ref(struct session_data *session)
 	return session;
 }
 
-static void session_free(struct session_data *session)
+static void free_pending(struct agent_pending *pending)
+{
+	if (pending->call)
+		dbus_pending_call_unref(pending->call);
+
+	g_free(pending);
+}
+
+static void agent_free(struct session_data *session)
+{
+	struct agent_data *agent = session->agent;
+
+	if (agent->watch)
+		g_dbus_remove_watch(session->conn, agent->watch);
+
+	if (agent->pending) {
+		dbus_pending_call_cancel(agent->pending->call);
+		free_pending(agent->pending);
+	}
+
+	session->agent = NULL;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void agent_release(struct session_data *session)
 {
-	if (session->agent_watch)
-		g_dbus_remove_watch(session->conn, session->agent_watch);
+	struct agent_data *agent = session->agent;
+	DBusMessage *message;
 
-	if (session->owner_watch)
-		g_dbus_remove_watch(session->conn, session->owner_watch);
+	message = dbus_message_new_method_call(agent->name,
+			agent->path, AGENT_INTERFACE, "Release");
 
-	if (session->agent_name != NULL) {
-		DBusMessage *message;
+	dbus_message_set_no_reply(message, TRUE);
 
-		message = dbus_message_new_method_call(session->agent_name,
-			session->agent_path, AGENT_INTERFACE, "Release");
+	g_dbus_send_message(session->conn, message);
 
-		dbus_message_set_no_reply(message, TRUE);
+	agent_free(session);
+}
 
-		g_dbus_send_message(session->conn, message);
+static void session_unregistered(struct session_data *session)
+{
+	switch (session->uuid.value.uuid16) {
+	case OBEX_FILETRANS_SVCLASS_ID:
+		g_dbus_unregister_interface(session->conn, session->path,
+						FTP_INTERFACE);
+		break;
+	case PBAP_PSE_SVCLASS_ID:
+		pbap_unregister_interface(session->conn, session->path,
+						session);
+		break;
+	case IRMC_SYNC_SVCLASS_ID:
+		sync_unregister_interface(session->conn, session->path,
+						session);
 	}
 
+	g_dbus_unregister_interface(session->conn, session->path,
+					SESSION_INTERFACE);
+
+	debug("Session(%p) unregistered %s", session, session->path);
+}
+
+static void session_free(struct session_data *session)
+{
+	debug("session_free(%p)", session);
+
+	if (session->agent)
+		agent_release(session);
+
+	if (session->watch)
+		g_dbus_remove_watch(session->conn, session->watch);
+
 	if (session->obex != NULL)
 		gw_obex_close(session->obex);
 
 	if (session->sock > 2)
 		close(session->sock);
 
-	if (session->conn) {
-		switch (session->uuid.value.uuid16) {
-		case OBEX_FILETRANS_SVCLASS_ID:
-			g_dbus_unregister_interface(session->conn,
-					session->path,	FTP_INTERFACE);
-			break;
-		case PBAP_PSE_SVCLASS_ID:
-			pbap_unregister_interface(session->conn,
-					session->path, session);
-			break;
-		case IRMC_SYNC_SVCLASS_ID:
-			sync_unregister_interface(session->conn,
-					session->path, session);
-		}
-
-		g_dbus_unregister_interface(session->conn,
-				session->path, SESSION_INTERFACE);
+	if (session->path)
+		session_unregistered(session);
 
+	if (session->conn) {
 		dbus_connection_unref(session->conn);
 	}
 
+	g_free(session->callback);
 	g_free(session->path);
-	g_free(session->agent_name);
-	g_free(session->agent_path);
 	g_free(session->owner);
 	g_free(session);
 }
 
-static void session_unref(struct session_data *session)
+void session_unref(struct session_data *session)
 {
 	gboolean ret;
 
@@ -395,7 +435,7 @@ static sdp_session_t *service_connect(const bdaddr_t *src, const bdaddr_t *dst,
 	return sdp;
 }
 
-int session_create(const char *source,
+struct session_data *session_create(const char *source,
 			const char *destination, const char *target,
 			uint8_t channel, session_callback_t function,
 			void *user_data)
@@ -405,11 +445,11 @@ int session_create(const char *source,
 	int err;
 
 	if (destination == NULL)
-		return -EINVAL;
+		return NULL;
 
 	session = g_try_malloc0(sizeof(*session));
 	if (session == NULL)
-		return -ENOMEM;
+		return NULL;
 
 	session->refcount = 1;
 	session->sock = -1;
@@ -418,7 +458,7 @@ int session_create(const char *source,
 	session->conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
 	if (session->conn == NULL) {
 		session_free(session);
-		return -ENOMEM;
+		return NULL;
 	}
 
 	if (source == NULL)
@@ -445,13 +485,13 @@ int session_create(const char *source,
 	} else if (!g_ascii_strncasecmp(target, "PCSUITE", 7)) {
 		sdp_uuid128_create(&session->uuid, pcsuite_uuid);
 	} else {
-		return -EINVAL;
+		return NULL;
 	}
 
 	callback = g_try_malloc0(sizeof(*callback));
 	if (callback == NULL) {
 		session_free(session);
-		return -ENOMEM;
+		return NULL;
 	}
 
 	callback->session = session_ref(session);
@@ -470,492 +510,41 @@ int session_create(const char *source,
 	if (err < 0) {
 		session_free(session);
 		g_free(callback);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static void agent_notify_progress(DBusConnection *conn, const char *agent_name,
-			const char *agent_path, const char *transfer_path,
-			uint64_t transferred)
-{
-	DBusMessage *message;
-
-	if (agent_name == NULL || agent_path == NULL || transfer_path == NULL)
-		return;
-
-	message = dbus_message_new_method_call(agent_name,
-			agent_path, AGENT_INTERFACE, "Progress");
-	if (message == NULL)
-		return;
-
-	dbus_message_set_no_reply(message, TRUE);
-
-	dbus_message_append_args(message,
-			DBUS_TYPE_OBJECT_PATH, &transfer_path,
-			DBUS_TYPE_UINT64, &transferred,
-			DBUS_TYPE_INVALID);
-
-	g_dbus_send_message(conn, message);
-}
-
-static void agent_notify_complete(DBusConnection *conn, const char *agent_name,
-			const char *agent_path, const char *transfer_path)
-{
-	DBusMessage *message;
-
-	if (agent_name == NULL || agent_path == NULL || transfer_path == NULL)
-		return;
-
-	message = dbus_message_new_method_call(agent_name,
-			agent_path, AGENT_INTERFACE, "Complete");
-	if (message == NULL)
-		return;
-
-	dbus_message_set_no_reply(message, TRUE);
-
-	dbus_message_append_args(message,
-			DBUS_TYPE_OBJECT_PATH, &transfer_path,
-			DBUS_TYPE_INVALID);
-
-	g_dbus_send_message(conn, message);
-
-}
-
-static void agent_notify_error(DBusConnection *conn, const char *agent_name,
-			const char *agent_path, const char *transfer_path,
-			const char *error_msg)
-{
-	DBusMessage *message;
-
-	if (agent_name == NULL || agent_path == NULL || transfer_path == NULL)
-		return;
-
-	message = dbus_message_new_method_call(agent_name,
-			agent_path, AGENT_INTERFACE, "Error");
-	if (message == NULL)
-		return;
-
-	dbus_message_set_no_reply(message, TRUE);
-
-	dbus_message_append_args(message,
-			DBUS_TYPE_OBJECT_PATH, &transfer_path,
-			DBUS_TYPE_STRING, &error_msg,
-			DBUS_TYPE_INVALID);
-
-	g_dbus_send_message(conn, message);
-}
-
-static void append_entry(DBusMessageIter *dict,
-				const char *key, int type, void *val)
-{
-	DBusMessageIter entry, value;
-	const char *signature;
-
-	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
-								NULL, &entry);
-
-	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
-
-	switch (type) {
-	case DBUS_TYPE_STRING:
-		signature = DBUS_TYPE_STRING_AS_STRING;
-		break;
-	case DBUS_TYPE_BYTE:
-		signature = DBUS_TYPE_BYTE_AS_STRING;
-		break;
-	case DBUS_TYPE_UINT64:
-		signature = DBUS_TYPE_UINT64_AS_STRING;
-		break;
-	default:
-		signature = DBUS_TYPE_VARIANT_AS_STRING;
-		break;
-	}
-
-	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
-							signature, &value);
-	dbus_message_iter_append_basic(&value, type, val);
-	dbus_message_iter_close_container(&entry, &value);
-
-	dbus_message_iter_close_container(dict, &entry);
-}
-
-static DBusMessage *transfer_get_properties(DBusConnection *connection,
-					DBusMessage *message, void *user_data)
-{
-	struct transfer_data *transfer = user_data;
-	DBusMessage *reply;
-	DBusMessageIter iter, dict;
-
-	reply = dbus_message_new_method_return(message);
-	if (!reply)
 		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	append_entry(&dict, "Name", DBUS_TYPE_STRING, &transfer->name);
-	append_entry(&dict, "Size", DBUS_TYPE_UINT64, &transfer->size);
-	append_entry(&dict, "Filename", DBUS_TYPE_STRING, &transfer->filename);
-
-	dbus_message_iter_close_container(&iter, &dict);
-
-	return reply;
-}
-
-static void free_request(struct transfer_request *request)
-{
-	if (request->call)
-		dbus_pending_call_unref(request->call);
-
-	g_free(request);
-}
-
-static void free_transfer(struct transfer_data *transfer)
-{
-	struct session_data *session = transfer->session;
-
-	if (transfer->xfer) {
-		gw_obex_xfer_close(transfer->xfer, NULL);
-		gw_obex_xfer_free(transfer->xfer);
-	}
-
-	if (transfer->fd > 0)
-		close(transfer->fd);
-
-	if (transfer->request)
-		free_request(transfer->request);
-
-	session->pending = g_slist_remove(session->pending, transfer);
-
-	session_unref(session);
-
-	g_free(transfer->params);
-	g_free(transfer->filename);
-	g_free(transfer->name);
-	g_free(transfer->type);
-	g_free(transfer->path);
-	g_free(transfer->buffer);
-	g_free(transfer);
-}
-
-static void unregister_transfer(struct transfer_data *transfer)
-{
-	struct session_data *session = transfer->session;
-
-	/* Before unregistering cancel any pending call */
-	if (transfer->request)
-		dbus_pending_call_cancel(transfer->request->call);
-
-	if (transfer->path) {
-		g_dbus_unregister_interface(session->conn,
-			transfer->path, TRANSFER_INTERFACE);
-
-		debug("Transfer unregistered %s", transfer->path);
 	}
 
-	free_transfer(transfer);
-}
-
-static void agent_request_reply(DBusPendingCall *call, gpointer user_data)
-{
-	struct transfer_data *transfer = user_data;
-	struct transfer_request *request = transfer->request;
-	DBusMessage *reply = dbus_pending_call_steal_reply(call);
-	const char *name;
-	DBusError derr;
-
-	dbus_error_init(&derr);
-	if (dbus_set_error_from_message(&derr, reply)) {
-		error("Replied with an error: %s, %s",
-				derr.name, derr.message);
-		dbus_error_free(&derr);
-		goto fail;
-	}
-
-	dbus_message_get_args(reply, NULL,
-			DBUS_TYPE_STRING, &name,
-			DBUS_TYPE_INVALID);
-
-	if (strlen(name)) {
-		g_free(transfer->name);
-		transfer->name = g_strdup(name);
-	}
-
-	if (request->callback(transfer, request->data))
-		goto fail;
-
-	free_request(request);
-	transfer->request = NULL;
-
-	return;
-
-fail:
-	finalize_transfer(transfer);
-}
-
-static void agent_request(struct transfer_data *transfer,
-				transfer_callback_t cb, void *user_data)
-{
-	struct session_data *session = transfer->session;
-	DBusMessage *message;
-	DBusPendingCall *call;
-	struct transfer_request *request;
-
-	if (session->agent_name == NULL || session->agent_path == NULL ||
-			transfer == NULL || transfer->path == NULL) {
-		if (cb(transfer, user_data))
-			goto fail;
-
-		return;
-	}
-
-	message = dbus_message_new_method_call(session->agent_name,
-			session->agent_path, AGENT_INTERFACE, "Request");
-
-	dbus_message_append_args(message,
-			DBUS_TYPE_OBJECT_PATH, &transfer->path,
-			DBUS_TYPE_INVALID);
-
-	if (!dbus_connection_send_with_reply(session->conn, message, &call, -1)) {
-		dbus_message_unref(message);
-		return;
-	}
-
-	dbus_message_unref(message);
-
-	request = g_new0(struct transfer_request, 1);
-	request->call = call;
-	request->callback = cb;
-	request->data = user_data;
-	transfer->request = request;
-
-	dbus_pending_call_set_notify(call, agent_request_reply, transfer, NULL);
-
-	return;
-
-fail:
-	finalize_transfer(transfer);
-}
-
-static void put_xfer_progress(GwObexXfer *xfer, gpointer user_data)
-{
-	struct transfer_data *transfer = user_data;
-	struct session_data *session = transfer->session;
-	gint written;
-
-	if (transfer->buffer_len == 0) {
-		transfer->buffer_len = DEFAULT_BUFFER_SIZE;
-		transfer->buffer = g_new0(char, DEFAULT_BUFFER_SIZE);
-	}
-
-	do {
-		ssize_t len;
-
-		len = read(transfer->fd, transfer->buffer + transfer->filled,
-				transfer->buffer_len - transfer->filled);
-		if (len < 0)
-			goto failed;
-
-		transfer->filled += len;
-
-		if (transfer->filled == 0)
-			goto complete;
-
-		if (gw_obex_xfer_write(xfer, transfer->buffer,
-					transfer->filled,
-					&written, NULL) == FALSE)
-			goto failed;
-
-		transfer->filled -= written;
-		transfer->transferred += written;
-	} while (transfer->filled == 0);
-
-	memmove(transfer->buffer, transfer->buffer + written, transfer->filled);
-
-	agent_notify_progress(session->conn, session->agent_name,
-			session->agent_path, transfer->path,
-			transfer->transferred);
-	return;
-
-complete:
-	agent_notify_complete(session->conn, session->agent_name,
-				session->agent_path, transfer->path);
-	goto done;
-
-failed:
-	agent_notify_error(session->conn, session->agent_name,
-				session->agent_path, transfer->path,
-				"Error sending object");
-
-done:
-	finalize_transfer(transfer);
-}
-
-static int session_send_reply(struct transfer_data *transfer, void *data)
-{
-	struct session_data *session = transfer->session;
-
-	transfer->xfer = gw_obex_put_async(session->obex, transfer->name,
-						NULL, transfer->size, -1,
-						NULL);
-	if (transfer->xfer == NULL)
-		return -ENOTCONN;
-
-	gw_obex_xfer_set_callback(transfer->xfer, put_xfer_progress, transfer);
-
-	agent_notify_progress(session->conn, session->agent_name,
-			session->agent_path, transfer->path, 0);
-
-	return 0;
-}
-
-static void abort_transfer(struct transfer_data *transfer)
-{
-	struct session_data *session = transfer->session;
-
-	agent_notify_error(session->conn, session->agent_name,
-			session->agent_path, transfer->path,
-			"The transfer was cancelled");
-
-	if (transfer->request && transfer->request->call)
-		dbus_pending_call_cancel(transfer->request->call);
-
-	if (transfer->xfer) {
-		gw_obex_xfer_abort(transfer->xfer, NULL);
-		gw_obex_xfer_free(transfer->xfer);
-		transfer->xfer = NULL;
-	}
+	return session;
 }
 
-static void session_shutdown(struct session_data *session)
+void session_shutdown(struct session_data *session)
 {
 	struct transfer_data *transfer;
 
+	debug("session_shutdown(%p)", session);
 	transfer = session->pending ? session->pending->data : NULL;
 
-	/* Abort active transfer */
-	if (transfer)
-		abort_transfer(transfer);
+	session_ref(session);
 
 	/* Unregister any pending transfer */
-	g_slist_foreach(session->pending, (GFunc) unregister_transfer, NULL);
+	g_slist_foreach(session->pending, (GFunc) transfer_unregister, NULL);
 
 	session_unref(session);
 }
 
-static void finalize_transfer(struct transfer_data *transfer)
-{
-	struct session_data *session = transfer->session;
-
-	unregister_transfer(transfer);
-
-	if (session->pending == NULL) {
-		session_shutdown(session);
-		return;
-	}
-
-	/* Request next transfer */
-	agent_request(session->pending->data, session_send_reply, NULL);
-}
-
-static DBusMessage *transfer_cancel(DBusConnection *connection,
-					DBusMessage *message, void *user_data)
-{
-	struct transfer_data *transfer = user_data;
-	struct session_data *session = transfer->session;
-	const gchar *sender;
-	DBusMessage *reply;
-
-	sender = dbus_message_get_sender(message);
-	if (g_str_equal(sender, session->agent_name) == FALSE)
-		return g_dbus_create_error(message,
-				"org.openobex.Error.NotAuthorized",
-				"Not Authorized");
-
-	reply = dbus_message_new_method_return(message);
-	if (!reply)
-		return NULL;
-
-	abort_transfer(transfer);
-
-	finalize_transfer(transfer);
-
-	return reply;
-}
-
-static GDBusMethodTable transfer_methods[] = {
-	{ "GetProperties", "", "a{sv}", transfer_get_properties },
-	{ "Cancel", "", "", transfer_cancel },
-	{ }
-};
-
-static struct transfer_data *register_transfer(struct session_data *session,
-						const char *filename,
-						const char *name,
-						const char *type,
-						struct transfer_params *params)
-{
-	struct transfer_data *transfer;
-
-	transfer = g_new0(struct transfer_data, 1);
-	transfer->session = session_ref(session);
-	transfer->filename = g_strdup(filename);
-	transfer->name = g_strdup(name);
-	transfer->type = g_strdup(type);
-	transfer->params = params;
-
-	/* for OBEX specific mime types we don't need to register a transfer */
-	if (type != NULL &&
-			(strncmp(type, "x-obex/", 7) == 0 ||
-			strncmp(type, "x-bt/", 5) == 0))
-		goto done;
-
-	transfer->path = g_strdup_printf("%s/transfer%ju",
-			TRANSFER_BASEPATH, counter++);
-
-	if (g_dbus_register_interface(session->conn, transfer->path,
-				TRANSFER_INTERFACE,
-				transfer_methods, NULL, NULL,
-				transfer, NULL) == FALSE) {
-		free_transfer(transfer);
-		return NULL;
-	}
-
-	debug("Transfer registered %s", transfer->path);
-
-done:
-	session->pending = g_slist_append(session->pending, transfer);
-
-	return transfer;
-}
-
 static void agent_disconnected(DBusConnection *connection, void *user_data)
 {
 	struct session_data *session = user_data;
 
-	if (session->agent_name) {
-		g_free(session->agent_name);
-		session->agent_name = NULL;
-	}
+	session->agent->watch = 0;
 
-	if (session->agent_path) {
-		g_free(session->agent_path);
-		session->agent_path = NULL;
-	}
+	agent_free(session);
 }
 
 static DBusMessage *assign_agent(DBusConnection *connection,
 				DBusMessage *message, void *user_data)
 {
 	struct session_data *session = user_data;
-	const gchar *sender;
-	gchar *path;
+	const gchar *sender, *path;
 
 	if (dbus_message_get_args(message, NULL,
 					DBUS_TYPE_OBJECT_PATH, &path,
@@ -964,18 +553,12 @@ static DBusMessage *assign_agent(DBusConnection *connection,
 				"org.openobex.Error.InvalidArguments",
 				"Invalid arguments in method call");
 
-	if (session->agent_path != NULL || session->agent_name != NULL)
-		return g_dbus_create_error(message,
-				"org.openobex.Error.AlreadyExists",
-				"Already exists");
-
 	sender = dbus_message_get_sender(message);
 
-	session->agent_name = g_strdup(sender);
-	session->agent_path = g_strdup(path);
-
-	session->agent_watch = g_dbus_add_disconnect_watch(connection, sender,
-				agent_disconnected, session, NULL);
+	if (session_set_agent(session, sender, path) < 0)
+		return g_dbus_create_error(message,
+				"org.openobex.Error.AlreadyExists",
+				"Already exists");
 
 	return dbus_message_new_method_return(message);
 }
@@ -984,6 +567,7 @@ static DBusMessage *release_agent(DBusConnection *connection,
 				DBusMessage *message, void *user_data)
 {
 	struct session_data *session = user_data;
+	struct agent_data *agent = session->agent;
 	const gchar *sender;
 	gchar *path;
 
@@ -996,48 +580,56 @@ static DBusMessage *release_agent(DBusConnection *connection,
 
 	sender = dbus_message_get_sender(message);
 
-	if (g_str_equal(sender, session->agent_name) == FALSE ||
-				g_str_equal(path, session->agent_path) == FALSE)
+	if (agent == NULL || g_str_equal(sender, agent->name) == FALSE ||
+				g_str_equal(path, agent->path) == FALSE)
 		return g_dbus_create_error(message,
 				"org.openobex.Error.NotAuthorized",
 				"Not Authorized");
 
-	g_free(session->agent_name);
-	session->agent_name = NULL;
-
-	g_free(session->agent_path);
-	session->agent_path = NULL;
-
-	if (session->agent_watch) {
-		g_dbus_remove_watch(session->conn, session->agent_watch);
-		session->agent_watch = 0;
-	}
+	agent_free(session);
 
 	return dbus_message_new_method_return(message);
 }
 
-static DBusMessage *close_session(DBusConnection *connection,
-				DBusMessage *message, void *user_data)
+static void owner_disconnected(DBusConnection *connection, void *user_data)
 {
 	struct session_data *session = user_data;
-	const gchar *sender;
-
-	sender = dbus_message_get_sender(message);
-	if (g_str_equal(sender, session->owner) == FALSE)
-		return g_dbus_create_error(message,
-				"org.openobex.Error.NotAuthorized",
-				"Not Authorized");
 
 	session_shutdown(session);
-
-	return dbus_message_new_method_return(message);
 }
 
-static void owner_disconnected(DBusConnection *connection, void *user_data)
+static void append_entry(DBusMessageIter *dict,
+				const char *key, int type, void *val)
 {
-	struct session_data *session = user_data;
+	DBusMessageIter entry, value;
+	const char *signature;
 
-	session_shutdown(session);
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+								NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+	switch (type) {
+	case DBUS_TYPE_STRING:
+		signature = DBUS_TYPE_STRING_AS_STRING;
+		break;
+	case DBUS_TYPE_BYTE:
+		signature = DBUS_TYPE_BYTE_AS_STRING;
+		break;
+	case DBUS_TYPE_UINT64:
+		signature = DBUS_TYPE_UINT64_AS_STRING;
+		break;
+	default:
+		signature = DBUS_TYPE_VARIANT_AS_STRING;
+		break;
+	}
+
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+							signature, &value);
+	dbus_message_iter_append_basic(&value, type, val);
+	dbus_message_iter_close_container(&entry, &value);
+
+	dbus_message_iter_close_container(dict, &entry);
 }
 
 static DBusMessage *session_get_properties(DBusConnection *connection,
@@ -1068,9 +660,6 @@ static DBusMessage *session_get_properties(DBusConnection *connection,
 
 	append_entry(&dict, "Channel", DBUS_TYPE_BYTE, &session->channel);
 
-	if (session->agent_path)
-		append_entry(&dict, "AgentPath", DBUS_TYPE_STRING, &session->agent_path);
-
 	dbus_message_iter_close_container(&iter, &dict);
 
 	return reply;
@@ -1080,7 +669,6 @@ static GDBusMethodTable session_methods[] = {
 	{ "GetProperties",	"", "a{sv}",	session_get_properties	},
 	{ "AssignAgent",	"o", "",	assign_agent	},
 	{ "ReleaseAgent",	"o", "",	release_agent	},
-	{ "Close",		"", "",		close_session	},
 	{ }
 };
 
@@ -1208,189 +796,244 @@ static void get_file_callback(struct session_data *session, void *user_data)
 
 }
 
-static void get_xfer_listing_progress(GwObexXfer *xfer,
-					gpointer user_data)
+static void session_request_reply(DBusPendingCall *call, gpointer user_data)
 {
-	struct transfer_data *transfer = user_data;
-	struct transfer_params *params = transfer->params;
-	struct session_data *session = transfer->session;
-	gint bsize, bread, err = 0;
+	struct session_data *session = user_data;
+	struct agent_data *agent = session->agent;
+	struct agent_pending *pending = agent->pending;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	const char *name;
+	DBusError derr;
+
+	dbus_error_init(&derr);
+	if (dbus_set_error_from_message(&derr, reply)) {
+		error("Replied with an error: %s, %s",
+				derr.name, derr.message);
+		dbus_error_free(&derr);
+		goto fail;
+	}
+
+	dbus_message_get_args(reply, NULL,
+			DBUS_TYPE_STRING, &name,
+			DBUS_TYPE_INVALID);
 
-	bsize = transfer->buffer_len - transfer->filled;
+	debug("Agent.Request() reply: %s", name);
 
-	if (bsize < DEFAULT_BUFFER_SIZE) {
-		transfer->buffer_len += DEFAULT_BUFFER_SIZE;
-		transfer->buffer = g_realloc(transfer->buffer, transfer->buffer_len);
-		bsize += DEFAULT_BUFFER_SIZE;
+	if (strlen(name)) {
+		g_free(pending->transfer->name);
+		pending->transfer->name = g_strdup(name);
 	}
 
-	gw_obex_xfer_read(xfer, transfer->buffer + transfer->filled,
-			bsize, &bread, &err);
+	pending->cb(session, pending->transfer);
 
+	free_pending(pending);
+	agent->pending = NULL;
 
-	if (session->msg && err) {
-		DBusMessage *reply;
+	return;
 
-		reply = g_dbus_create_error(session->msg,
-				"org.openobex.Error.Failed",
-				OBEX_ResponseToString(err));
+fail:
+	transfer_unregister(pending->transfer);
+}
 
-		g_dbus_send_message(session->conn, reply);
+static int session_request(struct session_data *session, session_callback_t cb,
+				struct transfer_data *transfer)
+{
+	struct agent_data *agent = session->agent;
+	DBusMessage *message;
+	DBusPendingCall *call;
+	struct agent_pending *pending;
 
-		dbus_message_unref(session->msg);
-		session->msg = NULL;
+	if (agent == NULL || transfer->path == NULL) {
+		cb(session, transfer);
+		return 0;
 	}
 
-	if (err) {
-		error("gw_obex_xfer_read(): %s",
-				OBEX_ResponseToString(err));
-		goto complete;
+	message = dbus_message_new_method_call(agent->name,
+			agent->path, AGENT_INTERFACE, "Request");
+
+	dbus_message_append_args(message,
+			DBUS_TYPE_OBJECT_PATH, &transfer->path,
+			DBUS_TYPE_INVALID);
+
+
+	if (!dbus_connection_send_with_reply(session->conn, message, &call, -1)) {
+		dbus_message_unref(message);
+		return -ENOMEM;
 	}
 
-	transfer->filled += bread;
+	dbus_message_unref(message);
 
-	if (gw_obex_xfer_object_done(xfer)) {
-		if (transfer->buffer[transfer->filled - 1] == '\0')
-			goto complete;
+	pending = g_new0(struct agent_pending, 1);
+	pending->call = call;
+	pending->cb = cb;
+	pending->transfer = transfer;
+	agent->pending = pending;
 
-		bsize = transfer->buffer_len - transfer->filled;
-		if (bsize < 1) {
-			transfer->buffer_len += DEFAULT_BUFFER_SIZE;
-			transfer->buffer = g_realloc(transfer->buffer, transfer->buffer_len);
-		}
+	dbus_pending_call_set_notify(call, session_request_reply, session, NULL);
 
-		transfer->buffer[transfer->filled] = '\0';
-		goto complete;
-	}
+	debug("Agent.Request(\"%s\")", transfer->path);
 
-	return;
+	return 0;
+}
+
+static void session_terminate_transfer(struct session_data *session,
+					struct transfer_data *transfer)
+{
+	struct session_callback *callback = session->callback;
 
-complete:
-	if (err == 0) {
-		agent_notify_progress(session->conn, session->agent_name,
-				session->agent_path, transfer->path,
-				transfer->filled);
-		agent_notify_complete(session->conn, session->agent_name,
-				session->agent_path, transfer->path);
+	if (callback) {
+		callback->func(session, callback->data);
+		return;
 	}
 
-	params->cb(session, params->user_data);
+	session_ref(session);
 
-	unregister_transfer(transfer);
+	transfer_unregister(transfer);
+
+	if (session->pending)
+		session_request(session, session_prepare_put,
+				session->pending->data);
+
+	session_unref(session);
 }
 
-static void get_xfer_progress(GwObexXfer *xfer, gpointer user_data)
+static void session_notify_complete(struct session_data *session,
+				struct transfer_data *transfer)
 {
-	struct transfer_data *transfer = user_data;
-	struct transfer_params *params = transfer->params;
-	struct session_data *session = transfer->session;
-	gint bsize, bread, err = 0;
-	gboolean ret;
+	struct agent_data *agent = session->agent;
+	DBusMessage *message;
 
-	if (transfer->buffer_len == 0) {
-		transfer->buffer_len = DEFAULT_BUFFER_SIZE;
-		transfer->buffer = g_new0(char, DEFAULT_BUFFER_SIZE);
-	}
+	if (agent == NULL || transfer->path == NULL)
+		goto done;
 
-	bsize = transfer->buffer_len - transfer->filled;
+	message = dbus_message_new_method_call(agent->name,
+			agent->path, AGENT_INTERFACE, "Complete");
+	if (message == NULL)
+		return;
 
-	ret = gw_obex_xfer_read(xfer, transfer->buffer + transfer->filled,
-					bsize, &bread, &err);
+	dbus_message_set_no_reply(message, TRUE);
 
-	/* For GetFile reply on the first received stream */
-	if (transfer->fd > 0 && session->msg) {
+	dbus_message_append_args(message,
+			DBUS_TYPE_OBJECT_PATH, &transfer->path,
+			DBUS_TYPE_INVALID);
+
+	g_dbus_send_message(session->conn, message);
+
+done:
+
+	debug("Transfer(%p) complete", transfer);
+
+	session_terminate_transfer(session, transfer);
+}
+
+static void session_notify_error(struct session_data *session,
+				struct transfer_data *transfer,
+				const char *err)
+{
+	struct agent_data *agent = session->agent;
+	DBusMessage *message;
+
+	if (session->msg) {
 		DBusMessage *reply;
 
-		if (ret == FALSE)
-			reply = g_dbus_create_error(session->msg,
+		reply = g_dbus_create_error(session->msg,
 					"org.openobex.Error.Failed",
-					OBEX_ResponseToString(err));
-		else
-			reply = dbus_message_new_method_return(session->msg);
-
+					err);
 		g_dbus_send_message(session->conn, reply);
 
 		dbus_message_unref(session->msg);
 		session->msg = NULL;
 	}
 
-	if (ret == FALSE) {
-		error("gw_obex_xfer_read(): %s",
-				OBEX_ResponseToString(err));
-		goto complete;
-	}
+	if (agent == NULL || transfer->path == NULL)
+		goto done;
 
-	transfer->filled += bread;
-	transfer->transferred += bread;
-	if (transfer->size == 0)
-		transfer->size = gw_obex_xfer_object_size(xfer);
+	message = dbus_message_new_method_call(agent->name,
+			agent->path, AGENT_INTERFACE, "Error");
+	if (message == NULL)
+		return;
 
-	if (transfer->fd > 0) {
-		gint w;
+	dbus_message_set_no_reply(message, TRUE);
 
-		w = write(transfer->fd, transfer->buffer, bread);
-		if (w < 0) {
-			ret = FALSE;
-			goto complete;
-		}
+	dbus_message_append_args(message,
+			DBUS_TYPE_OBJECT_PATH, &transfer->path,
+			DBUS_TYPE_STRING, &err,
+			DBUS_TYPE_INVALID);
 
-		transfer->filled = 0;
-	}
+	g_dbus_send_message(session->conn, message);
 
-	if (transfer->transferred == transfer->size)
-		goto complete;
+done:
+	error("Transfer(%p) Error: %s", transfer, err);
 
-	gw_obex_xfer_flush(xfer, NULL);
+	session_terminate_transfer(session, transfer);
+}
 
-	agent_notify_progress(session->conn, session->agent_name,
-			session->agent_path, transfer->path,
-			transfer->transferred);
+static void session_notify_progress(struct session_data *session,
+					struct transfer_data *transfer,
+					gint64 transferred)
+{
+	struct agent_data *agent = session->agent;
+	DBusMessage *message;
 
-	return;
+	/* For GetFile reply on the first received stream */
+	if (transfer->fd > 0 && session->msg) {
+		DBusMessage *reply;
+
+		reply = dbus_message_new_method_return(session->msg);
+		g_dbus_send_message(session->conn, reply);
+
+		dbus_message_unref(session->msg);
+		session->msg = NULL;
+	}
 
-complete:
+	if (agent == NULL || transfer->path == NULL)
+		goto done;
+
+	message = dbus_message_new_method_call(agent->name,
+			agent->path, AGENT_INTERFACE, "Progress");
+	if (message == NULL)
+		goto done;
+
+	dbus_message_set_no_reply(message, TRUE);
+
+	dbus_message_append_args(message,
+			DBUS_TYPE_OBJECT_PATH, &transfer->path,
+			DBUS_TYPE_UINT64, &transferred,
+			DBUS_TYPE_INVALID);
 
-	if (ret == TRUE) {
-		agent_notify_progress(session->conn, session->agent_name,
-				session->agent_path, transfer->path,
-				transfer->transferred);
-		agent_notify_complete(session->conn, session->agent_name,
-				session->agent_path, transfer->path);
-	} else
-		agent_notify_error(session->conn, session->agent_name,
-				session->agent_path, transfer->path,
-				"Error getting object");
+	g_dbus_send_message(session->conn, message);
 
-	params->cb(session, params->user_data);
+done:
+	debug("Transfer(%p) progress: %ld bytes", transfer,
+			(long int ) transferred);
 
-	unregister_transfer(transfer);
+	if (transferred == transfer->size)
+		session_notify_complete(session, transfer);
 }
 
-static int session_get_reply(struct transfer_data *transfer, void *data)
+static void transfer_progress(struct transfer_data *transfer, gint64 transferred,
+				int err, void *user_data)
 {
-	struct session_data *session = transfer->session;
-
-	transfer->xfer = gw_obex_get_async_with_apparam(session->obex,
-							transfer->filename,
-							transfer->type,
-							transfer->params->data,
-							transfer->params->size,
-							NULL);
-	if (transfer->xfer == NULL) {
-		unregister_transfer(transfer);
-		return -EIO;
-	}
+	struct session_data *session = user_data;
 
-	if (transfer->type == NULL)
-		gw_obex_xfer_set_callback(transfer->xfer, get_xfer_progress, transfer);
-	else
-		gw_obex_xfer_set_callback(transfer->xfer, get_xfer_listing_progress,
-					transfer);
+	if (err != 0)
+		goto fail;
 
-	agent_notify_progress(session->conn, session->agent_name,
-			session->agent_path, transfer->path, 0);
+	session_notify_progress(session, transfer, transferred);
 
-	return 0;
+	return;
+
+fail:
+	session_notify_error(session, transfer,
+			err > 0 ? OBEX_ResponseToString(err) : strerror(-err));
+}
+
+static void session_prepare_get(struct session_data *session, void *data)
+{
+	struct transfer_data *transfer = data;
+
+	if (transfer_get(transfer, transfer_progress, session) < 0)
+		transfer_unregister(transfer);
 }
 
 int session_get(struct session_data *session, const char *type,
@@ -1400,41 +1043,32 @@ int session_get(struct session_data *session, const char *type,
 {
 	struct transfer_data *transfer;
 	struct transfer_params *params;
-	int err, fd = 0;
+	int err;
 
 	if (session->obex == NULL)
 		return -ENOTCONN;
 
-	if (type == NULL) {
-		if (targetname == NULL)
-			targetname = filename;
-
-		fd = open(targetname, O_WRONLY | O_CREAT, 0600);
-		if (fd < 0) {
-			err = errno;
-			error("open(): %s(%d)", strerror(err), err);
-			return -err;
-		}
-	}
-
 	params = g_new0(struct transfer_params, 1);
 	params->data = apparam;
 	params->size = apparam_size;
-	params->cb = func;
 
-	transfer = register_transfer(session, filename, targetname, type,
+	transfer = transfer_register(session, filename, targetname, type,
 					params);
 	if (transfer == NULL) {
-		if (fd)
-			close(fd);
-
 		g_free(params);
 		return -EIO;
 	}
 
-	transfer->fd = fd;
+	if (func != NULL) {
+		struct session_callback *callback;
+		callback = g_new0(struct session_callback, 1);
+		callback->func = func;
+		session->callback = callback;
+	}
 
-	agent_request(transfer, session_get_reply, NULL);
+	err = session_request(session, session_prepare_get, transfer);
+	if (err < 0)
+		return err;
 
 	return 0;
 }
@@ -1606,46 +1240,30 @@ int session_send(struct session_data *session, const char *filename,
 				const char *targetname)
 {
 	struct transfer_data *transfer;
-	struct stat st;
-	int fd, err;
+	int err;
 
 	if (session->obex == NULL)
 		return -ENOTCONN;
 
-	transfer = register_transfer(session, filename, targetname, NULL, NULL);
+	transfer = transfer_register(session, filename, targetname, NULL,
+					NULL);
 	if (transfer == NULL) {
 		err = -EINVAL;
 		goto fail;
 	}
 
-	fd = open(filename, O_RDONLY);
-	if (fd < 0) {
-		err = -EIO;
-		goto fail;
-	}
-
-	if (fstat(fd, &st) < 0) {
-		close(fd);
-		err = -EIO;
-		goto fail;
-	}
-
-	transfer->fd = fd;
-
 	/* Transfer should start if it is the first in the pending list */
 	if (transfer != session->pending->data)
 		return 0;
 
-	agent_request(transfer, session_send_reply, NULL);
+	err = session_request(session, session_prepare_put, transfer);
+	if (err < 0)
+		goto fail;
 
 	return 0;
 
 fail:
-	agent_notify_error(session->conn, session->agent_name,
-			session->agent_path, transfer->path,
-			"Could not open file for sending");
-
-	unregister_transfer(transfer);
+	transfer_unregister(transfer);
 
 	return err;
 }
@@ -1655,36 +1273,30 @@ int session_pull(struct session_data *session,
 				session_callback_t function, void *user_data)
 {
 	struct transfer_data *transfer;
-	struct transfer_params *params;
+	int err;
 
 	if (session->obex == NULL)
 		return -ENOTCONN;
 
-	transfer = register_transfer(session, NULL, NULL, type, NULL);
-	if (transfer == NULL)
+	transfer = transfer_register(session, NULL, NULL, type, NULL);
+	if (transfer == NULL) {
 		return -EIO;
-
-	params = g_try_malloc0(sizeof(*params));
-	if (params == NULL) {
-		unregister_transfer(transfer);
-		return -ENOMEM;
 	}
 
-	params->cb = function;
-	params->user_data = user_data;
-
-	transfer->xfer = gw_obex_get_async(session->obex, NULL, type, NULL);
-	if (transfer->xfer == NULL) {
-		unregister_transfer(transfer);
-		g_free(params);
-		return -ENOTCONN;
+	if (function != NULL) {
+		struct session_callback *callback;
+		callback = g_new0(struct session_callback, 1);
+		callback->func = function;
+		callback->data = user_data;
+		session->callback = callback;
 	}
 
-	transfer->params = params;
-
-	gw_obex_xfer_set_callback(transfer->xfer, get_xfer_listing_progress, transfer);
+	err = session_request(session, session_prepare_get, transfer);
+	if (err == 0)
+		return 0;
 
-	return 0;
+	transfer_unregister(transfer);
+	return err;
 }
 
 int session_register(struct session_data *session)
@@ -1720,9 +1332,7 @@ int session_register(struct session_data *session)
 		return -EIO;
 	}
 
-	session->owner_watch = g_dbus_add_disconnect_watch(session->conn,
-					session->owner, owner_disconnected,
-								session, NULL);
+	debug("Session(%p) registered %s", session, session->path);
 
 	return 0;
 }
@@ -1737,64 +1347,18 @@ void session_set_data(struct session_data *session, void *priv)
 	session->priv = priv;
 }
 
-static void put_buf_xfer_progress(GwObexXfer *xfer, gpointer user_data)
-{
-	struct transfer_data *transfer = user_data;
-	struct session_data *session = transfer->session;
-	gint written;
-
-	if (transfer->transferred == transfer->size)
-		goto complete;
-
-	if (gw_obex_xfer_write(xfer, transfer->buffer + transfer->transferred,
-				transfer->size - transfer->transferred,
-				&written, NULL) == FALSE)
-		goto complete;
-
-	if (gw_obex_xfer_flush(xfer, NULL) == FALSE)
-		goto complete;
-
-	transfer->transferred += written;
-
-	agent_notify_progress(session->conn, session->agent_name,
-		session->agent_path, transfer->path,
-		transfer->transferred);
-
-	return;
-
-complete:
-	if (transfer->transferred == transfer->size)
-		agent_notify_complete(session->conn, session->agent_name,
-			session->agent_path, transfer->path);
-	else
-		agent_notify_error(session->conn, session->agent_name,
-			session->agent_path, transfer->path,
-			"Error sending object");
-
-	finalize_transfer(transfer);
-}
-
-static int session_put_reply(struct transfer_data *transfer, void *data)
+static void session_prepare_put(struct session_data *session, void *data)
 {
-	struct session_data *session = transfer->session;
-
-	transfer->xfer = gw_obex_put_async(session->obex, transfer->name, NULL,
-						transfer->size, -1, NULL);
-	if (transfer->xfer == NULL)
-		return -ENOTCONN;
-
-	gw_obex_xfer_set_callback(transfer->xfer, put_buf_xfer_progress,
-					transfer);
-
-	agent_notify_progress(session->conn, session->agent_name,
-		session->agent_path, transfer->path, 0);
+	struct transfer_data *transfer = data;
 
-	return 0;
+	if (transfer_put(transfer, transfer_progress, session) < 0)
+		transfer_unregister(transfer);
 }
 
 int session_put(struct session_data *session, char *buf, const char *targetname)
 {
 	struct transfer_data *transfer;
+	int err;
 
 	if (session->obex == NULL)
 		return -ENOTCONN;
@@ -1802,14 +1366,16 @@ int session_put(struct session_data *session, char *buf, const char *targetname)
 	if (session->pending != NULL)
 		return -EISCONN;
 
-	transfer = register_transfer(session, NULL, targetname, NULL, NULL);
+	transfer = transfer_register(session, NULL, targetname, NULL, NULL);
 	if (transfer == NULL)
 		return -EIO;
 
 	transfer->size = strlen(buf);
 	transfer->buffer = buf;
 
-	agent_request(transfer, session_put_reply, NULL);
+	err = session_request(session, session_prepare_put, transfer);
+	if (err < 0)
+		return err;
 
 	return 0;
 }
@@ -1817,19 +1383,67 @@ int session_put(struct session_data *session, char *buf, const char *targetname)
 int session_set_agent(struct session_data *session, const char *name,
 							const char *path)
 {
+	struct agent_data *agent;
+
+	if (session == NULL)
+		return -EINVAL;
+
+	if (session->agent)
+		return -EALREADY;
+
+	agent = g_new0(struct agent_data, 1);
+	agent->name = g_strdup(name);
+	agent->path = g_strdup(path);
+
+	if (session->watch == 0)
+		session_set_owner(session, name, owner_disconnected);
+
+	agent->watch = g_dbus_add_disconnect_watch(session->conn, name,
+							agent_disconnected,
+							session, NULL);
+
+	session->agent = agent;
+
+	return 0;
+}
+
+const char *session_get_agent(struct session_data *session)
+{
+	struct agent_data *agent;
+
+	if (session == NULL)
+		return NULL;
+
+	agent = session->agent;
+	if (agent == NULL)
+		return NULL;
+
+	return agent->name;
+}
+
+int session_set_owner(struct session_data *session, const char *name,
+			GDBusWatchFunction func)
+{
 	if (session == NULL)
 		return -EINVAL;
 
-	if (session->agent_name != NULL || session->agent_path != NULL ||
-			session->owner_watch != 0)
+	if (session->watch != 0)
 		return -EALREADY;
 
-	session->agent_name = g_strdup(name);
-	session->agent_path = g_strdup(path);
+	session->watch = g_dbus_add_disconnect_watch(session->conn, name, func,
+							session, NULL);
+	if (session->watch == 0)
+		return -EINVAL;
 
-	session->owner_watch = g_dbus_add_disconnect_watch(session->conn,
-					session->owner, owner_disconnected,
-								session, NULL);
+	session->owner = g_strdup(name);
 
 	return 0;
 }
+
+const char *session_get_owner(struct session_data *session)
+{
+	if (session == NULL)
+		return NULL;
+
+	return session->owner;
+}
diff --git a/obexd/client/session.h b/obexd/client/session.h
index 377e811..73337cd 100644
--- a/obexd/client/session.h
+++ b/obexd/client/session.h
@@ -23,30 +23,13 @@
 
 #include <glib.h>
 #include <gdbus.h>
+#include <gw-obex.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sdp.h>
-#include <gw-obex.h>
 
-struct transfer_request;
-struct transfer_params;
-
-struct transfer_data {
-	struct session_data *session;
-	struct transfer_params *params;
-	struct transfer_request *request;
-	char *path;		/* Transfer path */
-	gchar *filename;	/* Transfer file location */
-	char *name;		/* Transfer object name */
-	char *type;		/* Transfer object type */
-	int fd;
-	GwObexXfer *xfer;
-	char *buffer;
-	size_t buffer_len;
-	int filled;
-	gint64 size;
-	gint64 transferred;
-};
+struct agent_data;
+struct session_callback;
 
 struct session_data {
 	gint refcount;
@@ -61,11 +44,10 @@ struct session_data {
 	DBusConnection *conn;
 	DBusMessage *msg;
 	GwObex *obex;
-	gchar *agent_name;
-	gchar *agent_path;
-	guint agent_watch;
+	struct agent_data *agent;
+	struct session_callback *callback;
 	gchar *owner;		/* Session owner */
-	guint owner_watch;
+	guint watch;
 	GSList *pending;
 	void *priv;
 };
@@ -73,12 +55,23 @@ struct session_data {
 typedef void (*session_callback_t) (struct session_data *session,
 							void *user_data);
 
-int session_create(const char *source,
+struct session_data *session_create(const char *source,
 			const char *destination, const char *target,
 			uint8_t channel, session_callback_t function,
 			void *user_data);
+
+struct session_data *session_ref(struct session_data *session);
+void session_unref(struct session_data *session);
+void session_shutdown(struct session_data *session);
+
+int session_set_owner(struct session_data *session, const char *name,
+			GDBusWatchFunction func);
+const char *session_get_owner(struct session_data *session);
+
 int session_set_agent(struct session_data *session, const char *name,
 							const char *path);
+const char *session_get_agent(struct session_data *session);
+
 int session_send(struct session_data *session, const char *filename,
 				const char *remotename);
 int session_get(struct session_data *session, const char *type,
diff --git a/obexd/client/sync.c b/obexd/client/sync.c
index c475fd0..af0b38e 100644
--- a/obexd/client/sync.c
+++ b/obexd/client/sync.c
@@ -29,6 +29,7 @@
 #include <glib.h>
 #include <gdbus.h>
 
+#include "transfer.h"
 #include "session.h"
 #include "sync.h"
 
diff --git a/obexd/client/transfer.c b/obexd/client/transfer.c
new file mode 100644
index 0000000..c24c1ce
--- /dev/null
+++ b/obexd/client/transfer.c
@@ -0,0 +1,505 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  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 <string.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <gdbus.h>
+#include <gw-obex.h>
+
+#include "logging.h"
+#include "transfer.h"
+#include "session.h"
+
+#define TRANSFER_INTERFACE  "org.openobex.Transfer"
+#define TRANSFER_BASEPATH   "/org/openobex"
+
+#define DEFAULT_BUFFER_SIZE 4096
+
+static guint64 counter = 0;
+
+struct transfer_callback {
+	transfer_callback_t func;
+	void *data;
+};
+
+static void append_entry(DBusMessageIter *dict,
+				const char *key, int type, void *val)
+{
+	DBusMessageIter entry, value;
+	const char *signature;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+								NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+	switch (type) {
+	case DBUS_TYPE_STRING:
+		signature = DBUS_TYPE_STRING_AS_STRING;
+		break;
+	case DBUS_TYPE_BYTE:
+		signature = DBUS_TYPE_BYTE_AS_STRING;
+		break;
+	case DBUS_TYPE_UINT64:
+		signature = DBUS_TYPE_UINT64_AS_STRING;
+		break;
+	default:
+		signature = DBUS_TYPE_VARIANT_AS_STRING;
+		break;
+	}
+
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+							signature, &value);
+	dbus_message_iter_append_basic(&value, type, val);
+	dbus_message_iter_close_container(&entry, &value);
+
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static DBusMessage *transfer_get_properties(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct transfer_data *transfer = user_data;
+	DBusMessage *reply;
+	DBusMessageIter iter, dict;
+
+	reply = dbus_message_new_method_return(message);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	append_entry(&dict, "Name", DBUS_TYPE_STRING, &transfer->name);
+	append_entry(&dict, "Size", DBUS_TYPE_UINT64, &transfer->size);
+	append_entry(&dict, "Filename", DBUS_TYPE_STRING, &transfer->filename);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *transfer_cancel(DBusConnection *connection,
+					DBusMessage *message, void *user_data)
+{
+	struct transfer_data *transfer = user_data;
+	struct session_data *session = transfer->session;
+	const gchar *sender, *agent;
+	DBusMessage *reply;
+
+	sender = dbus_message_get_sender(message);
+	agent = session_get_agent(session);
+	if (g_str_equal(sender, agent) == FALSE)
+		return g_dbus_create_error(message,
+				"org.openobex.Error.NotAuthorized",
+				"Not Authorized");
+
+	reply = dbus_message_new_method_return(message);
+	if (!reply)
+		return NULL;
+
+	transfer_abort(transfer);
+
+	return reply;
+}
+
+static GDBusMethodTable transfer_methods[] = {
+	{ "GetProperties", "", "a{sv}", transfer_get_properties },
+	{ "Cancel", "", "", transfer_cancel },
+	{ }
+};
+
+static void transfer_free(struct transfer_data *transfer)
+{
+	struct session_data *session = transfer->session;
+
+	if (transfer->xfer) {
+		gw_obex_xfer_close(transfer->xfer, NULL);
+		gw_obex_xfer_free(transfer->xfer);
+	}
+
+	if (transfer->fd > 0)
+		close(transfer->fd);
+
+	session->pending = g_slist_remove(session->pending, transfer);
+
+	session_unref(session);
+
+	g_free(transfer->params);
+	g_free(transfer->callback);
+	g_free(transfer->filename);
+	g_free(transfer->name);
+	g_free(transfer->type);
+	g_free(transfer->path);
+	g_free(transfer->buffer);
+	g_free(transfer);
+}
+
+struct transfer_data *transfer_register(struct session_data *session,
+						const char *filename,
+						const char *name,
+						const char *type,
+						struct transfer_params *params)
+{
+	struct transfer_data *transfer;
+
+	transfer = g_new0(struct transfer_data, 1);
+	transfer->session = session_ref(session);
+	transfer->filename = g_strdup(filename);
+	transfer->name = g_strdup(name);
+	transfer->type = g_strdup(type);
+	transfer->params = params;
+
+	/* for OBEX specific mime types we don't need to register a transfer */
+	if (type != NULL &&
+			(strncmp(type, "x-obex/", 7) == 0 ||
+			strncmp(type, "x-bt/", 5) == 0))
+		goto done;
+
+	transfer->path = g_strdup_printf("%s/transfer%ju",
+			TRANSFER_BASEPATH, counter++);
+
+	if (g_dbus_register_interface(session->conn, transfer->path,
+				TRANSFER_INTERFACE,
+				transfer_methods, NULL, NULL,
+				transfer, NULL) == FALSE) {
+		transfer_free(transfer);
+		return NULL;
+	}
+
+	debug("Transfer(%p) registered %s", transfer, transfer->path);
+
+done:
+	session->pending = g_slist_append(session->pending, transfer);
+
+	return transfer;
+}
+
+void transfer_unregister(struct transfer_data *transfer)
+{
+	struct session_data *session = transfer->session;
+
+	if (transfer->path) {
+		g_dbus_unregister_interface(session->conn,
+			transfer->path, TRANSFER_INTERFACE);
+
+		debug("Transfer(%p) unregistered %s", transfer,
+					transfer->path);
+	}
+
+	transfer_free(transfer);
+}
+
+static void get_xfer_listing_progress(GwObexXfer *xfer,
+					gpointer user_data)
+{
+	struct transfer_data *transfer = user_data;
+	struct transfer_callback *callback = transfer->callback;
+	gint bsize, bread;
+
+	bsize = transfer->buffer_len - transfer->filled;
+
+	if (bsize < DEFAULT_BUFFER_SIZE) {
+		transfer->buffer_len += DEFAULT_BUFFER_SIZE;
+		transfer->buffer = g_realloc(transfer->buffer, transfer->buffer_len);
+		bsize += DEFAULT_BUFFER_SIZE;
+	}
+
+	if (gw_obex_xfer_read(xfer, transfer->buffer + transfer->filled,
+			bsize, &bread, &transfer->err) == FALSE)
+		goto done;
+
+	transfer->filled += bread;
+
+	if (gw_obex_xfer_object_done(xfer)) {
+		if (transfer->buffer[transfer->filled - 1] == '\0')
+			goto done;
+
+		bsize = transfer->buffer_len - transfer->filled;
+		if (bsize < 1) {
+			transfer->buffer_len += DEFAULT_BUFFER_SIZE;
+			transfer->buffer = g_realloc(transfer->buffer, transfer->buffer_len);
+		}
+
+		transfer->buffer[transfer->filled] = '\0';
+		goto done;
+	}
+
+	return;
+
+done:
+	transfer->size = strlen(transfer->buffer);
+	if (callback)
+		callback->func(transfer, transfer->size, transfer->err,
+				callback->data);
+}
+
+static void get_xfer_progress(GwObexXfer *xfer, gpointer user_data)
+{
+	struct transfer_data *transfer = user_data;
+	struct transfer_callback *callback = transfer->callback;
+	gint bsize, bread;
+	gboolean ret;
+
+	if (transfer->buffer_len == 0) {
+		transfer->buffer_len = DEFAULT_BUFFER_SIZE;
+		transfer->buffer = g_new0(char, DEFAULT_BUFFER_SIZE);
+	}
+
+	bsize = transfer->buffer_len - transfer->filled;
+
+	ret = gw_obex_xfer_read(xfer, transfer->buffer + transfer->filled,
+					bsize, &bread, &transfer->err);
+	if (ret == FALSE)
+		goto done;
+
+	transfer->filled += bread;
+	transfer->transferred += bread;
+	if (transfer->size == 0)
+		transfer->size = gw_obex_xfer_object_size(xfer);
+
+	if (transfer->fd > 0) {
+		gint w;
+
+		w = write(transfer->fd, transfer->buffer, bread);
+		if (w < 0) {
+			transfer->err = -errno;
+			goto done;
+		}
+
+		transfer->filled = 0;
+	}
+
+	if (transfer->transferred == transfer->size)
+		goto done;
+
+	gw_obex_xfer_flush(xfer, NULL);
+
+done:
+	if (callback)
+		callback->func(transfer, transfer->transferred, transfer->err,
+				callback->data);
+}
+
+static void put_buf_xfer_progress(GwObexXfer *xfer, gpointer user_data)
+{
+	struct transfer_data *transfer = user_data;
+	struct transfer_callback *callback = transfer->callback;
+	gint written;
+
+	if (transfer->transferred == transfer->size)
+		goto done;
+
+	if (gw_obex_xfer_write(xfer, transfer->buffer + transfer->transferred,
+				transfer->size - transfer->transferred,
+				&written, &transfer->err) == FALSE)
+		goto done;
+
+	if (gw_obex_xfer_flush(xfer, &transfer->err) == FALSE)
+		goto done;
+
+	transfer->transferred += written;
+
+done:
+	if (callback)
+		callback->func(transfer, transfer->transferred, transfer->err,
+				callback->data);
+}
+
+static void put_xfer_progress(GwObexXfer *xfer, gpointer user_data)
+{
+	struct transfer_data *transfer = user_data;
+	struct transfer_callback *callback = transfer->callback;
+	gint written, err = 0;
+
+	if (transfer->buffer_len == 0) {
+		transfer->buffer_len = DEFAULT_BUFFER_SIZE;
+		transfer->buffer = g_new0(char, DEFAULT_BUFFER_SIZE);
+	}
+
+	do {
+		ssize_t len;
+
+		len = read(transfer->fd, transfer->buffer + transfer->filled,
+				transfer->buffer_len - transfer->filled);
+		if (len < 0) {
+			err = -errno;
+			goto done;
+		}
+
+		transfer->filled += len;
+
+		if (transfer->filled == 0)
+			goto done;
+
+		if (gw_obex_xfer_write(xfer, transfer->buffer,
+					transfer->filled,
+					&written, &err) == FALSE)
+			goto done;
+
+		transfer->filled -= written;
+		transfer->transferred += written;
+	} while (transfer->filled == 0);
+
+	memmove(transfer->buffer, transfer->buffer + written, transfer->filled);
+
+done:
+	if (callback)
+		callback->func(transfer, transfer->transferred, err,
+				callback->data);
+}
+
+static void transfer_set_callback(struct transfer_data *transfer,
+					transfer_callback_t func,
+					void *user_data)
+{
+	struct transfer_callback *callback;
+
+	g_free(transfer->callback);
+
+	callback = g_new0(struct transfer_callback, 1);
+	callback->func = func;
+	callback->data = user_data;
+
+	transfer->callback = callback;
+}
+
+int transfer_get(struct transfer_data *transfer, transfer_callback_t func,
+			void *user_data)
+{
+	struct session_data *session = transfer->session;
+	gw_obex_xfer_cb_t cb;
+
+	if (transfer->xfer != NULL)
+		return -EALREADY;
+
+	if (transfer->type == NULL) {
+		int fd = open(transfer->name ? : transfer->filename,
+				O_WRONLY | O_CREAT, 0600);
+		if (transfer->fd < 0) {
+			error("open(): %s(%d)", strerror(errno), errno);
+			return -errno;
+		}
+		transfer->fd = fd;
+		cb = get_xfer_progress;
+	} else
+		cb = get_xfer_listing_progress;
+
+	if (transfer->params != NULL)
+		transfer->xfer = gw_obex_get_async_with_apparam(session->obex,
+							transfer->filename,
+							transfer->type,
+							transfer->params->data,
+							transfer->params->size,
+							NULL);
+	else
+		transfer->xfer = gw_obex_get_async(session->obex,
+							transfer->filename,
+							transfer->type,
+							NULL);
+	if (transfer->xfer == NULL)
+		return -ENOTCONN;
+
+	if (func)
+		transfer_set_callback(transfer, func, user_data);
+
+	gw_obex_xfer_set_callback(transfer->xfer, cb, transfer);
+
+	return 0;
+}
+
+int transfer_put(struct transfer_data *transfer, transfer_callback_t func,
+			void *user_data)
+{
+	struct session_data *session = transfer->session;
+	gw_obex_xfer_cb_t cb;
+	struct stat st;
+	int fd;
+
+	if (transfer->xfer != NULL)
+		return -EALREADY;
+
+	if (transfer->buffer) {
+		cb = put_buf_xfer_progress;
+		goto done;
+	}
+
+	fd = open(transfer->filename, O_RDONLY);
+	if (fd < 0) {
+		error("open(): %s(%d)", strerror(errno), errno);
+		return -errno;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		close(fd);
+		error("fstat(): %s(%d)", strerror(errno), errno);
+		return -errno;
+	}
+
+	transfer->fd = fd;
+	transfer->size = st.st_size;
+	cb = put_xfer_progress;
+
+done:
+	transfer->xfer = gw_obex_put_async(session->obex, transfer->name,
+						transfer->type, transfer->size,
+						-1, NULL);
+	if (transfer->xfer == NULL)
+		return -ENOTCONN;
+
+	if (func)
+		transfer_set_callback(transfer, func, user_data);
+
+	gw_obex_xfer_set_callback(transfer->xfer, cb, transfer);
+
+	return 0;
+}
+
+void transfer_abort(struct transfer_data *transfer)
+{
+	struct transfer_callback *callback = transfer->callback;
+
+	if (transfer->xfer == NULL)
+		return;
+
+	gw_obex_xfer_abort(transfer->xfer, NULL);
+	gw_obex_xfer_free(transfer->xfer);
+	transfer->xfer = NULL;
+
+	if (callback)
+		callback->func(transfer, transfer->transferred, -ECANCELED,
+				callback->data);
+}
diff --git a/obexd/client/transfer.h b/obexd/client/transfer.h
new file mode 100644
index 0000000..dc0f3a4
--- /dev/null
+++ b/obexd/client/transfer.h
@@ -0,0 +1,67 @@
+/*
+ *
+ *  OBEX Client
+ *
+ *  Copyright (C) 2007-2010  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 <gw-obex.h>
+
+struct transfer_params {
+	const guint8 *data;
+	gint size;
+};
+
+struct transfer_callback;
+
+struct transfer_data {
+	struct session_data *session;
+	struct transfer_params *params;
+	struct transfer_callback *callback;
+	char *path;		/* Transfer path */
+	gchar *filename;	/* Transfer file location */
+	char *name;		/* Transfer object name */
+	char *type;		/* Transfer object type */
+	int fd;
+	GwObexXfer *xfer;
+	char *buffer;
+	size_t buffer_len;
+	int filled;
+	gint64 size;
+	gint64 transferred;
+	int err;
+};
+
+typedef void (*transfer_callback_t) (struct transfer_data *transfer,
+					gint64 transferred, gint err,
+					void *user_data);
+
+struct transfer_data *transfer_register(struct session_data *session,
+						const char *filename,
+						const char *name,
+						const char *type,
+						struct transfer_params *params);
+
+void transfer_unregister(struct transfer_data *transfer);
+
+int transfer_get(struct transfer_data *transfer, transfer_callback_t func,
+			void *user_data);
+int transfer_put(struct transfer_data *transfer, transfer_callback_t func,
+			void *user_data);
+void transfer_abort(struct transfer_data *transfer);