upgrade obexd to 0.47
[profile/ivi/obexd.git] / src / manager.c
1 /*
2  *
3  *  OBEX Server
4  *
5  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
6  *
7  *
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.
12  *
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.
17  *
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
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <gdbus.h>
33 #include <sys/socket.h>
34 #include <inttypes.h>
35
36 #include <gobex.h>
37
38 #include "obexd.h"
39 #include "obex.h"
40 #include "obex-priv.h"
41 #include "server.h"
42 #include "manager.h"
43 #include "log.h"
44 #include "btio.h"
45 #include "service.h"
46
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"
53
54 #define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */
55
56 struct agent {
57         char *bus_name;
58         char *path;
59         gboolean auth_pending;
60         char *new_name;
61         char *new_folder;
62         unsigned int watch_id;
63 };
64
65 static struct agent *agent = NULL;
66
67 static DBusConnection *connection = NULL;
68
69 static void agent_free(struct agent *agent)
70 {
71         if (!agent)
72                 return;
73
74         g_free(agent->new_folder);
75         g_free(agent->new_name);
76         g_free(agent->bus_name);
77         g_free(agent->path);
78         g_free(agent);
79 }
80
81 static inline DBusMessage *invalid_args(DBusMessage *msg)
82 {
83         return g_dbus_create_error(msg,
84                         ERROR_INTERFACE ".InvalidArguments",
85                         "Invalid arguments in method call");
86 }
87
88 static inline DBusMessage *agent_already_exists(DBusMessage *msg)
89 {
90         return g_dbus_create_error(msg,
91                         ERROR_INTERFACE ".AlreadyExists",
92                         "Agent already exists");
93 }
94
95 static inline DBusMessage *agent_does_not_exist(DBusMessage *msg)
96 {
97         return g_dbus_create_error(msg,
98                         ERROR_INTERFACE ".DoesNotExist",
99                         "Agent does not exist");
100 }
101
102 static inline DBusMessage *not_authorized(DBusMessage *msg)
103 {
104         return g_dbus_create_error(msg,
105                         ERROR_INTERFACE ".NotAuthorized",
106                         "Not authorized");
107 }
108
109 static void dbus_message_iter_append_variant(DBusMessageIter *iter,
110                                                 int type, void *val)
111 {
112         DBusMessageIter value;
113         DBusMessageIter array;
114         const char *sig;
115
116         switch (type) {
117         case DBUS_TYPE_STRING:
118                 sig = DBUS_TYPE_STRING_AS_STRING;
119                 break;
120         case DBUS_TYPE_BYTE:
121                 sig = DBUS_TYPE_BYTE_AS_STRING;
122                 break;
123         case DBUS_TYPE_INT16:
124                 sig = DBUS_TYPE_INT16_AS_STRING;
125                 break;
126         case DBUS_TYPE_UINT16:
127                 sig = DBUS_TYPE_UINT16_AS_STRING;
128                 break;
129         case DBUS_TYPE_INT32:
130                 sig = DBUS_TYPE_INT32_AS_STRING;
131                 break;
132         case DBUS_TYPE_UINT32:
133                 sig = DBUS_TYPE_UINT32_AS_STRING;
134                 break;
135         case DBUS_TYPE_BOOLEAN:
136                 sig = DBUS_TYPE_BOOLEAN_AS_STRING;
137                 break;
138         case DBUS_TYPE_ARRAY:
139                 sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
140                 break;
141         case DBUS_TYPE_OBJECT_PATH:
142                 sig = DBUS_TYPE_OBJECT_PATH_AS_STRING;
143                 break;
144         default:
145                 error("Could not append variant with type %d", type);
146                 return;
147         }
148
149         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
150
151         if (type == DBUS_TYPE_ARRAY) {
152                 int i;
153                 const char ***str_array = val;
154
155                 dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
156                         DBUS_TYPE_STRING_AS_STRING, &array);
157
158                 for (i = 0; (*str_array)[i]; i++)
159                         dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
160                                                         &((*str_array)[i]));
161
162                 dbus_message_iter_close_container(&value, &array);
163         } else
164                 dbus_message_iter_append_basic(&value, type, val);
165
166         dbus_message_iter_close_container(iter, &value);
167 }
168
169 static void dbus_message_iter_append_dict_entry(DBusMessageIter *dict,
170                                         const char *key, int type, void *val)
171 {
172         DBusMessageIter entry;
173
174         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
175                                         NULL, &entry);
176
177         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
178
179         dbus_message_iter_append_variant(&entry, type, val);
180
181         dbus_message_iter_close_container(dict, &entry);
182 }
183
184 static void agent_disconnected(DBusConnection *conn, void *user_data)
185 {
186         DBG("Agent exited");
187         agent_free(agent);
188         agent = NULL;
189 }
190
191 static DBusMessage *register_agent(DBusConnection *conn,
192                                         DBusMessage *msg, void *data)
193 {
194         const char *path, *sender;
195
196         if (agent)
197                 return agent_already_exists(msg);
198
199         if (!dbus_message_get_args(msg, NULL,
200                                 DBUS_TYPE_OBJECT_PATH, &path,
201                                 DBUS_TYPE_INVALID))
202                 return invalid_args(msg);
203
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);
208
209         agent->watch_id = g_dbus_add_disconnect_watch(conn, sender,
210                                         agent_disconnected, NULL, NULL);
211
212         DBG("Agent registered");
213
214         return dbus_message_new_method_return(msg);
215 }
216
217 static DBusMessage *unregister_agent(DBusConnection *conn,
218                                         DBusMessage *msg, void *data)
219 {
220         const char *path, *sender;
221
222         if (!agent)
223                 return agent_does_not_exist(msg);
224
225         if (!dbus_message_get_args(msg, NULL,
226                                 DBUS_TYPE_OBJECT_PATH, &path,
227                                 DBUS_TYPE_INVALID))
228                 return invalid_args(msg);
229
230         if (strcmp(agent->path, path) != 0)
231                 return agent_does_not_exist(msg);
232
233         sender = dbus_message_get_sender(msg);
234         if (strcmp(agent->bus_name, sender) != 0)
235                 return not_authorized(msg);
236
237         g_dbus_remove_watch(conn, agent->watch_id);
238
239         agent_free(agent);
240         agent = NULL;
241
242         DBG("Agent unregistered");
243
244         return dbus_message_new_method_return(msg);
245 }
246
247 static char *target2str(const uint8_t *t)
248 {
249         if (!t)
250                 return NULL;
251
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]);
256 }
257
258 static DBusMessage *get_properties(DBusConnection *conn,
259                                 DBusMessage *msg, void *data)
260 {
261         struct obex_session *os = data;
262         DBusMessage *reply;
263         DBusMessageIter iter;
264         DBusMessageIter dict;
265         char *uuid;
266         const char *root;
267
268         reply = dbus_message_new_method_return(msg);
269         if (!reply)
270                 return NULL;
271
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);
277
278         /* Target */
279         uuid = target2str(os->service->target);
280         dbus_message_iter_append_dict_entry(&dict, "Target",
281                                         DBUS_TYPE_STRING, &uuid);
282         g_free(uuid);
283
284         /* Root folder */
285         root = obex_option_root_folder();
286         dbus_message_iter_append_dict_entry(&dict, "Root",
287                                         DBUS_TYPE_STRING, &root);
288
289         /* FIXME: Added Remote Address or USB */
290
291         dbus_message_iter_close_container(&iter, &dict);
292
293         return reply;
294 }
295
296 static DBusMessage *transfer_cancel(DBusConnection *connection,
297                                 DBusMessage *msg, void *user_data)
298 {
299         struct obex_session *os = user_data;
300         const char *sender;
301
302         if (!os)
303                 return invalid_args(msg);
304
305         sender = dbus_message_get_sender(msg);
306         if (strcmp(agent->bus_name, sender) != 0)
307                 return not_authorized(msg);
308
309         os->aborted = TRUE;
310
311         return dbus_message_new_method_return(msg);
312 }
313
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) },
319         { }
320 };
321
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" })) },
328         { }
329 };
330
331 static const GDBusMethodTable transfer_methods[] = {
332         { GDBUS_METHOD("Cancel", NULL, NULL, transfer_cancel) },
333         { }
334 };
335
336 static const GDBusSignalTable transfer_signals[] = {
337         { GDBUS_SIGNAL("Progress", GDBUS_ARGS({ "total", "i" },
338                                                 { "transferred", "i" })) },
339         { }
340 };
341
342 static const GDBusMethodTable session_methods[] = {
343         { GDBUS_METHOD("GetProperties",
344                                 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
345                                 get_properties) },
346         { }
347 };
348
349 gboolean manager_init(void)
350 {
351         DBusError err;
352
353         DBG("");
354
355         dbus_error_init(&err);
356
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);
362                 } else
363                         fprintf(stderr, "Can't register with session bus\n");
364                 return FALSE;
365         }
366
367         return g_dbus_register_interface(connection, OBEX_MANAGER_PATH,
368                                         OBEX_MANAGER_INTERFACE,
369                                         manager_methods, manager_signals, NULL,
370                                         NULL, NULL);
371 }
372
373 void manager_cleanup(void)
374 {
375         DBG("");
376
377         g_dbus_unregister_interface(connection, OBEX_MANAGER_PATH,
378                                                 OBEX_MANAGER_INTERFACE);
379
380         /* FIXME: Release agent? */
381
382         if (agent)
383                 agent_free(agent);
384
385         dbus_connection_unref(connection);
386 }
387
388 void manager_emit_transfer_started(struct obex_session *os)
389 {
390         char *path = g_strdup_printf("/transfer%u", os->id);
391
392         g_dbus_emit_signal(connection, OBEX_MANAGER_PATH,
393                         OBEX_MANAGER_INTERFACE, "TransferStarted",
394                         DBUS_TYPE_OBJECT_PATH, &path,
395                         DBUS_TYPE_INVALID);
396
397         g_free(path);
398 }
399
400 static void emit_transfer_completed(struct obex_session *os, gboolean success)
401 {
402         char *path = g_strdup_printf("/transfer%u", os->id);
403
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,
408                         DBUS_TYPE_INVALID);
409
410         g_free(path);
411 }
412
413 static void emit_transfer_progress(struct obex_session *os, uint32_t total,
414                                                         uint32_t transferred)
415 {
416         char *path = g_strdup_printf("/transfer%u", os->id);
417
418         g_dbus_emit_signal(connection, path,
419                         TRANSFER_INTERFACE, "Progress",
420                         DBUS_TYPE_INT32, &total,
421                         DBUS_TYPE_INT32, &transferred,
422                         DBUS_TYPE_INVALID);
423
424         g_free(path);
425 }
426
427 void manager_register_transfer(struct obex_session *os)
428 {
429         char *path = g_strdup_printf("/transfer%u", os->id);
430
431         if (!g_dbus_register_interface(connection, path,
432                                 TRANSFER_INTERFACE,
433                                 transfer_methods, transfer_signals,
434                                 NULL, os, NULL)) {
435                 error("Cannot register Transfer interface.");
436                 g_free(path);
437                 return;
438         }
439
440         g_free(path);
441 }
442
443 void manager_unregister_transfer(struct obex_session *os)
444 {
445         char *path = g_strdup_printf("/transfer%u", os->id);
446
447         /* Got an error during a transfer. */
448         if (os->object)
449                 emit_transfer_completed(os, os->offset == os->size);
450
451         g_dbus_unregister_interface(connection, path,
452                                 TRANSFER_INTERFACE);
453
454         g_free(path);
455 }
456
457 static void agent_cancel(void)
458 {
459         DBusMessage *msg;
460
461         if (agent == NULL)
462                 return;
463
464         msg = dbus_message_new_method_call(agent->bus_name, agent->path,
465                                                 AGENT_INTERFACE, "Cancel");
466
467         g_dbus_send_message(connection, msg);
468 }
469
470 static void agent_reply(DBusPendingCall *call, void *user_data)
471 {
472         DBusMessage *reply = dbus_pending_call_steal_reply(call);
473         const char *name;
474         DBusError derr;
475         gboolean *got_reply = user_data;
476
477         *got_reply = TRUE;
478
479         /* Received a reply after the agent exited */
480         if (!agent)
481                 return;
482
483         agent->auth_pending = FALSE;
484
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);
489
490                 if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY))
491                         agent_cancel();
492
493                 dbus_error_free(&derr);
494                 dbus_message_unref(reply);
495                 return;
496         }
497
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);
504                 if (!slash) {
505                         agent->new_name = g_strdup(name);
506                         agent->new_folder = NULL;
507                 } else {
508                         agent->new_name = g_strdup(slash + 1);
509                         agent->new_folder = g_strndup(name, slash - name);
510                 }
511         }
512
513         dbus_message_unref(reply);
514 }
515
516 static gboolean auth_error(GIOChannel *io, GIOCondition cond, void *user_data)
517 {
518         agent->auth_pending = FALSE;
519
520         return FALSE;
521 }
522
523 int manager_request_authorization(struct obex_session *os, int32_t time,
524                                         char **new_folder, char **new_name)
525 {
526         DBusMessage *msg;
527         DBusPendingCall *call;
528         const char *filename = os->name ? os->name : "";
529         const char *type = os->type ? os->type : "";
530         char *path, *address;
531         unsigned int watch;
532         gboolean got_reply;
533         int err;
534
535         if (!agent)
536                 return -1;
537
538         if (agent->auth_pending)
539                 return -EPERM;
540
541         if (!new_folder || !new_name)
542                 return -EINVAL;
543
544         err = obex_getpeername(os, &address);
545         if (err < 0)
546                 return err;
547
548         path = g_strdup_printf("/transfer%u", os->id);
549
550         msg = dbus_message_new_method_call(agent->bus_name, agent->path,
551                                                 AGENT_INTERFACE, "Authorize");
552
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,
560                         DBUS_TYPE_INVALID);
561
562         g_free(path);
563         g_free(address);
564
565         if (!dbus_connection_send_with_reply(connection,
566                                         msg, &call, TIMEOUT)) {
567                 dbus_message_unref(msg);
568                 return -EPERM;
569         }
570
571         dbus_message_unref(msg);
572
573         agent->auth_pending = TRUE;
574         got_reply = FALSE;
575
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);
580
581         dbus_pending_call_set_notify(call, agent_reply, &got_reply, NULL);
582
583         /* Workaround: process events while agent doesn't reply */
584         while (agent && agent->auth_pending)
585                 g_main_context_iteration(NULL, TRUE);
586
587         g_source_remove(watch);
588
589         if (!got_reply) {
590                 dbus_pending_call_cancel(call);
591                 agent_cancel();
592         }
593
594         dbus_pending_call_unref(call);
595
596         if (!agent || !agent->new_name)
597                 return -EPERM;
598
599         *new_folder = agent->new_folder;
600         *new_name = agent->new_name;
601         agent->new_folder = NULL;
602         agent->new_name = NULL;
603
604         return 0;
605 }
606
607 void manager_register_session(struct obex_session *os)
608 {
609         char *path = g_strdup_printf("/session%u", GPOINTER_TO_UINT(os));
610
611         if (!g_dbus_register_interface(connection, path,
612                                 SESSION_INTERFACE,
613                                 session_methods, NULL,
614                                 NULL, os, NULL)) {
615                 error("Cannot register Session interface.");
616                 goto done;
617         }
618
619         g_dbus_emit_signal(connection, OBEX_MANAGER_PATH,
620                         OBEX_MANAGER_INTERFACE, "SessionCreated",
621                         DBUS_TYPE_OBJECT_PATH, &path,
622                         DBUS_TYPE_INVALID);
623
624 done:
625         g_free(path);
626 }
627
628 void manager_unregister_session(struct obex_session *os)
629 {
630         char *path = g_strdup_printf("/session%u", GPOINTER_TO_UINT(os));
631
632         g_dbus_emit_signal(connection, OBEX_MANAGER_PATH,
633                         OBEX_MANAGER_INTERFACE, "SessionRemoved",
634                         DBUS_TYPE_OBJECT_PATH, &path,
635                         DBUS_TYPE_INVALID);
636
637         g_dbus_unregister_interface(connection, path,
638                                 SESSION_INTERFACE);
639
640         g_free(path);
641 }
642
643 void manager_emit_transfer_progress(struct obex_session *os)
644 {
645         emit_transfer_progress(os, os->size, os->offset);
646 }
647
648 void manager_emit_transfer_completed(struct obex_session *os)
649 {
650         if (os->object)
651                 emit_transfer_completed(os, !os->aborted);
652 }
653
654 DBusConnection *manager_dbus_get_connection(void)
655 {
656         if (connection == NULL)
657                 return NULL;
658
659         return dbus_connection_ref(connection);
660 }