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