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