5 * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
6 * Copyright (C) 2011-2012 BMW Car IT GmbH. All rights reserved.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 #include "transport.h"
48 #define SESSION_INTERFACE "org.bluez.obex.Session"
49 #define ERROR_INTERFACE "org.bluez.obex.Error"
50 #define SESSION_BASEPATH "/org/bluez/obex"
52 #define OBEX_IO_ERROR obex_io_error_quark()
53 #define OBEX_IO_ERROR_FIRST (0xff + 1)
56 OBEX_IO_DISCONNECTED = OBEX_IO_ERROR_FIRST,
60 static guint64 counter = 0;
62 struct callback_data {
63 struct obc_session *session;
64 session_callback_t func;
68 struct pending_request {
71 struct obc_session *session;
72 struct obc_transfer *transfer;
73 session_callback_t func;
80 session_callback_t func;
90 struct obc_transport *transport;
91 struct obc_driver *driver;
92 gchar *path; /* Session path */
95 struct pending_request *p;
96 gchar *owner; /* Session owner */
99 guint queue_complete_id;
102 static GSList *sessions = NULL;
104 static void session_process_queue(struct obc_session *session);
105 static void session_terminate_transfer(struct obc_session *session,
106 struct obc_transfer *transfer,
108 static void transfer_complete(struct obc_transfer *transfer,
109 GError *err, void *user_data);
111 static GQuark obex_io_error_quark(void)
113 return g_quark_from_static_string("obex-io-error-quark");
116 struct obc_session *obc_session_ref(struct obc_session *session)
118 g_atomic_int_inc(&session->refcount);
120 DBG("%p: ref=%d", session, session->refcount);
125 static void session_unregistered(struct obc_session *session)
129 if (session->driver && session->driver->remove)
130 session->driver->remove(session);
132 path = session->path;
133 session->path = NULL;
135 g_dbus_unregister_interface(session->conn, path, SESSION_INTERFACE);
137 DBG("Session(%p) unregistered %s", session, path);
142 static struct pending_request *pending_request_new(struct obc_session *session,
143 struct obc_transfer *transfer,
144 session_callback_t func,
147 struct pending_request *p;
150 p = g_new0(struct pending_request, 1);
152 p->session = obc_session_ref(session);
153 p->transfer = transfer;
160 static void pending_request_free(struct pending_request *p)
163 obc_transfer_unregister(p->transfer);
166 obc_session_unref(p->session);
171 static void session_free(struct obc_session *session)
175 if (session->queue_complete_id != 0)
176 g_source_remove(session->queue_complete_id);
178 if (session->queue) {
179 g_queue_foreach(session->queue, (GFunc) pending_request_free,
181 g_queue_free(session->queue);
185 g_dbus_remove_watch(session->conn, session->watch);
187 if (session->obex != NULL)
188 g_obex_unref(session->obex);
190 if (session->id > 0 && session->transport != NULL)
191 session->transport->disconnect(session->id);
194 session_unregistered(session);
197 dbus_connection_unref(session->conn);
200 pending_request_free(session->p);
202 sessions = g_slist_remove(sessions, session);
204 g_free(session->path);
205 g_free(session->owner);
206 g_free(session->source);
207 g_free(session->destination);
211 void obc_session_unref(struct obc_session *session)
215 ret = g_atomic_int_dec_and_test(&session->refcount);
217 DBG("%p: ref=%d", session, session->refcount);
222 session_free(session);
225 static void connect_cb(GObex *obex, GError *err, GObexPacket *rsp,
228 struct callback_data *callback = user_data;
233 error("connect_cb: %s", err->message);
234 gerr = g_error_copy(err);
238 rsp_code = g_obex_packet_get_operation(rsp, NULL);
239 if (rsp_code != G_OBEX_RSP_SUCCESS)
240 gerr = g_error_new(OBEX_IO_ERROR, -EIO,
241 "OBEX Connect failed with 0x%02x", rsp_code);
244 callback->func(callback->session, NULL, gerr, callback->data);
247 obc_session_unref(callback->session);
251 static void transport_func(GIOChannel *io, GError *err, gpointer user_data)
253 struct callback_data *callback = user_data;
254 struct obc_session *session = callback->session;
255 struct obc_driver *driver = session->driver;
256 struct obc_transport *transport = session->transport;
258 GObexTransportType type;
265 error("%s", err->message);
269 g_io_channel_set_close_on_unref(io, FALSE);
271 if (transport->getpacketopt &&
272 transport->getpacketopt(io, &tx_mtu, &rx_mtu) == 0)
273 type = G_OBEX_TRANSPORT_PACKET;
275 type = G_OBEX_TRANSPORT_STREAM;
277 obex = g_obex_new(io, type, tx_mtu, rx_mtu);
281 g_io_channel_set_close_on_unref(io, TRUE);
283 if (driver->target != NULL)
284 g_obex_connect(obex, connect_cb, callback, &err,
285 G_OBEX_HDR_TARGET, driver->target, driver->target_len,
288 g_obex_connect(obex, connect_cb, callback, &err,
292 error("%s", err->message);
297 session->obex = obex;
298 sessions = g_slist_prepend(sessions, session);
302 callback->func(callback->session, NULL, err, callback->data);
303 obc_session_unref(callback->session);
307 static void owner_disconnected(DBusConnection *connection, void *user_data)
309 struct obc_session *session = user_data;
313 obc_session_shutdown(session);
316 int obc_session_set_owner(struct obc_session *session, const char *name,
317 GDBusWatchFunction func)
323 g_dbus_remove_watch(session->conn, session->watch);
325 session->watch = g_dbus_add_disconnect_watch(session->conn, name, func,
327 if (session->watch == 0)
330 session->owner = g_strdup(name);
336 static struct obc_session *session_find(const char *source,
337 const char *destination,
344 for (l = sessions; l; l = l->next) {
345 struct obc_session *session = l->data;
347 if (g_strcmp0(session->destination, destination))
350 if (g_strcmp0(service, session->driver->service))
353 if (source && g_strcmp0(session->source, source))
356 if (channel && session->channel != channel)
359 if (g_strcmp0(owner, session->owner))
368 static gboolean connection_complete(gpointer data)
370 struct callback_data *cb = data;
372 cb->func(cb->session, NULL, NULL, cb->data);
374 obc_session_unref(cb->session);
381 static int session_connect(struct obc_session *session,
382 session_callback_t function, void *user_data)
384 struct callback_data *callback;
385 struct obc_transport *transport = session->transport;
386 struct obc_driver *driver = session->driver;
388 callback = g_try_malloc0(sizeof(*callback));
389 if (callback == NULL)
392 callback->func = function;
393 callback->data = user_data;
394 callback->session = obc_session_ref(session);
396 /* Connection completed */
398 g_idle_add(connection_complete, callback);
402 /* Ongoing connection */
403 if (session->id > 0) {
404 obc_session_unref(callback->session);
409 session->id = transport->connect(session->source, session->destination,
410 driver->uuid, session->channel,
411 transport_func, callback);
412 if (session->id == 0) {
413 obc_session_unref(callback->session);
421 struct obc_session *obc_session_create(const char *source,
422 const char *destination,
426 session_callback_t function,
429 DBusConnection *conn;
430 struct obc_session *session;
431 struct obc_transport *transport;
432 struct obc_driver *driver;
434 if (destination == NULL)
437 session = session_find(source, destination, service, channel, owner);
441 /* FIXME: Do proper transport lookup when the API supports it */
442 transport = obc_transport_find("Bluetooth");
443 if (transport == NULL)
446 driver = obc_driver_find(service);
450 conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
454 session = g_try_malloc0(sizeof(*session));
458 session->refcount = 1;
459 session->transport = transport;
460 session->driver = driver;
461 session->conn = conn;
462 session->source = g_strdup(source);
463 session->destination = g_strdup(destination);
464 session->channel = channel;
465 session->queue = g_queue_new();
468 obc_session_set_owner(session, owner, owner_disconnected);
471 if (session_connect(session, function, user_data) < 0) {
472 obc_session_unref(session);
476 DBG("session %p transport %s driver %s", session,
477 session->transport->name, session->driver->service);
482 void obc_session_shutdown(struct obc_session *session)
484 struct pending_request *p;
489 obc_session_ref(session);
491 /* Unregister any pending transfer */
492 err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
493 "Session closed by user");
495 if (session->p != NULL && session->p->id != 0) {
500 p->func(session, p->transfer, err, p->data);
502 pending_request_free(p);
505 while ((p = g_queue_pop_head(session->queue))) {
507 p->func(session, p->transfer, err, p->data);
509 pending_request_free(p);
514 /* Unregister interfaces */
516 session_unregistered(session);
518 /* Disconnect transport */
519 if (session->id > 0 && session->transport != NULL) {
520 session->transport->disconnect(session->id);
524 obc_session_unref(session);
527 static DBusMessage *session_get_properties(DBusConnection *connection,
528 DBusMessage *message, void *user_data)
530 struct obc_session *session = user_data;
532 DBusMessageIter iter, dict;
534 reply = dbus_message_new_method_return(message);
538 dbus_message_iter_init_append(reply, &iter);
540 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
541 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
542 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
543 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
545 if (session->source != NULL)
546 obex_dbus_dict_append(&dict, "Source", DBUS_TYPE_STRING,
549 obex_dbus_dict_append(&dict, "Destination", DBUS_TYPE_STRING,
550 &session->destination);
552 obex_dbus_dict_append(&dict, "Channel", DBUS_TYPE_BYTE,
555 dbus_message_iter_close_container(&iter, &dict);
560 static void capabilities_complete_callback(struct obc_session *session,
561 struct obc_transfer *transfer,
562 GError *err, void *user_data)
564 DBusMessage *message = user_data;
570 DBusMessage *error = g_dbus_create_error(message,
571 ERROR_INTERFACE ".Failed",
573 g_dbus_send_message(session->conn, error);
577 perr = obc_transfer_get_contents(transfer, &contents, &size);
579 DBusMessage *error = g_dbus_create_error(message,
580 ERROR_INTERFACE ".Failed",
581 "Error reading contents: %s",
583 g_dbus_send_message(session->conn, error);
587 g_dbus_send_reply(session->conn, message,
588 DBUS_TYPE_STRING, &contents,
593 dbus_message_unref(message);
596 static DBusMessage *get_capabilities(DBusConnection *connection,
597 DBusMessage *message, void *user_data)
599 struct obc_session *session = user_data;
600 struct obc_transfer *pull;
604 pull = obc_transfer_get("x-obex/capability", NULL, NULL, &gerr);
608 if (!obc_session_queue(session, pull, capabilities_complete_callback,
612 dbus_message_ref(message);
617 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
624 static const GDBusMethodTable session_methods[] = {
625 { GDBUS_METHOD("GetProperties",
626 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
627 session_get_properties) },
628 { GDBUS_ASYNC_METHOD("GetCapabilities",
629 NULL, GDBUS_ARGS({ "capabilities", "s" }),
634 static gboolean session_queue_complete(gpointer data)
636 struct obc_session *session = data;
638 session_process_queue(session);
640 session->queue_complete_id = 0;
645 guint obc_session_queue(struct obc_session *session,
646 struct obc_transfer *transfer,
647 session_callback_t func, void *user_data,
650 struct pending_request *p;
652 if (session->obex == NULL) {
653 obc_transfer_unregister(transfer);
654 g_set_error(err, OBEX_IO_ERROR, -ENOTCONN,
655 "Session not connected");
659 if (!obc_transfer_register(transfer, session->conn, session->path,
660 session->owner, err)) {
661 obc_transfer_unregister(transfer);
665 obc_transfer_set_callback(transfer, transfer_complete, session);
667 p = pending_request_new(session, transfer, func, user_data);
668 g_queue_push_tail(session->queue, p);
670 if (session->queue_complete_id == 0)
671 session->queue_complete_id = g_idle_add(
672 session_queue_complete, session);
677 static void session_process_queue(struct obc_session *session)
679 struct pending_request *p;
681 if (session->p != NULL)
684 if (session->queue == NULL || g_queue_is_empty(session->queue))
687 obc_session_ref(session);
689 while ((p = g_queue_pop_head(session->queue))) {
692 DBG("Transfer(%p) started", p->transfer);
694 if (obc_transfer_start(p->transfer, session->obex, &gerr)) {
700 p->func(session, p->transfer, gerr, p->data);
702 g_clear_error(&gerr);
704 pending_request_free(p);
707 obc_session_unref(session);
710 static gint pending_transfer_cmptransfer(gconstpointer a, gconstpointer b)
712 const struct pending_request *p = a;
713 const struct obc_transfer *transfer = b;
715 if (p->transfer == transfer)
721 static void session_terminate_transfer(struct obc_session *session,
722 struct obc_transfer *transfer,
725 struct pending_request *p = session->p;
727 if (p == NULL || p->transfer != transfer) {
730 match = g_list_find_custom(session->queue->head, transfer,
731 pending_transfer_cmptransfer);
736 g_queue_delete_link(session->queue, match);
740 obc_session_ref(session);
743 p->func(session, p->transfer, gerr, p->data);
745 pending_request_free(p);
747 if (session->p == NULL)
748 session_process_queue(session);
750 obc_session_unref(session);
753 static void session_notify_complete(struct obc_session *session,
754 struct obc_transfer *transfer)
756 DBG("Transfer(%p) complete", transfer);
758 session_terminate_transfer(session, transfer, NULL);
761 static void session_notify_error(struct obc_session *session,
762 struct obc_transfer *transfer,
765 error("Transfer(%p) Error: %s", transfer, err->message);
767 session_terminate_transfer(session, transfer, err);
770 static void transfer_complete(struct obc_transfer *transfer,
771 GError *err, void *user_data)
773 struct obc_session *session = user_data;
778 session_notify_complete(session, transfer);
783 session_notify_error(session, transfer, err);
786 const char *obc_session_register(struct obc_session *session,
787 GDBusDestroyFunction destroy)
790 return session->path;
792 session->path = g_strdup_printf("%s/session%ju",
793 SESSION_BASEPATH, counter++);
795 if (g_dbus_register_interface(session->conn, session->path,
796 SESSION_INTERFACE, session_methods,
797 NULL, NULL, session, destroy) == FALSE)
800 if (session->driver->probe && session->driver->probe(session) < 0) {
801 g_dbus_unregister_interface(session->conn, session->path,
806 DBG("Session(%p) registered %s", session, session->path);
808 return session->path;
811 g_free(session->path);
812 session->path = NULL;
816 const char *obc_session_get_owner(struct obc_session *session)
821 return session->owner;
824 const char *obc_session_get_path(struct obc_session *session)
826 return session->path;
829 const char *obc_session_get_target(struct obc_session *session)
831 return session->driver->target;
834 static void setpath_complete(struct obc_session *session,
835 struct obc_transfer *transfer,
836 GError *err, void *user_data)
838 struct pending_request *p = user_data;
839 struct setpath_data *data = p->data;
842 data->func(session, NULL, err, data->user_data);
844 g_strfreev(data->remaining);
850 pending_request_free(p);
852 session_process_queue(session);
855 static void setpath_cb(GObex *obex, GError *err, GObexPacket *rsp,
858 struct pending_request *p = user_data;
859 struct setpath_data *data = p->data;
866 setpath_complete(p->session, NULL, err, user_data);
870 code = g_obex_packet_get_operation(rsp, NULL);
871 if (code != G_OBEX_RSP_SUCCESS) {
873 g_set_error(&gerr, OBEX_IO_ERROR, code, "%s",
874 g_obex_strerror(code));
875 setpath_complete(p->session, NULL, err, user_data);
876 g_clear_error(&gerr);
880 next = data->remaining[data->index];
882 setpath_complete(p->session, NULL, NULL, user_data);
888 p->req_id = g_obex_setpath(obex, next, setpath_cb, p, &err);
890 setpath_complete(p->session, NULL, err, data);
895 guint obc_session_setpath(struct obc_session *session, const char *path,
896 session_callback_t func, void *user_data,
899 struct setpath_data *data;
900 struct pending_request *p;
901 const char *first = "";
903 if (session->obex == NULL) {
904 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
905 "Session disconnected");
909 if (session->p != NULL) {
910 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY,
915 data = g_new0(struct setpath_data, 1);
917 data->user_data = user_data;
918 data->remaining = g_strsplit(path, "/", 3);
920 p = pending_request_new(session, NULL, setpath_complete, data);
923 if (path[0] != '/') {
924 first = data->remaining[data->index];
928 p->req_id = g_obex_setpath(session->obex, first, setpath_cb, p, err);
937 g_strfreev(data->remaining);
939 pending_request_free(p);
943 static void async_cb(GObex *obex, GError *err, GObexPacket *rsp,
946 struct pending_request *p = user_data;
947 struct obc_session *session = p->session;
955 p->func(p->session, NULL, err, p->data);
959 code = g_obex_packet_get_operation(rsp, NULL);
960 if (code != G_OBEX_RSP_SUCCESS)
961 g_set_error(&gerr, OBEX_IO_ERROR, code, "%s",
962 g_obex_strerror(code));
965 p->func(p->session, NULL, gerr, p->data);
968 g_clear_error(&gerr);
971 pending_request_free(p);
974 session_process_queue(session);
977 guint obc_session_mkdir(struct obc_session *session, const char *folder,
978 session_callback_t func, void *user_data,
981 struct pending_request *p;
983 if (session->obex == NULL) {
984 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
985 "Session disconnected");
989 if (session->p != NULL) {
990 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy");
995 p = pending_request_new(session, NULL, func, user_data);
997 p->req_id = g_obex_mkdir(session->obex, folder, async_cb, p, err);
999 pending_request_free(p);
1007 guint obc_session_copy(struct obc_session *session, const char *srcname,
1008 const char *destname, session_callback_t func,
1009 void *user_data, GError **err)
1011 struct pending_request *p;
1013 if (session->obex == NULL) {
1014 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1015 "Session disconnected");
1019 if (session->p != NULL) {
1020 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy");
1024 p = pending_request_new(session, NULL, func, user_data);
1026 p->req_id = g_obex_copy(session->obex, srcname, destname, async_cb, p,
1029 pending_request_free(p);
1037 guint obc_session_move(struct obc_session *session, const char *srcname,
1038 const char *destname, session_callback_t func,
1039 void *user_data, GError **err)
1041 struct pending_request *p;
1043 if (session->obex == NULL) {
1044 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1045 "Session disconnected");
1049 if (session->p != NULL) {
1050 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy");
1054 p = pending_request_new(session, NULL, func, user_data);
1056 p->req_id = g_obex_move(session->obex, srcname, destname, async_cb, p,
1059 pending_request_free(p);
1067 guint obc_session_delete(struct obc_session *session, const char *file,
1068 session_callback_t func, void *user_data,
1071 struct pending_request *p;
1073 if (session->obex == NULL) {
1074 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1075 "Session disconnected");
1079 if (session->p != NULL) {
1080 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy");
1084 p = pending_request_new(session, NULL, func, user_data);
1086 p->req_id = g_obex_delete(session->obex, file, async_cb, p, err);
1088 pending_request_free(p);
1096 void obc_session_cancel(struct obc_session *session, guint id,
1099 struct pending_request *p = session->p;
1101 if (p == NULL || p->id != id)
1107 g_obex_cancel_req(session->obex, p->req_id, remove);
1111 pending_request_free(p);
1114 session_process_queue(session);