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