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
47 #define CLIENT_SERVICE "org.openobex.client"
49 #define CLIENT_INTERFACE "org.openobex.Client"
50 #define CLIENT_PATH "/"
53 DBusConnection *connection;
61 static GSList *sessions = NULL;
63 static void shutdown_session(struct obc_session *session)
65 sessions = g_slist_remove(sessions, session);
66 obc_session_shutdown(session);
67 obc_session_unref(session);
70 static void unregister_session(void *data)
72 struct obc_session *session = data;
74 if (g_slist_find(sessions, session) == NULL)
77 sessions = g_slist_remove(sessions, session);
78 obc_session_unref(session);
81 static void create_callback(struct obc_session *session, GError *err,
84 struct send_data *data = user_data;
88 DBusMessage *error = g_dbus_create_error(data->message,
89 "org.openobex.Error.Failed",
91 g_dbus_send_message(data->connection, error);
92 shutdown_session(session);
96 if (obc_session_get_target(session) != NULL) {
99 path = obc_session_register(session, unregister_session);
101 g_dbus_send_reply(data->connection, data->message,
102 DBUS_TYPE_OBJECT_PATH, &path,
107 g_dbus_send_reply(data->connection, data->message, DBUS_TYPE_INVALID);
109 obc_session_set_agent(session, data->sender, data->agent);
111 for (i = 0; i < data->files->len; i++) {
112 const gchar *filename = g_ptr_array_index(data->files, i);
113 gchar *basename = g_path_get_basename(filename);
115 if (obc_session_send(session, filename, basename) < 0) {
123 /* No need to keep a reference for SendFiles */
124 sessions = g_slist_remove(sessions, session);
125 obc_session_unref(session);
129 g_ptr_array_free(data->files, TRUE);
130 dbus_message_unref(data->message);
131 dbus_connection_unref(data->connection);
132 g_free(data->sender);
137 static int parse_device_dict(DBusMessageIter *iter,
138 const char **source, const char **dest, const char **target,
141 while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_DICT_ENTRY) {
142 DBusMessageIter entry, value;
145 dbus_message_iter_recurse(iter, &entry);
146 dbus_message_iter_get_basic(&entry, &key);
148 dbus_message_iter_next(&entry);
149 dbus_message_iter_recurse(&entry, &value);
151 switch (dbus_message_iter_get_arg_type(&value)) {
152 case DBUS_TYPE_STRING:
153 if (g_str_equal(key, "Source") == TRUE)
154 dbus_message_iter_get_basic(&value, source);
155 else if (g_str_equal(key, "Destination") == TRUE)
156 dbus_message_iter_get_basic(&value, dest);
157 else if (g_str_equal(key, "Target") == TRUE)
158 dbus_message_iter_get_basic(&value, target);
161 if (g_str_equal(key, "Channel") == TRUE)
162 dbus_message_iter_get_basic(&value, channel);
166 dbus_message_iter_next(iter);
172 static DBusMessage *send_files(DBusConnection *connection,
173 DBusMessage *message, void *user_data)
175 DBusMessageIter iter, array;
176 struct obc_session *session;
178 struct send_data *data;
179 const char *agent, *source = NULL, *dest = NULL, *target = NULL;
183 dbus_message_iter_init(message, &iter);
184 dbus_message_iter_recurse(&iter, &array);
186 parse_device_dict(&array, &source, &dest, &target, &channel);
188 return g_dbus_create_error(message,
189 "org.openobex.Error.InvalidArguments", NULL);
191 dbus_message_iter_next(&iter);
192 dbus_message_iter_recurse(&iter, &array);
194 files = g_ptr_array_new();
196 return g_dbus_create_error(message,
197 "org.openobex.Error.NoMemory", NULL);
199 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
202 dbus_message_iter_get_basic(&array, &value);
203 g_ptr_array_add(files, value);
205 dbus_message_iter_next(&array);
208 dbus_message_iter_next(&iter);
209 dbus_message_iter_get_basic(&iter, &agent);
211 if (files->len == 0) {
212 g_ptr_array_free(files, TRUE);
213 return g_dbus_create_error(message,
214 "org.openobex.Error.InvalidArguments", NULL);
217 sender = dbus_message_get_sender(message);
219 data = g_try_malloc0(sizeof(*data));
221 g_ptr_array_free(files, TRUE);
222 return g_dbus_create_error(message,
223 "org.openobex.Error.NoMemory", NULL);
226 data->connection = dbus_connection_ref(connection);
227 data->message = dbus_message_ref(message);
228 data->sender = g_strdup(sender);
229 data->agent = g_strdup(agent);
232 session = obc_session_create(source, dest, "OPP", channel, sender,
233 create_callback, data);
234 if (session != NULL) {
235 sessions = g_slist_append(sessions, session);
239 g_ptr_array_free(data->files, TRUE);
240 dbus_message_unref(data->message);
241 dbus_connection_unref(data->connection);
242 g_free(data->sender);
246 return g_dbus_create_error(message, "org.openobex.Error.Failed", NULL);
249 static void pull_complete_callback(struct obc_session *session,
250 GError *err, void *user_data)
252 struct send_data *data = user_data;
255 DBusMessage *error = g_dbus_create_error(data->message,
256 "org.openobex.Error.Failed",
258 g_dbus_send_message(data->connection, error);
262 g_dbus_send_reply(data->connection, data->message, DBUS_TYPE_INVALID);
265 shutdown_session(session);
266 dbus_message_unref(data->message);
267 dbus_connection_unref(data->connection);
268 g_free(data->filename);
269 g_free(data->sender);
273 static void pull_obc_session_callback(struct obc_session *session,
274 GError *err, void *user_data)
276 struct send_data *data = user_data;
279 DBusMessage *error = g_dbus_create_error(data->message,
280 "org.openobex.Error.Failed",
282 g_dbus_send_message(data->connection, error);
283 shutdown_session(session);
287 obc_session_pull(session, "text/x-vcard", data->filename,
288 pull_complete_callback, data);
293 dbus_message_unref(data->message);
294 dbus_connection_unref(data->connection);
295 g_free(data->filename);
296 g_free(data->sender);
300 static DBusMessage *pull_business_card(DBusConnection *connection,
301 DBusMessage *message, void *user_data)
303 DBusMessageIter iter, dict;
304 struct obc_session *session;
305 struct send_data *data;
306 const char *source = NULL, *dest = NULL, *target = NULL;
307 const char *name = NULL;
310 dbus_message_iter_init(message, &iter);
311 dbus_message_iter_recurse(&iter, &dict);
313 parse_device_dict(&dict, &source, &dest, &target, &channel);
315 return g_dbus_create_error(message,
316 "org.openobex.Error.InvalidArguments", NULL);
318 dbus_message_iter_next(&iter);
320 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
321 return g_dbus_create_error(message,
322 "org.openobex.Error.InvalidArguments", NULL);
324 dbus_message_iter_get_basic(&iter, &name);
326 data = g_try_malloc0(sizeof(*data));
328 return g_dbus_create_error(message,
329 "org.openobex.Error.NoMemory", NULL);
331 data->connection = dbus_connection_ref(connection);
332 data->message = dbus_message_ref(message);
333 data->sender = g_strdup(dbus_message_get_sender(message));
334 data->filename = g_strdup(name);
336 session = obc_session_create(source, dest, "OPP", channel, data->sender,
337 pull_obc_session_callback, data);
338 if (session != NULL) {
339 sessions = g_slist_append(sessions, session);
343 dbus_message_unref(data->message);
344 dbus_connection_unref(data->connection);
345 g_free(data->sender);
346 g_free(data->filename);
349 return g_dbus_create_error(message, "org.openobex.Error.Failed", NULL);
352 static DBusMessage *exchange_business_cards(DBusConnection *connection,
353 DBusMessage *message, void *user_data)
355 return g_dbus_create_error(message, "org.openobex.Error.Failed", NULL);
358 static struct obc_session *find_session(const char *path)
362 for (l = sessions; l; l = l->next) {
363 struct obc_session *session = l->data;
365 if (g_str_equal(obc_session_get_path(session), path) == TRUE)
372 static DBusMessage *create_session(DBusConnection *connection,
373 DBusMessage *message, void *user_data)
375 DBusMessageIter iter, dict;
376 struct obc_session *session;
377 struct send_data *data;
378 const char *source = NULL, *dest = NULL, *target = NULL;
381 dbus_message_iter_init(message, &iter);
382 dbus_message_iter_recurse(&iter, &dict);
384 parse_device_dict(&dict, &source, &dest, &target, &channel);
385 if (dest == NULL || target == NULL)
386 return g_dbus_create_error(message,
387 "org.openobex.Error.InvalidArguments", NULL);
389 data = g_try_malloc0(sizeof(*data));
391 return g_dbus_create_error(message,
392 "org.openobex.Error.NoMemory", NULL);
394 data->connection = dbus_connection_ref(connection);
395 data->message = dbus_message_ref(message);
396 data->sender = g_strdup(dbus_message_get_sender(message));
398 session = obc_session_create(source, dest, target, channel, data->sender,
399 create_callback, data);
400 if (session != NULL) {
401 sessions = g_slist_append(sessions, session);
405 dbus_message_unref(data->message);
406 dbus_connection_unref(data->connection);
407 g_free(data->sender);
410 return g_dbus_create_error(message, "org.openobex.Error.Failed", NULL);
413 static DBusMessage *remove_session(DBusConnection *connection,
414 DBusMessage *message, void *user_data)
416 struct obc_session *session;
417 const gchar *sender, *path;
419 if (dbus_message_get_args(message, NULL,
420 DBUS_TYPE_OBJECT_PATH, &path,
421 DBUS_TYPE_INVALID) == FALSE)
422 return g_dbus_create_error(message,
423 "org.openobex.Error.InvalidArguments", NULL);
425 session = find_session(path);
427 return g_dbus_create_error(message,
428 "org.openobex.Error.InvalidArguments", NULL);
430 sender = dbus_message_get_sender(message);
431 if (g_str_equal(sender, obc_session_get_owner(session)) == FALSE)
432 return g_dbus_create_error(message,
433 "org.openobex.Error.NotAuthorized",
436 shutdown_session(session);
438 return dbus_message_new_method_return(message);
441 static void capabilities_complete_callback(struct obc_session *session,
442 GError *err, void *user_data)
444 struct obc_transfer *transfer = obc_session_get_transfer(session);
445 struct send_data *data = user_data;
446 const char *capabilities;
450 DBusMessage *error = g_dbus_create_error(data->message,
451 "org.openobex.Error.Failed",
453 g_dbus_send_message(data->connection, error);
457 capabilities = obc_transfer_get_buffer(transfer, &size);
461 g_dbus_send_reply(data->connection, data->message,
462 DBUS_TYPE_STRING, &capabilities,
467 shutdown_session(session);
468 dbus_message_unref(data->message);
469 dbus_connection_unref(data->connection);
470 g_free(data->sender);
474 static void capability_obc_session_callback(struct obc_session *session,
475 GError *err, void *user_data)
477 struct send_data *data = user_data;
480 DBusMessage *error = g_dbus_create_error(data->message,
481 "org.openobex.Error.Failed",
483 g_dbus_send_message(data->connection, error);
484 shutdown_session(session);
488 obc_session_pull(session, "x-obex/capability", NULL,
489 capabilities_complete_callback, data);
494 dbus_message_unref(data->message);
495 dbus_connection_unref(data->connection);
496 g_free(data->sender);
500 static DBusMessage *get_capabilities(DBusConnection *connection,
501 DBusMessage *message, void *user_data)
503 DBusMessageIter iter, dict;
504 struct obc_session *session;
505 struct send_data *data;
506 const char *source = NULL, *dest = NULL, *target = NULL;
509 dbus_message_iter_init(message, &iter);
510 dbus_message_iter_recurse(&iter, &dict);
512 parse_device_dict(&dict, &source, &dest, &target, &channel);
514 return g_dbus_create_error(message,
515 "org.openobex.Error.InvalidArguments", NULL);
517 data = g_try_malloc0(sizeof(*data));
519 return g_dbus_create_error(message,
520 "org.openobex.Error.NoMemory", NULL);
522 data->connection = dbus_connection_ref(connection);
523 data->message = dbus_message_ref(message);
524 data->sender = g_strdup(dbus_message_get_sender(message));
529 session = obc_session_create(source, dest, target, channel, data->sender,
530 capability_obc_session_callback, data);
531 if (session != NULL) {
532 sessions = g_slist_append(sessions, session);
536 dbus_message_unref(data->message);
537 dbus_connection_unref(data->connection);
538 g_free(data->sender);
541 return g_dbus_create_error(message, "org.openobex.Error.Failed", NULL);
545 static DBusMessage *get_transfers(DBusConnection *connection,
546 DBusMessage *message, void *user_data)
550 DBusMessageIter iter;
551 DBusMessageIter array_iter;
553 reply = dbus_message_new_method_return(message);
557 dbus_message_iter_init_append(reply, &iter);
558 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
559 DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
561 for (l = opc_get_sessions(); l; l = l->next) {
562 struct obc_session *session = l->data;
568 for (k = opc_get_session_pending(session); k; k = k->next) {
569 struct obc_transfer *transfer = k->data;
570 const char *path = NULL;
572 if (transfer == NULL)
575 path = obc_transfer_get_path(transfer);
580 DBG("transfer path: %s", path);
582 dbus_message_iter_append_basic(&array_iter,
583 DBUS_TYPE_OBJECT_PATH,
588 dbus_message_iter_close_container(&iter, &array_iter);
593 static GDBusMethodTable client_methods[] = {
594 { "SendFiles", "a{sv}aso", "", send_files,
595 G_DBUS_METHOD_FLAG_ASYNC },
596 { "PullBusinessCard", "a{sv}s", "", pull_business_card,
597 G_DBUS_METHOD_FLAG_ASYNC },
598 { "ExchangeBusinessCards", "a{sv}ss", "", exchange_business_cards,
599 G_DBUS_METHOD_FLAG_ASYNC },
600 { "CreateSession", "a{sv}", "o", create_session,
601 G_DBUS_METHOD_FLAG_ASYNC },
602 { "RemoveSession", "o", "", remove_session,
603 G_DBUS_METHOD_FLAG_ASYNC },
604 { "GetCapabilities", "a{sv}", "s", get_capabilities,
605 G_DBUS_METHOD_FLAG_ASYNC },
607 { "GetTransfers", "", "ao", get_transfers,
608 G_DBUS_METHOD_FLAG_ASYNC },
613 static DBusConnection *conn = NULL;
615 static struct target_module {
620 { "opp", opp_init, opp_exit },
621 { "ftp", ftp_init, ftp_exit },
622 { "pbap", pbap_init, pbap_exit },
623 { "sync", sync_init, sync_exit },
624 { "map", map_init, map_exit },
628 int manager_init(void)
631 struct target_module *target;
633 dbus_error_init(&derr);
635 conn = g_dbus_setup_bus(DBUS_BUS_SESSION, CLIENT_SERVICE, &derr);
636 if (dbus_error_is_set(&derr) == TRUE) {
637 error("%s: %s", derr.name, derr.message);
638 dbus_error_free(&derr);
642 if (g_dbus_register_interface(conn, CLIENT_PATH, CLIENT_INTERFACE,
643 client_methods, NULL, NULL,
644 NULL, NULL) == FALSE) {
645 error("Can't register client interface");
646 dbus_connection_unref(conn);
651 for (target = targets; target && target->init; target++) {
652 if (target->init() < 0)
655 DBG("Target %s loaded", target->name);
661 void manager_exit(void)
663 struct target_module *target;
668 for (target = targets; target && target->exit; target++)
671 g_dbus_unregister_interface(conn, CLIENT_PATH, CLIENT_INTERFACE);
672 dbus_connection_unref(conn);