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
#include <gdbus.h>
#include "logging.h"
+#include "transfer.h"
#include "session.h"
#define CLIENT_SERVICE "org.openobex.client"
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;
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);
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);
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;
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);
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);
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",
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;
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);
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;
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);
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)
{
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);
done:
+ shutdown_session(session);
dbus_message_unref(data->message);
dbus_connection_unref(data->connection);
g_free(data->sender);
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);
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;
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);
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
#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
#include <glib.h>
#include <gdbus.h>
+#include <gw-obex.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#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,
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);
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;
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)
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;
session->conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
if (session->conn == NULL) {
session_free(session);
- return -ENOMEM;
+ return NULL;
}
if (source == NULL)
} 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);
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,
"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);
}
DBusMessage *message, void *user_data)
{
struct session_data *session = user_data;
+ struct agent_data *agent = session->agent;
const gchar *sender;
gchar *path;
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,
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;
{ "GetProperties", "", "a{sv}", session_get_properties },
{ "AssignAgent", "o", "", assign_agent },
{ "ReleaseAgent", "o", "", release_agent },
- { "Close", "", "", close_session },
{ }
};
}
-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,
{
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;
}
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;
}
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)
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;
}
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;
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 377e811..73337cd 100644
--- a/obexd/client/session.h
+++ b/obexd/client/session.h
#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;
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;
};
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
#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
+/*
+ *
+ * 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
+/*
+ *
+ * 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);