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