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