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