5 * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include <sys/socket.h>
35 #include "gdbus/gdbus.h"
36 #include "gobex/gobex.h"
38 #include "btio/btio.h"
41 #include "obex-priv.h"
47 #define OBEX_BASE_PATH "/org/bluez/obex"
48 #define SESSION_BASE_PATH OBEX_BASE_PATH "/server"
49 #define OBEX_MANAGER_INTERFACE OBEXD_SERVICE ".AgentManager1"
50 #define ERROR_INTERFACE OBEXD_SERVICE ".Error"
51 #define TRANSFER_INTERFACE OBEXD_SERVICE ".Transfer1"
52 #define SESSION_INTERFACE OBEXD_SERVICE ".Session1"
53 #define AGENT_INTERFACE OBEXD_SERVICE ".Agent1"
55 #define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */
60 gboolean auth_pending;
63 unsigned int watch_id;
67 TRANSFER_STATUS_QUEUED = 0,
68 TRANSFER_STATUS_ACTIVE,
69 TRANSFER_STATUS_COMPLETE,
73 struct obex_transfer {
76 struct obex_session *session;
79 static struct agent *agent = NULL;
81 static DBusConnection *connection = NULL;
83 static void agent_free(struct agent *agent)
88 g_free(agent->new_folder);
89 g_free(agent->new_name);
90 g_free(agent->bus_name);
95 static inline DBusMessage *invalid_args(DBusMessage *msg)
97 return g_dbus_create_error(msg,
98 ERROR_INTERFACE ".InvalidArguments",
99 "Invalid arguments in method call");
102 static inline DBusMessage *not_supported(DBusMessage *msg)
104 return g_dbus_create_error(msg,
105 ERROR_INTERFACE ".NotSupported",
106 "Operation is not supported");
109 static inline DBusMessage *agent_already_exists(DBusMessage *msg)
111 return g_dbus_create_error(msg,
112 ERROR_INTERFACE ".AlreadyExists",
113 "Agent already exists");
116 static inline DBusMessage *agent_does_not_exist(DBusMessage *msg)
118 return g_dbus_create_error(msg,
119 ERROR_INTERFACE ".DoesNotExist",
120 "Agent does not exist");
123 static inline DBusMessage *not_authorized(DBusMessage *msg)
125 return g_dbus_create_error(msg,
126 ERROR_INTERFACE ".NotAuthorized",
130 static void agent_disconnected(DBusConnection *conn, void *user_data)
137 static DBusMessage *register_agent(DBusConnection *conn,
138 DBusMessage *msg, void *data)
140 const char *path, *sender;
143 return agent_already_exists(msg);
145 if (!dbus_message_get_args(msg, NULL,
146 DBUS_TYPE_OBJECT_PATH, &path,
148 return invalid_args(msg);
150 sender = dbus_message_get_sender(msg);
151 agent = g_new0(struct agent, 1);
152 agent->bus_name = g_strdup(sender);
153 agent->path = g_strdup(path);
155 agent->watch_id = g_dbus_add_disconnect_watch(conn, sender,
156 agent_disconnected, NULL, NULL);
158 DBG("Agent registered");
160 return dbus_message_new_method_return(msg);
163 static DBusMessage *unregister_agent(DBusConnection *conn,
164 DBusMessage *msg, void *data)
166 const char *path, *sender;
169 return agent_does_not_exist(msg);
171 if (!dbus_message_get_args(msg, NULL,
172 DBUS_TYPE_OBJECT_PATH, &path,
174 return invalid_args(msg);
176 if (strcmp(agent->path, path) != 0)
177 return agent_does_not_exist(msg);
179 sender = dbus_message_get_sender(msg);
180 if (strcmp(agent->bus_name, sender) != 0)
181 return not_authorized(msg);
183 g_dbus_remove_watch(conn, agent->watch_id);
188 DBG("Agent unregistered");
190 return dbus_message_new_method_return(msg);
193 #ifdef __TIZEN_PATCH__
194 static DBusMessage *set_root(DBusConnection *conn, DBusMessage *msg,
195 const char *root, void *data)
197 DBG("new_root: %s", root);
199 /* Change the option root path (using in filesystem) */
200 obex_option_set_root_folder(root);
202 return dbus_message_new_method_return(msg);
205 static DBusMessage *set_property(DBusConnection *conn,
206 DBusMessage *msg, void *data)
208 DBusMessageIter iter;
210 const char *property;
212 if (!dbus_message_iter_init(msg, &iter))
213 return invalid_args(msg);
215 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
216 return invalid_args(msg);
218 dbus_message_iter_get_basic(&iter, &property);
219 dbus_message_iter_next(&iter);
221 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
222 return invalid_args(msg);
223 dbus_message_iter_recurse(&iter, &sub);
225 if (g_str_equal("Root", property)) {
228 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
229 return invalid_args(msg);
230 dbus_message_iter_get_basic(&sub, &root);
232 return set_root(conn, msg, root, data);
235 return invalid_args(msg);
239 static gboolean get_source(const GDBusPropertyTable *property,
240 DBusMessageIter *iter, void *data)
242 struct obex_session *os = data;
246 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &s);
251 static gboolean get_destination(const GDBusPropertyTable *property,
252 DBusMessageIter *iter, void *data)
254 struct obex_session *os = data;
258 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &s);
263 static gboolean session_target_exists(const GDBusPropertyTable *property,
266 struct obex_session *os = data;
268 return os->service->target ? TRUE : FALSE;
271 static char *target2str(const uint8_t *t)
276 return g_strdup_printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-"
277 "%02X%02X-%02X%02X%02X%02X%02X%02X",
278 t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7],
279 t[8], t[9], t[10], t[11], t[12], t[13], t[14],
283 static gboolean get_target(const GDBusPropertyTable *property,
284 DBusMessageIter *iter, void *data)
286 struct obex_session *os = data;
289 uuid = target2str(os->service->target);
290 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
296 static gboolean get_root(const GDBusPropertyTable *property,
297 DBusMessageIter *iter, void *data)
301 root = obex_option_root_folder();
302 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &root);
307 static DBusMessage *transfer_cancel(DBusConnection *connection,
308 DBusMessage *msg, void *user_data)
310 struct obex_transfer *transfer = user_data;
311 struct obex_session *os = transfer->session;
315 return invalid_args(msg);
317 sender = dbus_message_get_sender(msg);
318 if (strcmp(agent->bus_name, sender) != 0)
319 return not_authorized(msg);
323 return dbus_message_new_method_return(msg);
326 static const char *status2str(uint8_t status)
329 case TRANSFER_STATUS_QUEUED:
331 case TRANSFER_STATUS_ACTIVE:
333 case TRANSFER_STATUS_COMPLETE:
335 case TRANSFER_STATUS_ERROR:
341 static gboolean transfer_get_status(const GDBusPropertyTable *property,
342 DBusMessageIter *iter, void *data)
344 struct obex_transfer *transfer = data;
345 const char *status = status2str(transfer->status);
347 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
352 static gboolean transfer_get_session(const GDBusPropertyTable *property,
353 DBusMessageIter *iter, void *data)
355 struct obex_transfer *transfer = data;
356 struct obex_session *session = transfer->session;
362 path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, session->id);
364 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
371 static gboolean transfer_name_exists(const GDBusPropertyTable *property,
374 struct obex_transfer *transfer = data;
375 struct obex_session *session = transfer->session;
377 return session->name != NULL;
380 static gboolean transfer_get_name(const GDBusPropertyTable *property,
381 DBusMessageIter *iter, void *data)
383 struct obex_transfer *transfer = data;
384 struct obex_session *session = transfer->session;
386 if (session->name == NULL)
389 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->name);
394 static gboolean transfer_type_exists(const GDBusPropertyTable *property,
397 struct obex_transfer *transfer = data;
398 struct obex_session *session = transfer->session;
400 return session->type != NULL;
403 static gboolean transfer_get_type(const GDBusPropertyTable *property,
404 DBusMessageIter *iter, void *data)
406 struct obex_transfer *transfer = data;
407 struct obex_session *session = transfer->session;
409 if (session->type == NULL)
412 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->type);
417 static gboolean transfer_size_exists(const GDBusPropertyTable *property,
420 struct obex_transfer *transfer = data;
421 struct obex_session *session = transfer->session;
423 return session->size != OBJECT_SIZE_UNKNOWN;
426 static gboolean transfer_get_size(const GDBusPropertyTable *property,
427 DBusMessageIter *iter, void *data)
429 struct obex_transfer *transfer = data;
430 struct obex_session *session = transfer->session;
432 if (session->size == OBJECT_SIZE_UNKNOWN)
435 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &session->size);
440 static gboolean transfer_time_exists(const GDBusPropertyTable *property,
443 struct obex_transfer *transfer = data;
444 struct obex_session *session = transfer->session;
446 return session->time != 0;
449 static gboolean transfer_get_time(const GDBusPropertyTable *property,
450 DBusMessageIter *iter, void *data)
452 struct obex_transfer *transfer = data;
453 struct obex_session *session = transfer->session;
454 dbus_uint64_t time_u64;
456 if (session->size == 0)
459 time_u64 = session->time;
461 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &time_u64);
466 static gboolean transfer_filename_exists(const GDBusPropertyTable *property,
469 struct obex_transfer *transfer = data;
470 struct obex_session *session = transfer->session;
472 return session->path != NULL;
475 static gboolean transfer_get_filename(const GDBusPropertyTable *property,
476 DBusMessageIter *iter, void *data)
478 struct obex_transfer *transfer = data;
479 struct obex_session *session = transfer->session;
481 if (session->path == NULL)
484 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &session->path);
489 #ifdef __TIZEN_PATCH__
490 static gboolean transfer_operation_exists(const GDBusPropertyTable *property,
493 struct obex_transfer *transfer = data;
494 struct obex_session *session = transfer->session;
496 if (session->cmd == G_OBEX_OP_PUT &&
497 session->size != OBJECT_SIZE_DELETE)
499 else if (session->cmd == G_OBEX_OP_GET)
505 static gboolean transfer_get_operation(const GDBusPropertyTable *property,
506 DBusMessageIter *iter, void *data)
508 struct obex_transfer *transfer = data;
509 struct obex_session *session = transfer->session;
510 const char *operation;
512 if (session->cmd == G_OBEX_OP_PUT &&
513 session->size != OBJECT_SIZE_DELETE)
515 else if (session->cmd == G_OBEX_OP_GET)
520 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &operation);
525 static gboolean transfer_address_exists(const GDBusPropertyTable *property,
528 struct obex_transfer *transfer = data;
529 struct obex_session *session = transfer->session;
533 err = obex_getpeername(session, &address);
542 static gboolean transfer_get_address(const GDBusPropertyTable *property,
543 DBusMessageIter *iter, void *data)
545 struct obex_transfer *transfer = data;
546 struct obex_session *session = transfer->session;
550 err = obex_getpeername(session, &address);
554 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &address);
562 static gboolean transfer_get_transferred(const GDBusPropertyTable *property,
563 DBusMessageIter *iter, void *data)
565 struct obex_transfer *transfer = data;
566 struct obex_session *session = transfer->session;
568 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64,
574 static const GDBusMethodTable manager_methods[] = {
575 { GDBUS_METHOD("RegisterAgent",
576 GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) },
577 { GDBUS_METHOD("UnregisterAgent",
578 GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) },
579 #ifdef __TIZEN_PATCH__
580 { GDBUS_METHOD("SetProperty",
581 GDBUS_ARGS({ "property", "sv" }), NULL, set_property) },
586 static const GDBusMethodTable transfer_methods[] = {
587 { GDBUS_METHOD("Cancel", NULL, NULL, transfer_cancel) },
591 static const GDBusPropertyTable transfer_properties[] = {
592 { "Status", "s", transfer_get_status },
593 { "Session", "o", transfer_get_session },
594 { "Name", "s", transfer_get_name, NULL, transfer_name_exists },
595 { "Type", "s", transfer_get_type, NULL, transfer_type_exists },
596 { "Size", "t", transfer_get_size, NULL, transfer_size_exists },
597 { "Time", "t", transfer_get_time, NULL, transfer_time_exists },
598 { "Filename", "s", transfer_get_filename, NULL,
599 transfer_filename_exists },
600 #ifdef __TIZEN_PATCH__
601 { "Operation", "s", transfer_get_operation, NULL,
602 transfer_operation_exists },
603 { "Address", "s", transfer_get_address, NULL,
604 transfer_address_exists },
606 { "Transferred", "t", transfer_get_transferred },
610 static const GDBusPropertyTable session_properties[] = {
611 { "Source", "s", get_source },
612 { "Destination", "s", get_destination },
613 { "Target", "s", get_target, NULL, session_target_exists },
614 { "Root", "s", get_root },
618 gboolean manager_init(void)
624 dbus_error_init(&err);
626 connection = g_dbus_setup_bus(DBUS_BUS_SESSION, OBEXD_SERVICE, &err);
627 if (connection == NULL) {
628 if (dbus_error_is_set(&err) == TRUE) {
629 fprintf(stderr, "%s\n", err.message);
630 dbus_error_free(&err);
632 fprintf(stderr, "Can't register with session bus\n");
636 g_dbus_attach_object_manager(connection);
638 return g_dbus_register_interface(connection, OBEX_BASE_PATH,
639 OBEX_MANAGER_INTERFACE,
640 manager_methods, NULL, NULL,
644 void manager_cleanup(void)
648 g_dbus_unregister_interface(connection, OBEX_BASE_PATH,
649 OBEX_MANAGER_INTERFACE);
651 /* FIXME: Release agent? */
655 g_dbus_detach_object_manager(connection);
657 dbus_connection_unref(connection);
660 void manager_emit_transfer_started(struct obex_transfer *transfer)
662 transfer->status = TRANSFER_STATUS_ACTIVE;
664 g_dbus_emit_property_changed(connection, transfer->path,
665 TRANSFER_INTERFACE, "Status");
668 static void emit_transfer_completed(struct obex_transfer *transfer,
671 if (transfer->path == NULL)
674 transfer->status = success ? TRANSFER_STATUS_COMPLETE :
675 TRANSFER_STATUS_ERROR;
677 g_dbus_emit_property_changed(connection, transfer->path,
678 TRANSFER_INTERFACE, "Status");
681 static void emit_transfer_progress(struct obex_transfer *transfer,
682 uint32_t total, uint32_t transferred)
684 if (transfer->path == NULL)
687 g_dbus_emit_property_changed(connection, transfer->path,
688 TRANSFER_INTERFACE, "Transferred");
691 static void transfer_free(struct obex_transfer *transfer)
693 g_free(transfer->path);
697 struct obex_transfer *manager_register_transfer(struct obex_session *os)
699 struct obex_transfer *transfer;
700 static unsigned int id = 0;
702 transfer = g_new0(struct obex_transfer, 1);
703 transfer->path = g_strdup_printf("%s/session%u/transfer%u",
704 SESSION_BASE_PATH, os->id, id++);
705 transfer->session = os;
707 if (!g_dbus_register_interface(connection, transfer->path,
709 transfer_methods, NULL,
710 transfer_properties, transfer, NULL)) {
711 error("Cannot register Transfer interface.");
712 transfer_free(transfer);
719 void manager_unregister_transfer(struct obex_transfer *transfer)
721 struct obex_session *os;
723 if (transfer == NULL)
726 os = transfer->session;
728 if (transfer->status == TRANSFER_STATUS_ACTIVE)
729 emit_transfer_completed(transfer, os->offset == os->size);
731 g_dbus_unregister_interface(connection, transfer->path,
734 transfer_free(transfer);
737 static void agent_cancel(void)
744 msg = dbus_message_new_method_call(agent->bus_name, agent->path,
745 AGENT_INTERFACE, "Cancel");
747 g_dbus_send_message(connection, msg);
750 static void agent_reply(DBusPendingCall *call, void *user_data)
752 DBusMessage *reply = dbus_pending_call_steal_reply(call);
755 gboolean *got_reply = user_data;
759 /* Received a reply after the agent exited */
763 agent->auth_pending = FALSE;
765 dbus_error_init(&derr);
766 if (dbus_set_error_from_message(&derr, reply)) {
767 error("Agent replied with an error: %s, %s",
768 derr.name, derr.message);
770 if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY))
773 dbus_error_free(&derr);
774 dbus_message_unref(reply);
778 if (dbus_message_get_args(reply, NULL,
779 DBUS_TYPE_STRING, &name,
780 DBUS_TYPE_INVALID)) {
781 /* Splits folder and name */
782 const char *slash = strrchr(name, '/');
783 DBG("Agent replied with %s", name);
785 agent->new_name = g_strdup(name);
786 agent->new_folder = NULL;
788 agent->new_name = g_strdup(slash + 1);
789 agent->new_folder = g_strndup(name, slash - name);
793 dbus_message_unref(reply);
796 static gboolean auth_error(GIOChannel *io, GIOCondition cond, void *user_data)
798 agent->auth_pending = FALSE;
803 int manager_request_authorization(struct obex_transfer *transfer,
804 char **new_folder, char **new_name)
806 struct obex_session *os = transfer->session;
808 DBusPendingCall *call;
815 if (agent->auth_pending)
818 if (!new_folder || !new_name)
821 msg = dbus_message_new_method_call(agent->bus_name, agent->path,
825 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &transfer->path,
828 if (!g_dbus_send_message_with_reply(connection, msg, &call, TIMEOUT)) {
829 dbus_message_unref(msg);
833 dbus_message_unref(msg);
835 agent->auth_pending = TRUE;
838 /* Catches errors before authorization response comes */
839 watch = g_io_add_watch_full(os->io, G_PRIORITY_DEFAULT,
840 G_IO_HUP | G_IO_ERR | G_IO_NVAL,
841 auth_error, NULL, NULL);
843 dbus_pending_call_set_notify(call, agent_reply, &got_reply, NULL);
845 /* Workaround: process events while agent doesn't reply */
846 while (agent && agent->auth_pending)
847 g_main_context_iteration(NULL, TRUE);
849 g_source_remove(watch);
852 dbus_pending_call_cancel(call);
856 dbus_pending_call_unref(call);
858 if (!agent || !agent->new_name)
861 *new_folder = agent->new_folder;
862 *new_name = agent->new_name;
863 agent->new_folder = NULL;
864 agent->new_name = NULL;
869 static DBusMessage *session_get_capabilities(DBusConnection *connection,
870 DBusMessage *message, void *user_data)
872 return not_supported(message);
875 static const GDBusMethodTable session_methods[] = {
876 { GDBUS_ASYNC_METHOD("GetCapabilities",
877 NULL, GDBUS_ARGS({ "capabilities", "s" }),
878 session_get_capabilities) },
882 void manager_register_session(struct obex_session *os)
886 path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, os->id);
888 if (!g_dbus_register_interface(connection, path,
890 session_methods, NULL,
891 session_properties, os, NULL))
892 error("Cannot register Session interface.");
897 void manager_unregister_session(struct obex_session *os)
901 path = g_strdup_printf("%s/session%u", SESSION_BASE_PATH, os->id);
903 g_dbus_unregister_interface(connection, path, SESSION_INTERFACE);
908 void manager_emit_transfer_progress(struct obex_transfer *transfer)
910 emit_transfer_progress(transfer, transfer->session->size,
911 transfer->session->offset);
914 void manager_emit_transfer_completed(struct obex_transfer *transfer)
916 struct obex_session *session;
918 if (transfer == NULL)
921 session = transfer->session;
923 if (session == NULL || session->object == NULL)
926 emit_transfer_completed(transfer, !session->aborted);
929 DBusConnection *manager_dbus_get_connection(void)
931 if (connection == NULL)
934 return dbus_connection_ref(connection);