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
33 #include <sys/socket.h>
40 #include "obex-priv.h"
47 #define OPENOBEX_MANAGER_PATH "/"
48 #define OPENOBEX_MANAGER_INTERFACE OPENOBEX_SERVICE ".Manager"
49 #define ERROR_INTERFACE OPENOBEX_SERVICE ".Error"
50 #define TRANSFER_INTERFACE OPENOBEX_SERVICE ".Transfer"
51 #define SESSION_INTERFACE OPENOBEX_SERVICE ".Session"
53 #define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */
58 gboolean auth_pending;
61 unsigned int watch_id;
64 static struct agent *agent = NULL;
66 static DBusConnection *connection = NULL;
68 static void agent_free(struct agent *agent)
73 g_free(agent->new_folder);
74 g_free(agent->new_name);
75 g_free(agent->bus_name);
80 static inline DBusMessage *invalid_args(DBusMessage *msg)
82 return g_dbus_create_error(msg,
83 ERROR_INTERFACE ".InvalidArguments",
84 "Invalid arguments in method call");
87 static inline DBusMessage *agent_already_exists(DBusMessage *msg)
89 return g_dbus_create_error(msg,
90 ERROR_INTERFACE ".AlreadyExists",
91 "Agent already exists");
94 static inline DBusMessage *agent_does_not_exist(DBusMessage *msg)
96 return g_dbus_create_error(msg,
97 ERROR_INTERFACE ".DoesNotExist",
98 "Agent does not exist");
101 static inline DBusMessage *not_authorized(DBusMessage *msg)
103 return g_dbus_create_error(msg,
104 ERROR_INTERFACE ".NotAuthorized",
108 static void dbus_message_iter_append_variant(DBusMessageIter *iter,
111 DBusMessageIter value;
112 DBusMessageIter array;
116 case DBUS_TYPE_STRING:
117 sig = DBUS_TYPE_STRING_AS_STRING;
120 sig = DBUS_TYPE_BYTE_AS_STRING;
122 case DBUS_TYPE_INT16:
123 sig = DBUS_TYPE_INT16_AS_STRING;
125 case DBUS_TYPE_UINT16:
126 sig = DBUS_TYPE_UINT16_AS_STRING;
128 case DBUS_TYPE_INT32:
129 sig = DBUS_TYPE_INT32_AS_STRING;
131 case DBUS_TYPE_UINT32:
132 sig = DBUS_TYPE_UINT32_AS_STRING;
135 case DBUS_TYPE_UINT64:
136 sig = DBUS_TYPE_UINT64_AS_STRING;
139 case DBUS_TYPE_BOOLEAN:
140 sig = DBUS_TYPE_BOOLEAN_AS_STRING;
142 case DBUS_TYPE_ARRAY:
143 sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
145 case DBUS_TYPE_OBJECT_PATH:
146 sig = DBUS_TYPE_OBJECT_PATH_AS_STRING;
149 error("Could not append variant with type %d", type);
153 dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
155 if (type == DBUS_TYPE_ARRAY) {
157 const char ***str_array = val;
159 dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
160 DBUS_TYPE_STRING_AS_STRING, &array);
162 for (i = 0; (*str_array)[i]; i++)
163 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
166 dbus_message_iter_close_container(&value, &array);
168 dbus_message_iter_append_basic(&value, type, val);
170 dbus_message_iter_close_container(iter, &value);
173 static void dbus_message_iter_append_dict_entry(DBusMessageIter *dict,
174 const char *key, int type, void *val)
176 DBusMessageIter entry;
178 dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
181 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
183 dbus_message_iter_append_variant(&entry, type, val);
185 dbus_message_iter_close_container(dict, &entry);
188 static void agent_disconnected(DBusConnection *conn, void *user_data)
195 static DBusMessage *register_agent(DBusConnection *conn,
196 DBusMessage *msg, void *data)
198 const char *path, *sender;
201 return agent_already_exists(msg);
203 if (!dbus_message_get_args(msg, NULL,
204 DBUS_TYPE_OBJECT_PATH, &path,
206 return invalid_args(msg);
208 sender = dbus_message_get_sender(msg);
209 agent = g_new0(struct agent, 1);
210 agent->bus_name = g_strdup(sender);
211 agent->path = g_strdup(path);
213 agent->watch_id = g_dbus_add_disconnect_watch(conn, sender,
214 agent_disconnected, NULL, NULL);
216 DBG("Agent registered");
218 return dbus_message_new_method_return(msg);
221 static DBusMessage *unregister_agent(DBusConnection *conn,
222 DBusMessage *msg, void *data)
224 const char *path, *sender;
227 return agent_does_not_exist(msg);
229 if (!dbus_message_get_args(msg, NULL,
230 DBUS_TYPE_OBJECT_PATH, &path,
232 return invalid_args(msg);
234 if (strcmp(agent->path, path) != 0)
235 return agent_does_not_exist(msg);
237 sender = dbus_message_get_sender(msg);
238 if (strcmp(agent->bus_name, sender) != 0)
239 return not_authorized(msg);
241 g_dbus_remove_watch(conn, agent->watch_id);
246 DBG("Agent unregistered");
248 return dbus_message_new_method_return(msg);
252 static DBusMessage *set_root(DBusConnection *conn, DBusMessage *msg,
253 const char *root, void *data)
255 DBG("new_root: %s", root);
257 /* Change the option root path (using in filesystem) */
258 obex_option_set_root_folder(root);
260 return dbus_message_new_method_return(msg);
263 static DBusMessage *set_property(DBusConnection *conn,
264 DBusMessage *msg, void *data)
266 DBusMessageIter iter;
268 const char *property;
270 if (!dbus_message_iter_init(msg, &iter))
271 return invalid_args(msg);
273 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
274 return invalid_args(msg);
276 dbus_message_iter_get_basic(&iter, &property);
277 dbus_message_iter_next(&iter);
279 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
280 return invalid_args(msg);
281 dbus_message_iter_recurse(&iter, &sub);
283 if (g_str_equal("Root", property)) {
286 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
287 return invalid_args(msg);
288 dbus_message_iter_get_basic(&sub, &root);
290 return set_root(conn, msg, root, data);
293 return invalid_args(msg);
298 static char *target2str(const uint8_t *t)
303 return g_strdup_printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-"
304 "%02X%02X-%02X%02X%02X%02X%02X%02X",
305 t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7],
306 t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]);
309 static DBusMessage *get_properties(DBusConnection *conn,
310 DBusMessage *msg, void *data)
312 struct obex_session *os = data;
314 DBusMessageIter iter;
315 DBusMessageIter dict;
319 reply = dbus_message_new_method_return(msg);
323 dbus_message_iter_init_append(reply, &iter);
324 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
325 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
326 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
327 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
330 uuid = target2str(os->service->target);
331 dbus_message_iter_append_dict_entry(&dict, "Target",
332 DBUS_TYPE_STRING, &uuid);
336 root = obex_option_root_folder();
337 dbus_message_iter_append_dict_entry(&dict, "Root",
338 DBUS_TYPE_STRING, &root);
340 /* FIXME: Added Remote Address or USB */
342 dbus_message_iter_close_container(&iter, &dict);
347 static DBusMessage *transfer_cancel(DBusConnection *connection,
348 DBusMessage *msg, void *user_data)
350 struct obex_session *os = user_data;
354 return invalid_args(msg);
356 sender = dbus_message_get_sender(msg);
357 if (strcmp(agent->bus_name, sender) != 0)
358 return not_authorized(msg);
362 return dbus_message_new_method_return(msg);
366 static char *get_obex_operation(struct obex_session *os)
368 char *operation = NULL;
372 if (os->size != OBJECT_SIZE_DELETE)
373 operation = g_strdup("PUT");
376 operation = g_strdup("GET");
385 static DBusMessage *transfer_get_properties(DBusConnection *connection,
386 DBusMessage *message, void *user_data)
388 struct obex_session *os = user_data;
390 DBusMessageIter iter, dict;
394 return invalid_args(message);
396 reply = dbus_message_new_method_return(message);
400 dbus_message_iter_init_append(reply, &iter);
402 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
403 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
404 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
405 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
408 dbus_message_iter_append_dict_entry(&dict, "Filename",
409 DBUS_TYPE_STRING, &os->name);
411 dbus_message_iter_append_dict_entry(&dict, "Size",
412 DBUS_TYPE_UINT64, &os->size);
414 operation = get_obex_operation(os);
416 dbus_message_iter_append_dict_entry(&dict, "Operation",
417 DBUS_TYPE_STRING, &operation);
421 dbus_message_iter_close_container(&iter, &dict);
427 static GDBusMethodTable manager_methods[] = {
428 { "RegisterAgent", "o", "", register_agent },
429 { "UnregisterAgent", "o", "", unregister_agent },
431 { "SetProperty", "sv", "", set_property },
436 static GDBusSignalTable manager_signals[] = {
437 { "TransferStarted", "o" },
438 { "TransferCompleted", "ob" },
439 { "SessionCreated", "o" },
440 { "SessionRemoved", "o" },
444 static GDBusMethodTable transfer_methods[] = {
445 { "Cancel", "", "", transfer_cancel },
447 { "GetProperties", "", "{sv}", transfer_get_properties },
452 static GDBusSignalTable transfer_signals[] = {
453 { "Progress", "ii" },
457 static GDBusMethodTable session_methods[] = {
458 { "GetProperties", "", "{sv}", get_properties },
462 gboolean manager_init(void)
468 dbus_error_init(&err);
469 connection = g_dbus_setup_bus(DBUS_BUS_SESSION, OPENOBEX_SERVICE,
471 if (connection == NULL) {
472 if (dbus_error_is_set(&err) == TRUE) {
473 fprintf(stderr, "%s\n", err.message);
474 dbus_error_free(&err);
476 fprintf(stderr, "Can't register with session bus\n");
480 return g_dbus_register_interface(connection, OPENOBEX_MANAGER_PATH,
481 OPENOBEX_MANAGER_INTERFACE,
482 manager_methods, manager_signals, NULL,
486 void manager_cleanup(void)
490 g_dbus_unregister_interface(connection, OPENOBEX_MANAGER_PATH,
491 OPENOBEX_MANAGER_INTERFACE);
493 /* FIXME: Release agent? */
498 dbus_connection_unref(connection);
501 void manager_emit_transfer_started(struct obex_session *os)
503 char *path = g_strdup_printf("/transfer%u", os->id);
505 g_dbus_emit_signal(connection, OPENOBEX_MANAGER_PATH,
506 OPENOBEX_MANAGER_INTERFACE, "TransferStarted",
507 DBUS_TYPE_OBJECT_PATH, &path,
513 static void emit_transfer_completed(struct obex_session *os, gboolean success)
515 char *path = g_strdup_printf("/transfer%u", os->id);
517 g_dbus_emit_signal(connection, OPENOBEX_MANAGER_PATH,
518 OPENOBEX_MANAGER_INTERFACE, "TransferCompleted",
519 DBUS_TYPE_OBJECT_PATH, &path,
520 DBUS_TYPE_BOOLEAN, &success,
526 static void emit_transfer_progress(struct obex_session *os, uint32_t total,
529 char *path = g_strdup_printf("/transfer%u", os->id);
531 g_dbus_emit_signal(connection, path,
532 TRANSFER_INTERFACE, "Progress",
533 DBUS_TYPE_INT32, &total,
534 DBUS_TYPE_INT32, &transfered,
540 void manager_register_transfer(struct obex_session *os)
542 char *path = g_strdup_printf("/transfer%u", os->id);
544 if (!g_dbus_register_interface(connection, path,
546 transfer_methods, transfer_signals,
548 error("Cannot register Transfer interface.");
556 void manager_unregister_transfer(struct obex_session *os)
558 char *path = g_strdup_printf("/transfer%u", os->id);
560 /* Got an error during a transfer. */
562 emit_transfer_completed(os, os->offset == os->size);
564 g_dbus_unregister_interface(connection, path,
570 static void agent_cancel(void)
577 msg = dbus_message_new_method_call(agent->bus_name, agent->path,
578 "org.openobex.Agent", "Cancel");
580 g_dbus_send_message(connection, msg);
583 static void agent_reply(DBusPendingCall *call, void *user_data)
585 DBusMessage *reply = dbus_pending_call_steal_reply(call);
588 gboolean *got_reply = user_data;
592 /* Received a reply after the agent exited */
596 agent->auth_pending = FALSE;
598 dbus_error_init(&derr);
599 if (dbus_set_error_from_message(&derr, reply)) {
600 error("Agent replied with an error: %s, %s",
601 derr.name, derr.message);
603 if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY))
606 dbus_error_free(&derr);
607 dbus_message_unref(reply);
611 if (dbus_message_get_args(reply, NULL,
612 DBUS_TYPE_STRING, &name,
613 DBUS_TYPE_INVALID)) {
614 /* Splits folder and name */
615 const char *slash = strrchr(name, '/');
616 DBG("Agent replied with %s", name);
618 agent->new_name = g_strdup(name);
619 agent->new_folder = NULL;
621 agent->new_name = g_strdup(slash + 1);
622 agent->new_folder = g_strndup(name, slash - name);
626 dbus_message_unref(reply);
629 static gboolean auth_error(GIOChannel *io, GIOCondition cond, void *user_data)
631 agent->auth_pending = FALSE;
636 int manager_request_authorization(struct obex_session *os, int32_t time,
637 char **new_folder, char **new_name)
640 DBusPendingCall *call;
641 const char *filename = os->name ? os->name : "";
642 const char *type = os->type ? os->type : "";
643 char *path, *address;
651 if (agent->auth_pending)
654 if (!new_folder || !new_name)
657 err = obex_getpeername(os, &address);
661 path = g_strdup_printf("/transfer%u", os->id);
663 msg = dbus_message_new_method_call(agent->bus_name, agent->path,
664 "org.openobex.Agent", "Authorize");
666 dbus_message_append_args(msg,
667 DBUS_TYPE_OBJECT_PATH, &path,
668 DBUS_TYPE_STRING, &address,
669 DBUS_TYPE_STRING, &filename,
670 DBUS_TYPE_STRING, &type,
671 DBUS_TYPE_INT32, &os->size,
672 DBUS_TYPE_INT32, &time,
678 if (!dbus_connection_send_with_reply(connection,
679 msg, &call, TIMEOUT)) {
680 dbus_message_unref(msg);
684 dbus_message_unref(msg);
686 agent->auth_pending = TRUE;
689 /* Catches errors before authorization response comes */
690 watch = g_io_add_watch_full(os->io, G_PRIORITY_DEFAULT,
691 G_IO_HUP | G_IO_ERR | G_IO_NVAL,
692 auth_error, NULL, NULL);
694 dbus_pending_call_set_notify(call, agent_reply, &got_reply, NULL);
696 /* Workaround: process events while agent doesn't reply */
697 while (agent && agent->auth_pending)
698 g_main_context_iteration(NULL, TRUE);
700 g_source_remove(watch);
703 dbus_pending_call_cancel(call);
707 dbus_pending_call_unref(call);
709 if (!agent || !agent->new_name)
712 *new_folder = agent->new_folder;
713 *new_name = agent->new_name;
714 agent->new_folder = NULL;
715 agent->new_name = NULL;
720 void manager_register_session(struct obex_session *os)
722 char *path = g_strdup_printf("/session%u", GPOINTER_TO_UINT(os));
724 if (!g_dbus_register_interface(connection, path,
726 session_methods, NULL,
728 error("Cannot register Session interface.");
733 g_dbus_emit_signal(connection, OPENOBEX_MANAGER_PATH,
734 OPENOBEX_MANAGER_INTERFACE, "SessionCreated",
735 DBUS_TYPE_OBJECT_PATH, &path,
741 void manager_unregister_session(struct obex_session *os)
743 char *path = g_strdup_printf("/session%u", GPOINTER_TO_UINT(os));
745 g_dbus_emit_signal(connection, OPENOBEX_MANAGER_PATH,
746 OPENOBEX_MANAGER_INTERFACE, "SessionRemoved",
747 DBUS_TYPE_OBJECT_PATH, &path,
750 g_dbus_unregister_interface(connection, path,
756 void manager_emit_transfer_progress(struct obex_session *os)
758 emit_transfer_progress(os, os->size, os->offset);
761 void manager_emit_transfer_completed(struct obex_session *os)
764 emit_transfer_completed(os, !os->aborted);
767 DBusConnection *manager_dbus_get_connection(void)
769 if (connection == NULL)
772 return dbus_connection_ref(connection);