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