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