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