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