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 OBEX_MANAGER_PATH "/"
48 #define OBEX_MANAGER_INTERFACE OBEXD_SERVICE ".Manager"
49 #define ERROR_INTERFACE OBEXD_SERVICE ".Error"
50 #define TRANSFER_INTERFACE OBEXD_SERVICE ".Transfer"
51 #define SESSION_INTERFACE OBEXD_SERVICE ".Session"
52 #define AGENT_INTERFACE OBEXD_SERVICE ".Agent"
54 #define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */
59 gboolean auth_pending;
62 unsigned int watch_id;
65 static struct agent *agent = NULL;
67 static DBusConnection *connection = NULL;
69 static void agent_free(struct agent *agent)
74 g_free(agent->new_folder);
75 g_free(agent->new_name);
76 g_free(agent->bus_name);
81 static inline DBusMessage *invalid_args(DBusMessage *msg)
83 return g_dbus_create_error(msg,
84 ERROR_INTERFACE ".InvalidArguments",
85 "Invalid arguments in method call");
88 static inline DBusMessage *agent_already_exists(DBusMessage *msg)
90 return g_dbus_create_error(msg,
91 ERROR_INTERFACE ".AlreadyExists",
92 "Agent already exists");
95 static inline DBusMessage *agent_does_not_exist(DBusMessage *msg)
97 return g_dbus_create_error(msg,
98 ERROR_INTERFACE ".DoesNotExist",
99 "Agent does not exist");
102 static inline DBusMessage *not_authorized(DBusMessage *msg)
104 return g_dbus_create_error(msg,
105 ERROR_INTERFACE ".NotAuthorized",
109 static void dbus_message_iter_append_variant(DBusMessageIter *iter,
112 DBusMessageIter value;
113 DBusMessageIter array;
117 case DBUS_TYPE_STRING:
118 sig = DBUS_TYPE_STRING_AS_STRING;
121 sig = DBUS_TYPE_BYTE_AS_STRING;
123 case DBUS_TYPE_INT16:
124 sig = DBUS_TYPE_INT16_AS_STRING;
126 case DBUS_TYPE_UINT16:
127 sig = DBUS_TYPE_UINT16_AS_STRING;
129 case DBUS_TYPE_INT32:
130 sig = DBUS_TYPE_INT32_AS_STRING;
132 case DBUS_TYPE_UINT32:
133 sig = DBUS_TYPE_UINT32_AS_STRING;
135 case DBUS_TYPE_BOOLEAN:
136 sig = DBUS_TYPE_BOOLEAN_AS_STRING;
138 case DBUS_TYPE_ARRAY:
139 sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
141 case DBUS_TYPE_OBJECT_PATH:
142 sig = DBUS_TYPE_OBJECT_PATH_AS_STRING;
145 error("Could not append variant with type %d", type);
149 dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
151 if (type == DBUS_TYPE_ARRAY) {
153 const char ***str_array = val;
155 dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
156 DBUS_TYPE_STRING_AS_STRING, &array);
158 for (i = 0; (*str_array)[i]; i++)
159 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
162 dbus_message_iter_close_container(&value, &array);
164 dbus_message_iter_append_basic(&value, type, val);
166 dbus_message_iter_close_container(iter, &value);
169 static void dbus_message_iter_append_dict_entry(DBusMessageIter *dict,
170 const char *key, int type, void *val)
172 DBusMessageIter entry;
174 dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
177 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
179 dbus_message_iter_append_variant(&entry, type, val);
181 dbus_message_iter_close_container(dict, &entry);
184 static void agent_disconnected(DBusConnection *conn, void *user_data)
191 static DBusMessage *register_agent(DBusConnection *conn,
192 DBusMessage *msg, void *data)
194 const char *path, *sender;
197 return agent_already_exists(msg);
199 if (!dbus_message_get_args(msg, NULL,
200 DBUS_TYPE_OBJECT_PATH, &path,
202 return invalid_args(msg);
204 sender = dbus_message_get_sender(msg);
205 agent = g_new0(struct agent, 1);
206 agent->bus_name = g_strdup(sender);
207 agent->path = g_strdup(path);
209 agent->watch_id = g_dbus_add_disconnect_watch(conn, sender,
210 agent_disconnected, NULL, NULL);
212 DBG("Agent registered");
214 return dbus_message_new_method_return(msg);
217 static DBusMessage *unregister_agent(DBusConnection *conn,
218 DBusMessage *msg, void *data)
220 const char *path, *sender;
223 return agent_does_not_exist(msg);
225 if (!dbus_message_get_args(msg, NULL,
226 DBUS_TYPE_OBJECT_PATH, &path,
228 return invalid_args(msg);
230 if (strcmp(agent->path, path) != 0)
231 return agent_does_not_exist(msg);
233 sender = dbus_message_get_sender(msg);
234 if (strcmp(agent->bus_name, sender) != 0)
235 return not_authorized(msg);
237 g_dbus_remove_watch(conn, agent->watch_id);
242 DBG("Agent unregistered");
244 return dbus_message_new_method_return(msg);
247 static char *target2str(const uint8_t *t)
252 return g_strdup_printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-"
253 "%02X%02X-%02X%02X%02X%02X%02X%02X",
254 t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7],
255 t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]);
258 static DBusMessage *get_properties(DBusConnection *conn,
259 DBusMessage *msg, void *data)
261 struct obex_session *os = data;
263 DBusMessageIter iter;
264 DBusMessageIter dict;
268 reply = dbus_message_new_method_return(msg);
272 dbus_message_iter_init_append(reply, &iter);
273 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
274 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
275 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
276 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
279 uuid = target2str(os->service->target);
280 dbus_message_iter_append_dict_entry(&dict, "Target",
281 DBUS_TYPE_STRING, &uuid);
285 root = obex_option_root_folder();
286 dbus_message_iter_append_dict_entry(&dict, "Root",
287 DBUS_TYPE_STRING, &root);
289 /* FIXME: Added Remote Address or USB */
291 dbus_message_iter_close_container(&iter, &dict);
296 static DBusMessage *transfer_cancel(DBusConnection *connection,
297 DBusMessage *msg, void *user_data)
299 struct obex_session *os = user_data;
303 return invalid_args(msg);
305 sender = dbus_message_get_sender(msg);
306 if (strcmp(agent->bus_name, sender) != 0)
307 return not_authorized(msg);
311 return dbus_message_new_method_return(msg);
314 static const GDBusMethodTable manager_methods[] = {
315 { GDBUS_METHOD("RegisterAgent",
316 GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) },
317 { GDBUS_METHOD("UnregisterAgent",
318 GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) },
322 static const GDBusSignalTable manager_signals[] = {
323 { GDBUS_SIGNAL("TransferStarted", GDBUS_ARGS({ "transfer", "o"})) },
324 { GDBUS_SIGNAL("TransferCompleted", GDBUS_ARGS({ "transfer", "o" },
325 { "success", "b" })) },
326 { GDBUS_SIGNAL("SessionCreated", GDBUS_ARGS({ "session", "o" })) },
327 { GDBUS_SIGNAL("SessionRemoved", GDBUS_ARGS({ "session", "o" })) },
331 static const GDBusMethodTable transfer_methods[] = {
332 { GDBUS_METHOD("Cancel", NULL, NULL, transfer_cancel) },
336 static const GDBusSignalTable transfer_signals[] = {
337 { GDBUS_SIGNAL("Progress", GDBUS_ARGS({ "total", "i" },
338 { "transferred", "i" })) },
342 static const GDBusMethodTable session_methods[] = {
343 { GDBUS_METHOD("GetProperties",
344 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
349 gboolean manager_init(void)
355 dbus_error_init(&err);
357 connection = g_dbus_setup_bus(DBUS_BUS_SESSION, OBEXD_SERVICE, &err);
358 if (connection == NULL) {
359 if (dbus_error_is_set(&err) == TRUE) {
360 fprintf(stderr, "%s\n", err.message);
361 dbus_error_free(&err);
363 fprintf(stderr, "Can't register with session bus\n");
367 return g_dbus_register_interface(connection, OBEX_MANAGER_PATH,
368 OBEX_MANAGER_INTERFACE,
369 manager_methods, manager_signals, NULL,
373 void manager_cleanup(void)
377 g_dbus_unregister_interface(connection, OBEX_MANAGER_PATH,
378 OBEX_MANAGER_INTERFACE);
380 /* FIXME: Release agent? */
385 dbus_connection_unref(connection);
388 void manager_emit_transfer_started(struct obex_session *os)
390 char *path = g_strdup_printf("/transfer%u", os->id);
392 g_dbus_emit_signal(connection, OBEX_MANAGER_PATH,
393 OBEX_MANAGER_INTERFACE, "TransferStarted",
394 DBUS_TYPE_OBJECT_PATH, &path,
400 static void emit_transfer_completed(struct obex_session *os, gboolean success)
402 char *path = g_strdup_printf("/transfer%u", os->id);
404 g_dbus_emit_signal(connection, OBEX_MANAGER_PATH,
405 OBEX_MANAGER_INTERFACE, "TransferCompleted",
406 DBUS_TYPE_OBJECT_PATH, &path,
407 DBUS_TYPE_BOOLEAN, &success,
413 static void emit_transfer_progress(struct obex_session *os, uint32_t total,
414 uint32_t transferred)
416 char *path = g_strdup_printf("/transfer%u", os->id);
418 g_dbus_emit_signal(connection, path,
419 TRANSFER_INTERFACE, "Progress",
420 DBUS_TYPE_INT32, &total,
421 DBUS_TYPE_INT32, &transferred,
427 void manager_register_transfer(struct obex_session *os)
429 char *path = g_strdup_printf("/transfer%u", os->id);
431 if (!g_dbus_register_interface(connection, path,
433 transfer_methods, transfer_signals,
435 error("Cannot register Transfer interface.");
443 void manager_unregister_transfer(struct obex_session *os)
445 char *path = g_strdup_printf("/transfer%u", os->id);
447 /* Got an error during a transfer. */
449 emit_transfer_completed(os, os->offset == os->size);
451 g_dbus_unregister_interface(connection, path,
457 static void agent_cancel(void)
464 msg = dbus_message_new_method_call(agent->bus_name, agent->path,
465 AGENT_INTERFACE, "Cancel");
467 g_dbus_send_message(connection, msg);
470 static void agent_reply(DBusPendingCall *call, void *user_data)
472 DBusMessage *reply = dbus_pending_call_steal_reply(call);
475 gboolean *got_reply = user_data;
479 /* Received a reply after the agent exited */
483 agent->auth_pending = FALSE;
485 dbus_error_init(&derr);
486 if (dbus_set_error_from_message(&derr, reply)) {
487 error("Agent replied with an error: %s, %s",
488 derr.name, derr.message);
490 if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY))
493 dbus_error_free(&derr);
494 dbus_message_unref(reply);
498 if (dbus_message_get_args(reply, NULL,
499 DBUS_TYPE_STRING, &name,
500 DBUS_TYPE_INVALID)) {
501 /* Splits folder and name */
502 const char *slash = strrchr(name, '/');
503 DBG("Agent replied with %s", name);
505 agent->new_name = g_strdup(name);
506 agent->new_folder = NULL;
508 agent->new_name = g_strdup(slash + 1);
509 agent->new_folder = g_strndup(name, slash - name);
513 dbus_message_unref(reply);
516 static gboolean auth_error(GIOChannel *io, GIOCondition cond, void *user_data)
518 agent->auth_pending = FALSE;
523 int manager_request_authorization(struct obex_session *os, int32_t time,
524 char **new_folder, char **new_name)
527 DBusPendingCall *call;
528 const char *filename = os->name ? os->name : "";
529 const char *type = os->type ? os->type : "";
530 char *path, *address;
538 if (agent->auth_pending)
541 if (!new_folder || !new_name)
544 err = obex_getpeername(os, &address);
548 path = g_strdup_printf("/transfer%u", os->id);
550 msg = dbus_message_new_method_call(agent->bus_name, agent->path,
551 AGENT_INTERFACE, "Authorize");
553 dbus_message_append_args(msg,
554 DBUS_TYPE_OBJECT_PATH, &path,
555 DBUS_TYPE_STRING, &address,
556 DBUS_TYPE_STRING, &filename,
557 DBUS_TYPE_STRING, &type,
558 DBUS_TYPE_INT32, &os->size,
559 DBUS_TYPE_INT32, &time,
565 if (!dbus_connection_send_with_reply(connection,
566 msg, &call, TIMEOUT)) {
567 dbus_message_unref(msg);
571 dbus_message_unref(msg);
573 agent->auth_pending = TRUE;
576 /* Catches errors before authorization response comes */
577 watch = g_io_add_watch_full(os->io, G_PRIORITY_DEFAULT,
578 G_IO_HUP | G_IO_ERR | G_IO_NVAL,
579 auth_error, NULL, NULL);
581 dbus_pending_call_set_notify(call, agent_reply, &got_reply, NULL);
583 /* Workaround: process events while agent doesn't reply */
584 while (agent && agent->auth_pending)
585 g_main_context_iteration(NULL, TRUE);
587 g_source_remove(watch);
590 dbus_pending_call_cancel(call);
594 dbus_pending_call_unref(call);
596 if (!agent || !agent->new_name)
599 *new_folder = agent->new_folder;
600 *new_name = agent->new_name;
601 agent->new_folder = NULL;
602 agent->new_name = NULL;
607 void manager_register_session(struct obex_session *os)
609 char *path = g_strdup_printf("/session%u", GPOINTER_TO_UINT(os));
611 if (!g_dbus_register_interface(connection, path,
613 session_methods, NULL,
615 error("Cannot register Session interface.");
619 g_dbus_emit_signal(connection, OBEX_MANAGER_PATH,
620 OBEX_MANAGER_INTERFACE, "SessionCreated",
621 DBUS_TYPE_OBJECT_PATH, &path,
628 void manager_unregister_session(struct obex_session *os)
630 char *path = g_strdup_printf("/session%u", GPOINTER_TO_UINT(os));
632 g_dbus_emit_signal(connection, OBEX_MANAGER_PATH,
633 OBEX_MANAGER_INTERFACE, "SessionRemoved",
634 DBUS_TYPE_OBJECT_PATH, &path,
637 g_dbus_unregister_interface(connection, path,
643 void manager_emit_transfer_progress(struct obex_session *os)
645 emit_transfer_progress(os, os->size, os->offset);
648 void manager_emit_transfer_completed(struct obex_session *os)
651 emit_transfer_completed(os, !os->aborted);
654 DBusConnection *manager_dbus_get_connection(void)
656 if (connection == NULL)
659 return dbus_connection_ref(connection);