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