From 9ce74b2d68a43e17a56bf63885e9e5fa266d7466 Mon Sep 17 00:00:00 2001 From: Luiz Augusto Von Dentz Date: Tue, 20 Apr 2010 15:33:59 +0300 Subject: [PATCH] obexd: Move transfer implementation to its own file --- obexd/client/main.c | 109 +++- obexd/client/pbap.c | 1 + obexd/client/session.c | 1226 ++++++++++++++------------------------- obexd/client/session.h | 43 +- obexd/client/sync.c | 1 + obexd/client/transfer.c | 505 ++++++++++++++++ obexd/client/transfer.h | 67 +++ 7 files changed, 1110 insertions(+), 842 deletions(-) create mode 100644 obexd/client/transfer.c create mode 100644 obexd/client/transfer.h diff --git a/obexd/client/main.c b/obexd/client/main.c index 9a7e8e65b..aff7a99ec 100644 --- a/obexd/client/main.c +++ b/obexd/client/main.c @@ -35,6 +35,7 @@ #include #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 15e1d4754..cb3def49e 100644 --- a/obexd/client/pbap.c +++ b/obexd/client/pbap.c @@ -31,6 +31,7 @@ #include #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 3f41bfa8a..40e930f93 100644 --- a/obexd/client/session.c +++ b/obexd/client/session.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -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,34 +1366,84 @@ 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; } 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 377e811eb..73337cd7e 100644 --- a/obexd/client/session.h +++ b/obexd/client/session.h @@ -23,30 +23,13 @@ #include #include +#include #include #include -#include -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 c475fd0d0..af0b38eff 100644 --- a/obexd/client/sync.c +++ b/obexd/client/sync.c @@ -29,6 +29,7 @@ #include #include +#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 000000000..c24c1ceec --- /dev/null +++ b/obexd/client/transfer.c @@ -0,0 +1,505 @@ +/* + * + * OBEX Client + * + * Copyright (C) 2007-2010 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 000000000..dc0f3a416 --- /dev/null +++ b/obexd/client/transfer.h @@ -0,0 +1,67 @@ +/* + * + * OBEX Client + * + * Copyright (C) 2007-2010 Marcel Holtmann + * + * + * 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 + +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); -- 2.47.3