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