Imported Upstream version 1.35
[platform/upstream/connman.git] / gdbus / client.c
1 /*
2  *
3  *  D-Bus helper library
4  *
5  *  Copyright (C) 2004-2011  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 <glib.h>
30 #include <dbus/dbus.h>
31
32 #include "gdbus.h"
33
34 #define METHOD_CALL_TIMEOUT (300 * 1000)
35
36 #ifndef DBUS_INTERFACE_OBJECT_MANAGER
37 #define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager"
38 #endif
39
40 struct GDBusClient {
41         int ref_count;
42         DBusConnection *dbus_conn;
43         char *service_name;
44         char *base_path;
45         char *root_path;
46         guint watch;
47         guint added_watch;
48         guint removed_watch;
49         GPtrArray *match_rules;
50         DBusPendingCall *pending_call;
51         DBusPendingCall *get_objects_call;
52         GDBusWatchFunction connect_func;
53         void *connect_data;
54         GDBusWatchFunction disconn_func;
55         gboolean connected;
56         void *disconn_data;
57         GDBusMessageFunction signal_func;
58         void *signal_data;
59         GDBusProxyFunction proxy_added;
60         GDBusProxyFunction proxy_removed;
61         GDBusClientFunction ready;
62         void *ready_data;
63         GDBusPropertyFunction property_changed;
64         void *user_data;
65         GList *proxy_list;
66 };
67
68 struct GDBusProxy {
69         int ref_count;
70         GDBusClient *client;
71         char *obj_path;
72         char *interface;
73         GHashTable *prop_list;
74         guint watch;
75         GDBusPropertyFunction prop_func;
76         void *prop_data;
77         GDBusProxyFunction removed_func;
78         void *removed_data;
79 };
80
81 struct prop_entry {
82         char *name;
83         int type;
84         DBusMessage *msg;
85 };
86
87 static void modify_match_reply(DBusPendingCall *call, void *user_data)
88 {
89         DBusMessage *reply = dbus_pending_call_steal_reply(call);
90         DBusError error;
91
92         dbus_error_init(&error);
93
94         if (dbus_set_error_from_message(&error, reply) == TRUE)
95                 dbus_error_free(&error);
96
97         dbus_message_unref(reply);
98 }
99
100 static gboolean modify_match(DBusConnection *conn, const char *member,
101                                                         const char *rule)
102 {
103         DBusMessage *msg;
104         DBusPendingCall *call;
105
106         msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
107                                         DBUS_INTERFACE_DBUS, member);
108         if (msg == NULL)
109                 return FALSE;
110
111         dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule,
112                                                 DBUS_TYPE_INVALID);
113
114         if (g_dbus_send_message_with_reply(conn, msg, &call, -1) == FALSE) {
115                 dbus_message_unref(msg);
116                 return FALSE;
117         }
118
119         dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL);
120         dbus_pending_call_unref(call);
121
122         dbus_message_unref(msg);
123
124         return TRUE;
125 }
126
127 static void iter_append_iter(DBusMessageIter *base, DBusMessageIter *iter)
128 {
129         int type;
130
131         type = dbus_message_iter_get_arg_type(iter);
132
133         if (dbus_type_is_basic(type)) {
134                 const void *value;
135
136                 dbus_message_iter_get_basic(iter, &value);
137                 dbus_message_iter_append_basic(base, type, &value);
138         } else if (dbus_type_is_container(type)) {
139                 DBusMessageIter iter_sub, base_sub;
140                 char *sig;
141
142                 dbus_message_iter_recurse(iter, &iter_sub);
143
144                 switch (type) {
145                 case DBUS_TYPE_ARRAY:
146                 case DBUS_TYPE_VARIANT:
147                         sig = dbus_message_iter_get_signature(&iter_sub);
148                         break;
149                 default:
150                         sig = NULL;
151                         break;
152                 }
153
154                 dbus_message_iter_open_container(base, type, sig, &base_sub);
155
156                 if (sig != NULL)
157                         dbus_free(sig);
158
159                 while (dbus_message_iter_get_arg_type(&iter_sub) !=
160                                                         DBUS_TYPE_INVALID) {
161                         iter_append_iter(&base_sub, &iter_sub);
162                         dbus_message_iter_next(&iter_sub);
163                 }
164
165                 dbus_message_iter_close_container(base, &base_sub);
166         }
167 }
168
169 static void prop_entry_update(struct prop_entry *prop, DBusMessageIter *iter)
170 {
171         DBusMessage *msg;
172         DBusMessageIter base;
173
174         msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
175         if (msg == NULL)
176                 return;
177
178         dbus_message_iter_init_append(msg, &base);
179         iter_append_iter(&base, iter);
180
181         if (prop->msg != NULL)
182                 dbus_message_unref(prop->msg);
183
184         prop->msg = dbus_message_copy(msg);
185         dbus_message_unref(msg);
186 }
187
188 static struct prop_entry *prop_entry_new(const char *name,
189                                                 DBusMessageIter *iter)
190 {
191         struct prop_entry *prop;
192
193         prop = g_try_new0(struct prop_entry, 1);
194         if (prop == NULL)
195                 return NULL;
196
197         prop->name = g_strdup(name);
198         prop->type = dbus_message_iter_get_arg_type(iter);
199
200         prop_entry_update(prop, iter);
201
202         return prop;
203 }
204
205 static void prop_entry_free(gpointer data)
206 {
207         struct prop_entry *prop = data;
208
209         if (prop->msg != NULL)
210                 dbus_message_unref(prop->msg);
211
212         g_free(prop->name);
213
214         g_free(prop);
215 }
216
217 static void add_property(GDBusProxy *proxy, const char *name,
218                                 DBusMessageIter *iter, gboolean send_changed)
219 {
220         GDBusClient *client = proxy->client;
221         DBusMessageIter value;
222         struct prop_entry *prop;
223
224         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
225                 return;
226
227         dbus_message_iter_recurse(iter, &value);
228
229         prop = g_hash_table_lookup(proxy->prop_list, name);
230         if (prop != NULL) {
231                 prop_entry_update(prop, &value);
232                 goto done;
233         }
234
235         prop = prop_entry_new(name, &value);
236         if (prop == NULL)
237                 return;
238
239         g_hash_table_replace(proxy->prop_list, prop->name, prop);
240
241 done:
242         if (proxy->prop_func)
243                 proxy->prop_func(proxy, name, &value, proxy->prop_data);
244
245         if (client == NULL || send_changed == FALSE)
246                 return;
247
248         if (client->property_changed)
249                 client->property_changed(proxy, name, &value,
250                                                         client->user_data);
251 }
252
253 static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter,
254                                                         gboolean send_changed)
255 {
256         DBusMessageIter dict;
257
258         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
259                 return;
260
261         dbus_message_iter_recurse(iter, &dict);
262
263         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
264                 DBusMessageIter entry;
265                 const char *name;
266
267                 dbus_message_iter_recurse(&dict, &entry);
268
269                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
270                         break;
271
272                 dbus_message_iter_get_basic(&entry, &name);
273                 dbus_message_iter_next(&entry);
274
275                 add_property(proxy, name, &entry, send_changed);
276
277                 dbus_message_iter_next(&dict);
278         }
279 }
280
281 static void get_all_properties_reply(DBusPendingCall *call, void *user_data)
282 {
283         GDBusProxy *proxy = user_data;
284         GDBusClient *client = proxy->client;
285         DBusMessage *reply = dbus_pending_call_steal_reply(call);
286         DBusMessageIter iter;
287         DBusError error;
288
289         dbus_error_init(&error);
290
291         if (dbus_set_error_from_message(&error, reply) == TRUE) {
292                 dbus_error_free(&error);
293                 goto done;
294         }
295
296         dbus_message_iter_init(reply, &iter);
297
298         update_properties(proxy, &iter, FALSE);
299
300 done:
301         if (g_list_find(client->proxy_list, proxy) == NULL) {
302                 if (client->proxy_added)
303                         client->proxy_added(proxy, client->user_data);
304
305                 client->proxy_list = g_list_append(client->proxy_list, proxy);
306         }
307
308         dbus_message_unref(reply);
309
310         g_dbus_client_unref(client);
311 }
312
313 static void get_all_properties(GDBusProxy *proxy)
314 {
315         GDBusClient *client = proxy->client;
316         const char *service_name = client->service_name;
317         DBusMessage *msg;
318         DBusPendingCall *call;
319
320         msg = dbus_message_new_method_call(service_name, proxy->obj_path,
321                                         DBUS_INTERFACE_PROPERTIES, "GetAll");
322         if (msg == NULL)
323                 return;
324
325         dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface,
326                                                         DBUS_TYPE_INVALID);
327
328         if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
329                                                         &call, -1) == FALSE) {
330                 dbus_message_unref(msg);
331                 return;
332         }
333
334         g_dbus_client_ref(client);
335
336         dbus_pending_call_set_notify(call, get_all_properties_reply,
337                                                         proxy, NULL);
338         dbus_pending_call_unref(call);
339
340         dbus_message_unref(msg);
341 }
342
343 static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path,
344                                                 const char *interface)
345 {
346         GList *list;
347
348         for (list = g_list_first(client->proxy_list); list;
349                                                 list = g_list_next(list)) {
350                 GDBusProxy *proxy = list->data;
351
352                 if (g_str_equal(proxy->interface, interface) == TRUE &&
353                                 g_str_equal(proxy->obj_path, path) == TRUE)
354                         return proxy;
355         }
356
357         return NULL;
358 }
359
360 static gboolean properties_changed(DBusConnection *conn, DBusMessage *msg,
361                                                         void *user_data)
362 {
363         GDBusProxy *proxy = user_data;
364         GDBusClient *client = proxy->client;
365         DBusMessageIter iter, entry;
366         const char *interface;
367
368         if (dbus_message_iter_init(msg, &iter) == FALSE)
369                 return TRUE;
370
371         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
372                 return TRUE;
373
374         dbus_message_iter_get_basic(&iter, &interface);
375         dbus_message_iter_next(&iter);
376
377         update_properties(proxy, &iter, TRUE);
378
379         dbus_message_iter_next(&iter);
380
381         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
382                 return TRUE;
383
384         dbus_message_iter_recurse(&iter, &entry);
385
386         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
387                 const char *name;
388
389                 dbus_message_iter_get_basic(&entry, &name);
390
391                 g_hash_table_remove(proxy->prop_list, name);
392
393                 if (proxy->prop_func)
394                         proxy->prop_func(proxy, name, NULL, proxy->prop_data);
395
396                 if (client->property_changed)
397                         client->property_changed(proxy, name, NULL,
398                                                         client->user_data);
399
400                 dbus_message_iter_next(&entry);
401         }
402
403         return TRUE;
404 }
405
406 static GDBusProxy *proxy_new(GDBusClient *client, const char *path,
407                                                 const char *interface)
408 {
409         GDBusProxy *proxy;
410
411         proxy = g_try_new0(GDBusProxy, 1);
412         if (proxy == NULL)
413                 return NULL;
414
415         proxy->client = client;
416         proxy->obj_path = g_strdup(path);
417         proxy->interface = g_strdup(interface);
418
419         proxy->prop_list = g_hash_table_new_full(g_str_hash, g_str_equal,
420                                                         NULL, prop_entry_free);
421         proxy->watch = g_dbus_add_properties_watch(client->dbus_conn,
422                                                         client->service_name,
423                                                         proxy->obj_path,
424                                                         proxy->interface,
425                                                         properties_changed,
426                                                         proxy, NULL);
427
428         return g_dbus_proxy_ref(proxy);
429 }
430
431 static void proxy_free(gpointer data)
432 {
433         GDBusProxy *proxy = data;
434
435         if (proxy->client) {
436                 GDBusClient *client = proxy->client;
437
438                 if (client->proxy_removed)
439                         client->proxy_removed(proxy, client->user_data);
440
441                 g_dbus_remove_watch(client->dbus_conn, proxy->watch);
442
443                 g_hash_table_remove_all(proxy->prop_list);
444
445                 proxy->client = NULL;
446         }
447
448         if (proxy->removed_func)
449                 proxy->removed_func(proxy, proxy->removed_data);
450
451         g_dbus_proxy_unref(proxy);
452 }
453
454 static void proxy_remove(GDBusClient *client, const char *path,
455                                                 const char *interface)
456 {
457         GList *list;
458
459         for (list = g_list_first(client->proxy_list); list;
460                                                 list = g_list_next(list)) {
461                 GDBusProxy *proxy = list->data;
462
463                 if (g_str_equal(proxy->interface, interface) == TRUE &&
464                                 g_str_equal(proxy->obj_path, path) == TRUE) {
465                         client->proxy_list =
466                                 g_list_delete_link(client->proxy_list, list);
467                         proxy_free(proxy);
468                         break;
469                 }
470         }
471 }
472
473 GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
474                                                         const char *interface)
475 {
476         GDBusProxy *proxy;
477
478         if (client == NULL)
479                 return NULL;
480
481         proxy = proxy_lookup(client, path, interface);
482         if (proxy)
483                 return g_dbus_proxy_ref(proxy);
484
485         proxy = proxy_new(client, path, interface);
486         if (proxy == NULL)
487                 return NULL;
488
489         get_all_properties(proxy);
490
491         return g_dbus_proxy_ref(proxy);
492 }
493
494 GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy)
495 {
496         if (proxy == NULL)
497                 return NULL;
498
499         __sync_fetch_and_add(&proxy->ref_count, 1);
500
501         return proxy;
502 }
503
504 void g_dbus_proxy_unref(GDBusProxy *proxy)
505 {
506         if (proxy == NULL)
507                 return;
508
509         if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0)
510                 return;
511
512         g_hash_table_destroy(proxy->prop_list);
513
514         g_free(proxy->obj_path);
515         g_free(proxy->interface);
516
517         g_free(proxy);
518 }
519
520 const char *g_dbus_proxy_get_path(GDBusProxy *proxy)
521 {
522         if (proxy == NULL)
523                 return NULL;
524
525         return proxy->obj_path;
526 }
527
528 const char *g_dbus_proxy_get_interface(GDBusProxy *proxy)
529 {
530         if (proxy == NULL)
531                 return NULL;
532
533         return proxy->interface;
534 }
535
536 gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name,
537                                                         DBusMessageIter *iter)
538 {
539         struct prop_entry *prop;
540
541         if (proxy == NULL || name == NULL)
542                 return FALSE;
543
544         prop = g_hash_table_lookup(proxy->prop_list, name);
545         if (prop == NULL)
546                 return FALSE;
547
548         if (prop->msg == NULL)
549                 return FALSE;
550
551         if (dbus_message_iter_init(prop->msg, iter) == FALSE)
552                 return FALSE;
553
554         return TRUE;
555 }
556
557 struct refresh_property_data {
558         GDBusProxy *proxy;
559         char *name;
560 };
561
562 static void refresh_property_free(gpointer user_data)
563 {
564         struct refresh_property_data *data = user_data;
565
566         g_free(data->name);
567         g_free(data);
568 }
569
570 static void refresh_property_reply(DBusPendingCall *call, void *user_data)
571 {
572         struct refresh_property_data *data = user_data;
573         DBusMessage *reply = dbus_pending_call_steal_reply(call);
574         DBusError error;
575
576         dbus_error_init(&error);
577
578         if (dbus_set_error_from_message(&error, reply) == FALSE) {
579                 DBusMessageIter iter;
580
581                 dbus_message_iter_init(reply, &iter);
582
583                 add_property(data->proxy, data->name, &iter, TRUE);
584         } else
585                 dbus_error_free(&error);
586
587         dbus_message_unref(reply);
588 }
589
590 gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name)
591 {
592         struct refresh_property_data *data;
593         GDBusClient *client;
594         DBusMessage *msg;
595         DBusMessageIter iter;
596         DBusPendingCall *call;
597
598         if (proxy == NULL || name == NULL)
599                 return FALSE;
600
601         client = proxy->client;
602         if (client == NULL)
603                 return FALSE;
604
605         data = g_try_new0(struct refresh_property_data, 1);
606         if (data == NULL)
607                 return FALSE;
608
609         data->proxy = proxy;
610         data->name = g_strdup(name);
611
612         msg = dbus_message_new_method_call(client->service_name,
613                         proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get");
614         if (msg == NULL) {
615                 refresh_property_free(data);
616                 return FALSE;
617         }
618
619         dbus_message_iter_init_append(msg, &iter);
620         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
621                                                         &proxy->interface);
622         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
623
624         if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
625                                                         &call, -1) == FALSE) {
626                 dbus_message_unref(msg);
627                 refresh_property_free(data);
628                 return FALSE;
629         }
630
631         dbus_pending_call_set_notify(call, refresh_property_reply,
632                                                 data, refresh_property_free);
633         dbus_pending_call_unref(call);
634
635         dbus_message_unref(msg);
636
637         return TRUE;
638 }
639
640 struct set_property_data {
641         GDBusResultFunction function;
642         void *user_data;
643         GDBusDestroyFunction destroy;
644 };
645
646 static void set_property_reply(DBusPendingCall *call, void *user_data)
647 {
648         struct set_property_data *data = user_data;
649         DBusMessage *reply = dbus_pending_call_steal_reply(call);
650         DBusError error;
651
652         dbus_error_init(&error);
653
654         dbus_set_error_from_message(&error, reply);
655
656         if (data->function)
657                 data->function(&error, data->user_data);
658
659         if (data->destroy)
660                 data->destroy(data->user_data);
661
662         dbus_error_free(&error);
663
664         dbus_message_unref(reply);
665 }
666
667 gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
668                                 const char *name, int type, const void *value,
669                                 GDBusResultFunction function, void *user_data,
670                                 GDBusDestroyFunction destroy)
671 {
672         struct set_property_data *data;
673         GDBusClient *client;
674         DBusMessage *msg;
675         DBusMessageIter iter, variant;
676         DBusPendingCall *call;
677         char type_as_str[2];
678
679         if (proxy == NULL || name == NULL || value == NULL)
680                 return FALSE;
681
682         if (dbus_type_is_basic(type) == FALSE)
683                 return FALSE;
684
685         client = proxy->client;
686         if (client == NULL)
687                 return FALSE;
688
689         data = g_try_new0(struct set_property_data, 1);
690         if (data == NULL)
691                 return FALSE;
692
693         data->function = function;
694         data->user_data = user_data;
695         data->destroy = destroy;
696
697         msg = dbus_message_new_method_call(client->service_name,
698                         proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
699         if (msg == NULL) {
700                 g_free(data);
701                 return FALSE;
702         }
703
704         type_as_str[0] = (char) type;
705         type_as_str[1] = '\0';
706
707         dbus_message_iter_init_append(msg, &iter);
708         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
709                                                         &proxy->interface);
710         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
711
712         dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
713                                                 type_as_str, &variant);
714         dbus_message_iter_append_basic(&variant, type, value);
715         dbus_message_iter_close_container(&iter, &variant);
716
717         if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
718                                                         &call, -1) == FALSE) {
719                 dbus_message_unref(msg);
720                 g_free(data);
721                 return FALSE;
722         }
723
724         dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
725         dbus_pending_call_unref(call);
726
727         dbus_message_unref(msg);
728
729         return TRUE;
730 }
731
732 gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
733                                 const char *name, int type, const void *value,
734                                 size_t size, GDBusResultFunction function,
735                                 void *user_data, GDBusDestroyFunction destroy)
736 {
737         struct set_property_data *data;
738         GDBusClient *client;
739         DBusMessage *msg;
740         DBusMessageIter iter, variant, array;
741         DBusPendingCall *call;
742         char array_sig[3];
743         char type_sig[2];
744
745         if (!proxy || !name || !value)
746                 return FALSE;
747
748         if (!dbus_type_is_basic(type))
749                 return FALSE;
750
751         client = proxy->client;
752         if (!client)
753                 return FALSE;
754
755         data = g_try_new0(struct set_property_data, 1);
756         if (!data)
757                 return FALSE;
758
759         data->function = function;
760         data->user_data = user_data;
761         data->destroy = destroy;
762
763         msg = dbus_message_new_method_call(client->service_name,
764                                                 proxy->obj_path,
765                                                 DBUS_INTERFACE_PROPERTIES,
766                                                 "Set");
767         if (!msg) {
768                 g_free(data);
769                 return FALSE;
770         }
771
772         array_sig[0] = DBUS_TYPE_ARRAY;
773         array_sig[1] = (char) type;
774         array_sig[2] = '\0';
775
776         type_sig[0] = (char) type;
777         type_sig[1] = '\0';
778
779         dbus_message_iter_init_append(msg, &iter);
780         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
781                                                         &proxy->interface);
782         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
783
784         dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
785                                                         array_sig, &variant);
786
787         dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
788                                                         type_sig, &array);
789
790         if (dbus_type_is_fixed(type))
791                 dbus_message_iter_append_fixed_array(&array, type, &value,
792                                                                         size);
793         else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
794                 const char **str = (const char **) value;
795                 size_t i;
796
797                 for (i = 0; i < size; i++)
798                         dbus_message_iter_append_basic(&array, type, &str[i]);
799         }
800
801         dbus_message_iter_close_container(&variant, &array);
802         dbus_message_iter_close_container(&iter, &variant);
803
804         if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
805                                                         &call, -1) == FALSE) {
806                 dbus_message_unref(msg);
807                 g_free(data);
808                 return FALSE;
809         }
810
811         dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
812         dbus_pending_call_unref(call);
813
814         dbus_message_unref(msg);
815
816         return TRUE;
817 }
818
819 struct method_call_data {
820         GDBusReturnFunction function;
821         void *user_data;
822         GDBusDestroyFunction destroy;
823 };
824
825 static void method_call_reply(DBusPendingCall *call, void *user_data)
826 {
827         struct method_call_data *data = user_data;
828         DBusMessage *reply = dbus_pending_call_steal_reply(call);
829
830         if (data->function)
831                 data->function(reply, data->user_data);
832
833         if (data->destroy)
834                 data->destroy(data->user_data);
835
836         dbus_message_unref(reply);
837 }
838
839 gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
840                                 GDBusSetupFunction setup,
841                                 GDBusReturnFunction function, void *user_data,
842                                 GDBusDestroyFunction destroy)
843 {
844         struct method_call_data *data;
845         GDBusClient *client;
846         DBusMessage *msg;
847         DBusPendingCall *call;
848
849         if (proxy == NULL || method == NULL)
850                 return FALSE;
851
852         client = proxy->client;
853         if (client == NULL)
854                 return FALSE;
855
856         msg = dbus_message_new_method_call(client->service_name,
857                                 proxy->obj_path, proxy->interface, method);
858         if (msg == NULL)
859                 return FALSE;
860
861         if (setup) {
862                 DBusMessageIter iter;
863
864                 dbus_message_iter_init_append(msg, &iter);
865                 setup(&iter, user_data);
866         }
867
868         if (!function)
869                 return g_dbus_send_message(client->dbus_conn, msg);
870
871         data = g_try_new0(struct method_call_data, 1);
872         if (data == NULL)
873                 return FALSE;
874
875         data->function = function;
876         data->user_data = user_data;
877         data->destroy = destroy;
878
879
880         if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
881                                         &call, METHOD_CALL_TIMEOUT) == FALSE) {
882                 dbus_message_unref(msg);
883                 g_free(data);
884                 return FALSE;
885         }
886
887         dbus_pending_call_set_notify(call, method_call_reply, data, g_free);
888         dbus_pending_call_unref(call);
889
890         dbus_message_unref(msg);
891
892         return TRUE;
893 }
894
895 gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy,
896                         GDBusPropertyFunction function, void *user_data)
897 {
898         if (proxy == NULL)
899                 return FALSE;
900
901         proxy->prop_func = function;
902         proxy->prop_data = user_data;
903
904         return TRUE;
905 }
906
907 gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy,
908                                 GDBusProxyFunction function, void *user_data)
909 {
910         if (proxy == NULL)
911                 return FALSE;
912
913         proxy->removed_func = function;
914         proxy->removed_data = user_data;
915
916         return TRUE;
917 }
918
919 static void refresh_properties(GDBusClient *client)
920 {
921         GList *list;
922
923         for (list = g_list_first(client->proxy_list); list;
924                                                 list = g_list_next(list)) {
925                 GDBusProxy *proxy = list->data;
926
927                 get_all_properties(proxy);
928         }
929 }
930
931 static void parse_properties(GDBusClient *client, const char *path,
932                                 const char *interface, DBusMessageIter *iter)
933 {
934         GDBusProxy *proxy;
935
936         if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE)
937                 return;
938
939         if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
940                 return;
941
942         proxy = proxy_lookup(client, path, interface);
943         if (proxy) {
944                 update_properties(proxy, iter, FALSE);
945                 return;
946         }
947
948         proxy = proxy_new(client, path, interface);
949         if (proxy == NULL)
950                 return;
951
952         update_properties(proxy, iter, FALSE);
953
954         if (client->proxy_added)
955                 client->proxy_added(proxy, client->user_data);
956
957         client->proxy_list = g_list_append(client->proxy_list, proxy);
958 }
959
960 static void parse_interfaces(GDBusClient *client, const char *path,
961                                                 DBusMessageIter *iter)
962 {
963         DBusMessageIter dict;
964
965         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
966                 return;
967
968         dbus_message_iter_recurse(iter, &dict);
969
970         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
971                 DBusMessageIter entry;
972                 const char *interface;
973
974                 dbus_message_iter_recurse(&dict, &entry);
975
976                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
977                         break;
978
979                 dbus_message_iter_get_basic(&entry, &interface);
980                 dbus_message_iter_next(&entry);
981
982                 parse_properties(client, path, interface, &entry);
983
984                 dbus_message_iter_next(&dict);
985         }
986 }
987
988 static gboolean interfaces_added(DBusConnection *conn, DBusMessage *msg,
989                                                         void *user_data)
990 {
991         GDBusClient *client = user_data;
992         DBusMessageIter iter;
993         const char *path;
994
995         if (dbus_message_iter_init(msg, &iter) == FALSE)
996                 return TRUE;
997
998         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
999                 return TRUE;
1000
1001         dbus_message_iter_get_basic(&iter, &path);
1002         dbus_message_iter_next(&iter);
1003
1004         g_dbus_client_ref(client);
1005
1006         parse_interfaces(client, path, &iter);
1007
1008         g_dbus_client_unref(client);
1009
1010         return TRUE;
1011 }
1012
1013 static gboolean interfaces_removed(DBusConnection *conn, DBusMessage *msg,
1014                                                         void *user_data)
1015 {
1016         GDBusClient *client = user_data;
1017         DBusMessageIter iter, entry;
1018         const char *path;
1019
1020         if (dbus_message_iter_init(msg, &iter) == FALSE)
1021                 return TRUE;
1022
1023         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
1024                 return TRUE;
1025
1026         dbus_message_iter_get_basic(&iter, &path);
1027         dbus_message_iter_next(&iter);
1028
1029         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
1030                 return TRUE;
1031
1032         dbus_message_iter_recurse(&iter, &entry);
1033
1034         g_dbus_client_ref(client);
1035
1036         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
1037                 const char *interface;
1038
1039                 dbus_message_iter_get_basic(&entry, &interface);
1040                 proxy_remove(client, path, interface);
1041                 dbus_message_iter_next(&entry);
1042         }
1043
1044         g_dbus_client_unref(client);
1045
1046         return TRUE;
1047 }
1048
1049 static void parse_managed_objects(GDBusClient *client, DBusMessage *msg)
1050 {
1051         DBusMessageIter iter, dict;
1052
1053         if (dbus_message_iter_init(msg, &iter) == FALSE)
1054                 return;
1055
1056         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
1057                 return;
1058
1059         dbus_message_iter_recurse(&iter, &dict);
1060
1061         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1062                 DBusMessageIter entry;
1063                 const char *path;
1064
1065                 dbus_message_iter_recurse(&dict, &entry);
1066
1067                 if (dbus_message_iter_get_arg_type(&entry) !=
1068                                                         DBUS_TYPE_OBJECT_PATH)
1069                         break;
1070
1071                 dbus_message_iter_get_basic(&entry, &path);
1072                 dbus_message_iter_next(&entry);
1073
1074                 parse_interfaces(client, path, &entry);
1075
1076                 dbus_message_iter_next(&dict);
1077         }
1078 }
1079
1080 static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)
1081 {
1082         GDBusClient *client = user_data;
1083         DBusMessage *reply = dbus_pending_call_steal_reply(call);
1084         DBusError error;
1085
1086         g_dbus_client_ref(client);
1087
1088         dbus_error_init(&error);
1089
1090         if (dbus_set_error_from_message(&error, reply) == TRUE) {
1091                 dbus_error_free(&error);
1092                 goto done;
1093         }
1094
1095         parse_managed_objects(client, reply);
1096
1097 done:
1098         if (client->ready)
1099                 client->ready(client, client->ready_data);
1100
1101         dbus_message_unref(reply);
1102
1103         dbus_pending_call_unref(client->get_objects_call);
1104         client->get_objects_call = NULL;
1105
1106         g_dbus_client_unref(client);
1107 }
1108
1109 static void get_managed_objects(GDBusClient *client)
1110 {
1111         DBusMessage *msg;
1112
1113         if (!client->connected)
1114                 return;
1115
1116         if ((!client->proxy_added && !client->proxy_removed) ||
1117                                                         !client->root_path) {
1118                 refresh_properties(client);
1119                 return;
1120         }
1121
1122         if (client->get_objects_call != NULL)
1123                 return;
1124
1125         msg = dbus_message_new_method_call(client->service_name,
1126                                                 client->root_path,
1127                                                 DBUS_INTERFACE_OBJECT_MANAGER,
1128                                                 "GetManagedObjects");
1129         if (msg == NULL)
1130                 return;
1131
1132         dbus_message_append_args(msg, DBUS_TYPE_INVALID);
1133
1134         if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
1135                                 &client->get_objects_call, -1) == FALSE) {
1136                 dbus_message_unref(msg);
1137                 return;
1138         }
1139
1140         dbus_pending_call_set_notify(client->get_objects_call,
1141                                                 get_managed_objects_reply,
1142                                                 client, NULL);
1143
1144         dbus_message_unref(msg);
1145 }
1146
1147 static void service_connect(DBusConnection *conn, void *user_data)
1148 {
1149         GDBusClient *client = user_data;
1150
1151         g_dbus_client_ref(client);
1152
1153         client->connected = TRUE;
1154
1155         if (client->connect_func)
1156                 client->connect_func(conn, client->connect_data);
1157
1158         get_managed_objects(client);
1159
1160         g_dbus_client_unref(client);
1161 }
1162
1163 static void service_disconnect(DBusConnection *conn, void *user_data)
1164 {
1165         GDBusClient *client = user_data;
1166
1167         client->connected = FALSE;
1168
1169         g_list_free_full(client->proxy_list, proxy_free);
1170         client->proxy_list = NULL;
1171
1172         if (client->disconn_func)
1173                 client->disconn_func(conn, client->disconn_data);
1174 }
1175
1176 static DBusHandlerResult message_filter(DBusConnection *connection,
1177                                         DBusMessage *message, void *user_data)
1178 {
1179         GDBusClient *client = user_data;
1180         const char *sender, *path, *interface;
1181
1182         if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
1183                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1184
1185         sender = dbus_message_get_sender(message);
1186         if (sender == NULL)
1187                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1188
1189         path = dbus_message_get_path(message);
1190         interface = dbus_message_get_interface(message);
1191
1192         if (g_str_has_prefix(path, client->base_path) == FALSE)
1193                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1194
1195         if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
1196                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1197
1198         if (client->signal_func)
1199                 client->signal_func(connection, message, client->signal_data);
1200
1201         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1202 }
1203
1204 GDBusClient *g_dbus_client_new(DBusConnection *connection,
1205                                         const char *service, const char *path)
1206 {
1207         return g_dbus_client_new_full(connection, service, path, "/");
1208 }
1209
1210 GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
1211                                                         const char *service,
1212                                                         const char *path,
1213                                                         const char *root_path)
1214 {
1215         GDBusClient *client;
1216         unsigned int i;
1217
1218         if (!connection || !service)
1219                 return NULL;
1220
1221         client = g_try_new0(GDBusClient, 1);
1222         if (client == NULL)
1223                 return NULL;
1224
1225         if (dbus_connection_add_filter(connection, message_filter,
1226                                                 client, NULL) == FALSE) {
1227                 g_free(client);
1228                 return NULL;
1229         }
1230
1231         client->dbus_conn = dbus_connection_ref(connection);
1232         client->service_name = g_strdup(service);
1233         client->base_path = g_strdup(path);
1234         client->root_path = g_strdup(root_path);
1235         client->connected = FALSE;
1236
1237         client->match_rules = g_ptr_array_sized_new(1);
1238         g_ptr_array_set_free_func(client->match_rules, g_free);
1239
1240         client->watch = g_dbus_add_service_watch(connection, service,
1241                                                 service_connect,
1242                                                 service_disconnect,
1243                                                 client, NULL);
1244
1245         if (!root_path)
1246                 return g_dbus_client_ref(client);
1247
1248         client->added_watch = g_dbus_add_signal_watch(connection, service,
1249                                                 client->root_path,
1250                                                 DBUS_INTERFACE_OBJECT_MANAGER,
1251                                                 "InterfacesAdded",
1252                                                 interfaces_added,
1253                                                 client, NULL);
1254         client->removed_watch = g_dbus_add_signal_watch(connection, service,
1255                                                 client->root_path,
1256                                                 DBUS_INTERFACE_OBJECT_MANAGER,
1257                                                 "InterfacesRemoved",
1258                                                 interfaces_removed,
1259                                                 client, NULL);
1260         g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
1261                                 "sender='%s',path_namespace='%s'",
1262                                 client->service_name, client->base_path));
1263
1264         for (i = 0; i < client->match_rules->len; i++) {
1265                 modify_match(client->dbus_conn, "AddMatch",
1266                                 g_ptr_array_index(client->match_rules, i));
1267         }
1268
1269         return g_dbus_client_ref(client);
1270 }
1271
1272 GDBusClient *g_dbus_client_ref(GDBusClient *client)
1273 {
1274         if (client == NULL)
1275                 return NULL;
1276
1277         __sync_fetch_and_add(&client->ref_count, 1);
1278
1279         return client;
1280 }
1281
1282 void g_dbus_client_unref(GDBusClient *client)
1283 {
1284         unsigned int i;
1285
1286         if (client == NULL)
1287                 return;
1288
1289         if (__sync_sub_and_fetch(&client->ref_count, 1) > 0)
1290                 return;
1291
1292         if (client->pending_call != NULL) {
1293                 dbus_pending_call_cancel(client->pending_call);
1294                 dbus_pending_call_unref(client->pending_call);
1295         }
1296
1297         if (client->get_objects_call != NULL) {
1298                 dbus_pending_call_cancel(client->get_objects_call);
1299                 dbus_pending_call_unref(client->get_objects_call);
1300         }
1301
1302         for (i = 0; i < client->match_rules->len; i++) {
1303                 modify_match(client->dbus_conn, "RemoveMatch",
1304                                 g_ptr_array_index(client->match_rules, i));
1305         }
1306
1307         g_ptr_array_free(client->match_rules, TRUE);
1308
1309         dbus_connection_remove_filter(client->dbus_conn,
1310                                                 message_filter, client);
1311
1312         g_list_free_full(client->proxy_list, proxy_free);
1313
1314         /*
1315          * Don't call disconn_func twice if disconnection
1316          * was previously reported.
1317          */
1318         if (client->disconn_func && client->connected)
1319                 client->disconn_func(client->dbus_conn, client->disconn_data);
1320
1321         g_dbus_remove_watch(client->dbus_conn, client->watch);
1322         g_dbus_remove_watch(client->dbus_conn, client->added_watch);
1323         g_dbus_remove_watch(client->dbus_conn, client->removed_watch);
1324
1325         dbus_connection_unref(client->dbus_conn);
1326
1327         g_free(client->service_name);
1328         g_free(client->base_path);
1329         g_free(client->root_path);
1330
1331         g_free(client);
1332 }
1333
1334 gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
1335                                 GDBusWatchFunction function, void *user_data)
1336 {
1337         if (client == NULL)
1338                 return FALSE;
1339
1340         client->connect_func = function;
1341         client->connect_data = user_data;
1342
1343         return TRUE;
1344 }
1345
1346 gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
1347                                 GDBusWatchFunction function, void *user_data)
1348 {
1349         if (client == NULL)
1350                 return FALSE;
1351
1352         client->disconn_func = function;
1353         client->disconn_data = user_data;
1354
1355         return TRUE;
1356 }
1357
1358 gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
1359                                 GDBusMessageFunction function, void *user_data)
1360 {
1361         if (client == NULL)
1362                 return FALSE;
1363
1364         client->signal_func = function;
1365         client->signal_data = user_data;
1366
1367         return TRUE;
1368 }
1369
1370 gboolean g_dbus_client_set_ready_watch(GDBusClient *client,
1371                                 GDBusClientFunction ready, void *user_data)
1372 {
1373         if (client == NULL)
1374                 return FALSE;
1375
1376         client->ready = ready;
1377         client->ready_data = user_data;
1378
1379         return TRUE;
1380 }
1381
1382 gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
1383                                         GDBusProxyFunction proxy_added,
1384                                         GDBusProxyFunction proxy_removed,
1385                                         GDBusPropertyFunction property_changed,
1386                                         void *user_data)
1387 {
1388         if (client == NULL)
1389                 return FALSE;
1390
1391         client->proxy_added = proxy_added;
1392         client->proxy_removed = proxy_removed;
1393         client->property_changed = property_changed;
1394         client->user_data = user_data;
1395
1396         if (proxy_added || proxy_removed || property_changed)
1397                 get_managed_objects(client);
1398
1399         return TRUE;
1400 }