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