gdbus: Add properties into Introspectable interface
[platform/upstream/connman.git] / gdbus / object.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 <string.h>
30
31 #include <glib.h>
32 #include <dbus/dbus.h>
33
34 #include "gdbus.h"
35
36 #define info(fmt...)
37 #define error(fmt...)
38 #define debug(fmt...)
39
40 struct generic_data {
41         unsigned int refcount;
42         GSList *interfaces;
43         char *introspect;
44 };
45
46 struct interface_data {
47         char *name;
48         const GDBusMethodTable *methods;
49         const GDBusSignalTable *signals;
50         const GDBusPropertyTable *properties;
51         void *user_data;
52         GDBusDestroyFunction destroy;
53 };
54
55 struct security_data {
56         GDBusPendingReply pending;
57         DBusMessage *message;
58         const GDBusMethodTable *method;
59         void *iface_user_data;
60 };
61
62 struct property_data {
63         GDBusPendingPropertySet id;
64         DBusMessage *message;
65 };
66
67 static void print_arguments(GString *gstr, const GDBusArgInfo *args,
68                                                 const char *direction)
69 {
70         for (; args && args->name; args++) {
71                 g_string_append_printf(gstr,
72                                         "\t\t\t<arg name=\"%s\" type=\"%s\"",
73                                         args->name, args->signature);
74
75                 if (direction)
76                         g_string_append_printf(gstr,
77                                         " direction=\"%s\"/>\n", direction);
78                 else
79                         g_string_append_printf(gstr, "/>\n");
80
81         }
82 }
83
84 #define G_DBUS_ANNOTATE(prefix_, name_, value_)                         \
85         prefix_ "<annotation name=\"org.freedesktop.DBus." name_ "\" "  \
86         "value=\"" value_ "\"/>\n"
87
88 #define G_DBUS_ANNOTATE_DEPRECATED(prefix_) \
89         G_DBUS_ANNOTATE(prefix_, "Deprecated", "true")
90
91 #define G_DBUS_ANNOTATE_NOREPLY(prefix_) \
92         G_DBUS_ANNOTATE(prefix_, "Method.NoReply", "true")
93
94 static void generate_interface_xml(GString *gstr, struct interface_data *iface)
95 {
96         const GDBusMethodTable *method;
97         const GDBusSignalTable *signal;
98         const GDBusPropertyTable *property;
99
100         for (method = iface->methods; method && method->name; method++) {
101                 gboolean deprecated = method->flags &
102                                                 G_DBUS_METHOD_FLAG_DEPRECATED;
103                 gboolean noreply = method->flags &
104                                                 G_DBUS_METHOD_FLAG_NOREPLY;
105
106                 if (!deprecated && !noreply &&
107                                 !(method->in_args && method->in_args->name) &&
108                                 !(method->out_args && method->out_args->name))
109                         g_string_append_printf(gstr,
110                                                 "\t\t<method name=\"%s\"/>\n",
111                                                 method->name);
112                 else {
113                         g_string_append_printf(gstr,
114                                                 "\t\t<method name=\"%s\">\n",
115                                                 method->name);
116                         print_arguments(gstr, method->in_args, "in");
117                         print_arguments(gstr, method->out_args, "out");
118
119                         if (deprecated)
120                                 g_string_append_printf(gstr,
121                                         G_DBUS_ANNOTATE_DEPRECATED("\t\t\t"));
122                         if (noreply)
123                                 g_string_append_printf(gstr,
124                                         G_DBUS_ANNOTATE_NOREPLY("\t\t\t"));
125
126                         g_string_append_printf(gstr, "\t\t</method>\n");
127                 }
128         }
129
130         for (signal = iface->signals; signal && signal->name; signal++) {
131                 gboolean deprecated = signal->flags &
132                                                 G_DBUS_SIGNAL_FLAG_DEPRECATED;
133
134                 if (!deprecated && !(signal->args && signal->args->name))
135                         g_string_append_printf(gstr,
136                                                 "\t\t<signal name=\"%s\"/>\n",
137                                                 signal->name);
138                 else {
139                         g_string_append_printf(gstr,
140                                                 "\t\t<signal name=\"%s\">\n",
141                                                 signal->name);
142                         print_arguments(gstr, signal->args, NULL);
143
144                         if (deprecated)
145                                 g_string_append_printf(gstr,
146                                         G_DBUS_ANNOTATE_DEPRECATED("\t\t\t"));
147
148                         g_string_append_printf(gstr, "\t\t</signal>\n");
149                 }
150         }
151
152         for (property = iface->properties; property && property->name;
153                                                                 property++) {
154                 gboolean deprecated = property->flags &
155                                         G_DBUS_PROPERTY_FLAG_DEPRECATED;
156
157                 g_string_append_printf(gstr, "\t\t<property name=\"%s\""
158                                         " type=\"%s\" access=\"%s%s\"",
159                                         property->name, property->type,
160                                         property->get ? "read" : "",
161                                         property->set ? "write" : "");
162
163                 if (!deprecated)
164                         g_string_append_printf(gstr, "/>\n");
165                 else
166                         g_string_append_printf(gstr,
167                                 G_DBUS_ANNOTATE_DEPRECATED(">\n\t\t\t"));
168         }
169 }
170
171 static void generate_introspection_xml(DBusConnection *conn,
172                                 struct generic_data *data, const char *path)
173 {
174         GSList *list;
175         GString *gstr;
176         char **children;
177         int i;
178
179         g_free(data->introspect);
180
181         gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
182
183         g_string_append_printf(gstr, "<node>\n");
184
185         for (list = data->interfaces; list; list = list->next) {
186                 struct interface_data *iface = list->data;
187
188                 g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
189                                                                 iface->name);
190
191                 generate_interface_xml(gstr, iface);
192
193                 g_string_append_printf(gstr, "\t</interface>\n");
194         }
195
196         if (!dbus_connection_list_registered(conn, path, &children))
197                 goto done;
198
199         for (i = 0; children[i]; i++)
200                 g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
201                                                                 children[i]);
202
203         dbus_free_string_array(children);
204
205 done:
206         g_string_append_printf(gstr, "</node>\n");
207
208         data->introspect = g_string_free(gstr, FALSE);
209 }
210
211 static DBusMessage *introspect(DBusConnection *connection,
212                                 DBusMessage *message, void *user_data)
213 {
214         struct generic_data *data = user_data;
215         DBusMessage *reply;
216
217         if (data->introspect == NULL)
218                 generate_introspection_xml(connection, data,
219                                                 dbus_message_get_path(message));
220
221         reply = dbus_message_new_method_return(message);
222         if (reply == NULL)
223                 return NULL;
224
225         dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
226                                         DBUS_TYPE_INVALID);
227
228         return reply;
229 }
230
231 static DBusHandlerResult process_message(DBusConnection *connection,
232                         DBusMessage *message, const GDBusMethodTable *method,
233                                                         void *iface_user_data)
234 {
235         DBusMessage *reply;
236
237         reply = method->function(connection, message, iface_user_data);
238
239         if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
240                 if (reply != NULL)
241                         dbus_message_unref(reply);
242                 return DBUS_HANDLER_RESULT_HANDLED;
243         }
244
245         if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
246                 if (reply == NULL)
247                         return DBUS_HANDLER_RESULT_HANDLED;
248         }
249
250         if (reply == NULL)
251                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
252
253         dbus_connection_send(connection, reply, NULL);
254         dbus_message_unref(reply);
255
256         return DBUS_HANDLER_RESULT_HANDLED;
257 }
258
259 static GDBusPendingReply next_pending = 1;
260 static GSList *pending_security = NULL;
261
262 static const GDBusSecurityTable *security_table = NULL;
263
264 void g_dbus_pending_success(DBusConnection *connection,
265                                         GDBusPendingReply pending)
266 {
267         GSList *list;
268
269         for (list = pending_security; list; list = list->next) {
270                 struct security_data *secdata = list->data;
271
272                 if (secdata->pending != pending)
273                         continue;
274
275                 pending_security = g_slist_remove(pending_security, secdata);
276
277                 process_message(connection, secdata->message,
278                                 secdata->method, secdata->iface_user_data);
279
280                 dbus_message_unref(secdata->message);
281                 g_free(secdata);
282                 return;
283         }
284 }
285
286 void g_dbus_pending_error_valist(DBusConnection *connection,
287                                 GDBusPendingReply pending, const char *name,
288                                         const char *format, va_list args)
289 {
290         GSList *list;
291
292         for (list = pending_security; list; list = list->next) {
293                 struct security_data *secdata = list->data;
294                 DBusMessage *reply;
295
296                 if (secdata->pending != pending)
297                         continue;
298
299                 pending_security = g_slist_remove(pending_security, secdata);
300
301                 reply = g_dbus_create_error_valist(secdata->message,
302                                                         name, format, args);
303                 if (reply != NULL) {
304                         dbus_connection_send(connection, reply, NULL);
305                         dbus_message_unref(reply);
306                 }
307
308                 dbus_message_unref(secdata->message);
309                 g_free(secdata);
310                 return;
311         }
312 }
313
314 void g_dbus_pending_error(DBusConnection *connection,
315                                 GDBusPendingReply pending,
316                                 const char *name, const char *format, ...)
317 {
318         va_list args;
319
320         va_start(args, format);
321
322         g_dbus_pending_error_valist(connection, pending, name, format, args);
323
324         va_end(args);
325 }
326
327 int polkit_check_authorization(DBusConnection *conn,
328                                 const char *action, gboolean interaction,
329                                 void (*function) (dbus_bool_t authorized,
330                                                         void *user_data),
331                                                 void *user_data, int timeout);
332
333 struct builtin_security_data {
334         DBusConnection *conn;
335         GDBusPendingReply pending;
336 };
337
338 static void builtin_security_result(dbus_bool_t authorized, void *user_data)
339 {
340         struct builtin_security_data *data = user_data;
341
342         if (authorized == TRUE)
343                 g_dbus_pending_success(data->conn, data->pending);
344         else
345                 g_dbus_pending_error(data->conn, data->pending,
346                                                 DBUS_ERROR_AUTH_FAILED, NULL);
347
348         g_free(data);
349 }
350
351 static void builtin_security_function(DBusConnection *conn,
352                                                 const char *action,
353                                                 gboolean interaction,
354                                                 GDBusPendingReply pending)
355 {
356         struct builtin_security_data *data;
357
358         data = g_new0(struct builtin_security_data, 1);
359         data->conn = conn;
360         data->pending = pending;
361
362         if (polkit_check_authorization(conn, action, interaction,
363                                 builtin_security_result, data, 30000) < 0)
364                 g_dbus_pending_error(conn, pending, NULL, NULL);
365 }
366
367 static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
368                         const GDBusMethodTable *method, void *iface_user_data)
369 {
370         const GDBusSecurityTable *security;
371
372         for (security = security_table; security && security->privilege;
373                                                                 security++) {
374                 struct security_data *secdata;
375                 gboolean interaction;
376
377                 if (security->privilege != method->privilege)
378                         continue;
379
380                 secdata = g_new(struct security_data, 1);
381                 secdata->pending = next_pending++;
382                 secdata->message = dbus_message_ref(msg);
383                 secdata->method = method;
384                 secdata->iface_user_data = iface_user_data;
385
386                 pending_security = g_slist_prepend(pending_security, secdata);
387
388                 if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
389                         interaction = TRUE;
390                 else
391                         interaction = FALSE;
392
393                 if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
394                                                         security->function)
395                         security->function(conn, security->action,
396                                                 interaction, secdata->pending);
397                 else
398                         builtin_security_function(conn, security->action,
399                                                 interaction, secdata->pending);
400
401                 return TRUE;
402         }
403
404         return FALSE;
405 }
406
407 static GDBusPendingPropertySet next_pending_property = 1;
408 static GSList *pending_property_set;
409
410 static struct property_data *remove_pending_property_data(
411                                                 GDBusPendingPropertySet id)
412 {
413         struct property_data *propdata;
414         GSList *l;
415
416         for (l = pending_property_set; l != NULL; l = l->next) {
417                 propdata = l->data;
418                 if (propdata->id != id)
419                         continue;
420         }
421
422         if (l == NULL)
423                 return NULL;
424
425         pending_property_set = g_slist_delete_link(pending_property_set, l);
426
427         return propdata;
428 }
429
430 void g_dbus_pending_property_success(DBusConnection *connection,
431                                                 GDBusPendingPropertySet id)
432 {
433         struct property_data *propdata;
434
435         propdata = remove_pending_property_data(id);
436         if (propdata == NULL)
437                 return;
438
439         g_dbus_send_reply(connection, propdata->message, DBUS_TYPE_INVALID);
440         dbus_message_unref(propdata->message);
441         g_free(propdata);
442 }
443
444 void g_dbus_pending_property_error_valist(DBusConnection *connection,
445                                         GDBusPendingReply id, const char *name,
446                                         const char *format, va_list args)
447 {
448         struct property_data *propdata;
449         DBusMessage *reply;
450
451         propdata = remove_pending_property_data(id);
452         if (propdata == NULL)
453                 return;
454
455         reply = g_dbus_create_error_valist(propdata->message, name, format,
456                                                                         args);
457         if (reply != NULL) {
458                 dbus_connection_send(connection, reply, NULL);
459                 dbus_message_unref(reply);
460         }
461
462         dbus_message_unref(propdata->message);
463         g_free(propdata);
464 }
465
466 void g_dbus_pending_property_error(DBusConnection *connection,
467                                         GDBusPendingReply id, const char *name,
468                                         const char *format, ...)
469 {
470         va_list args;
471
472         va_start(args, format);
473
474         g_dbus_pending_property_error_valist(connection, id, name, format,
475                                                                         args);
476
477         va_end(args);
478 }
479
480 static void generic_unregister(DBusConnection *connection, void *user_data)
481 {
482         struct generic_data *data = user_data;
483
484         g_free(data->introspect);
485         g_free(data);
486 }
487
488 static struct interface_data *find_interface(GSList *interfaces,
489                                                 const char *name)
490 {
491         GSList *list;
492
493         if (name == NULL)
494                 return NULL;
495
496         for (list = interfaces; list; list = list->next) {
497                 struct interface_data *iface = list->data;
498                 if (!strcmp(name, iface->name))
499                         return iface;
500         }
501
502         return NULL;
503 }
504
505 static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
506                                                         DBusMessage *message)
507 {
508         const char *sig = dbus_message_get_signature(message);
509         const char *p = NULL;
510
511         for (; args && args->signature && *sig; args++) {
512                 p = args->signature;
513
514                 for (; *sig && *p; sig++, p++) {
515                         if (*p != *sig)
516                                 return FALSE;
517                 }
518         }
519
520         if (*sig || (p && *p) || (args && args->signature))
521                 return FALSE;
522
523         return TRUE;
524 }
525
526 static DBusHandlerResult generic_message(DBusConnection *connection,
527                                         DBusMessage *message, void *user_data)
528 {
529         struct generic_data *data = user_data;
530         struct interface_data *iface;
531         const GDBusMethodTable *method;
532         const char *interface;
533
534         interface = dbus_message_get_interface(message);
535
536         iface = find_interface(data->interfaces, interface);
537         if (iface == NULL)
538                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
539
540         for (method = iface->methods; method &&
541                         method->name && method->function; method++) {
542                 if (dbus_message_is_method_call(message, iface->name,
543                                                         method->name) == FALSE)
544                         continue;
545
546                 if (g_dbus_args_have_signature(method->in_args,
547                                                         message) == FALSE)
548                         continue;
549
550                 if (check_privilege(connection, message, method,
551                                                 iface->user_data) == TRUE)
552                         return DBUS_HANDLER_RESULT_HANDLED;
553
554                 return process_message(connection, message, method,
555                                                         iface->user_data);
556         }
557
558         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
559 }
560
561 static DBusObjectPathVTable generic_table = {
562         .unregister_function    = generic_unregister,
563         .message_function       = generic_message,
564 };
565
566 static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
567 {
568         struct generic_data *data = NULL;
569         char *parent_path, *slash;
570
571         parent_path = g_strdup(child_path);
572         slash = strrchr(parent_path, '/');
573         if (slash == NULL)
574                 goto done;
575
576         if (slash == parent_path && parent_path[1] != '\0')
577                 parent_path[1] = '\0';
578         else
579                 *slash = '\0';
580
581         if (!strlen(parent_path))
582                 goto done;
583
584         if (dbus_connection_get_object_path_data(conn, parent_path,
585                                                         (void *) &data) == FALSE) {
586                 goto done;
587         }
588
589         invalidate_parent_data(conn, parent_path);
590
591         if (data == NULL)
592                 goto done;
593
594         g_free(data->introspect);
595         data->introspect = NULL;
596
597 done:
598         g_free(parent_path);
599 }
600
601 static const GDBusMethodTable introspect_methods[] = {
602         { GDBUS_METHOD("Introspect", NULL,
603                         GDBUS_ARGS({ "xml", "s" }), introspect) },
604         { }
605 };
606
607 static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties,
608                                                         const char *name)
609 {
610         const GDBusPropertyTable *p;
611
612         for (p = properties; p && p->name; p++) {
613                 if (strcmp(name, p->name) == 0)
614                         return p;
615         }
616
617         return NULL;
618 }
619
620 static DBusMessage *properties_get(DBusConnection *connection,
621                                         DBusMessage *message, void *user_data)
622 {
623         struct generic_data *data = user_data;
624         struct interface_data *iface;
625         const GDBusPropertyTable *property;
626         const char *interface, *name;
627         DBusMessageIter iter, value;
628         DBusMessage *reply;
629
630         if (!dbus_message_get_args(message, NULL,
631                                         DBUS_TYPE_STRING, &interface,
632                                         DBUS_TYPE_STRING, &name,
633                                         DBUS_TYPE_INVALID))
634                 return NULL;
635
636         iface = find_interface(data->interfaces, interface);
637         if (iface == NULL)
638                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
639                                 "No such interface '%s'", interface);
640
641         property = find_property(iface->properties, name);
642         if (property == NULL)
643                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
644                                 "No such property '%s'", name);
645
646         if (property->exists != NULL &&
647                         !property->exists(property, iface->user_data))
648                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
649                                         "No such property '%s'", name);
650
651         if (property->get == NULL)
652                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
653                                 "Property '%s' is not readable", name);
654
655         reply = dbus_message_new_method_return(message);
656         if (reply == NULL)
657                 return NULL;
658
659         dbus_message_iter_init_append(reply, &iter);
660         dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
661                                                 property->type, &value);
662
663         if (!property->get(property, &value, iface->user_data)) {
664                 dbus_message_unref(reply);
665                 return NULL;
666         }
667
668         dbus_message_iter_close_container(&iter, &value);
669
670         return reply;
671 }
672
673 static DBusMessage *properties_get_all(DBusConnection *connection,
674                                         DBusMessage *message, void *user_data)
675 {
676         struct generic_data *data = user_data;
677         struct interface_data *iface;
678         const GDBusPropertyTable *p;
679         const char *interface;
680         DBusMessageIter iter, dict;
681         DBusMessage *reply;
682
683         if (!dbus_message_get_args(message, NULL,
684                                         DBUS_TYPE_STRING, &interface,
685                                         DBUS_TYPE_INVALID))
686                 return NULL;
687
688         iface = find_interface(data->interfaces, interface);
689         if (iface == NULL)
690                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
691                                         "No such interface '%s'", interface);
692
693         reply = dbus_message_new_method_return(message);
694         if (reply == NULL)
695                 return NULL;
696
697         dbus_message_iter_init_append(reply, &iter);
698         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
699                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
700                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
701                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
702
703         for (p = iface->properties; p && p->name; p++) {
704                 DBusMessageIter entry, value;
705
706                 if (p->get == NULL)
707                         continue;
708
709                 if (p->exists != NULL && !p->exists(p, iface->user_data))
710                         continue;
711
712                 dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
713                                                                 NULL, &entry);
714                 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
715                                                                 p->name);
716                 dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
717                                                         p->type, &value);
718
719                 if (!p->get(p, &value, iface->user_data)) {
720                         dbus_message_unref(reply);
721                         return NULL;
722                 }
723
724                 dbus_message_iter_close_container(&entry, &value);
725                 dbus_message_iter_close_container(&dict, &entry);
726         }
727
728         dbus_message_iter_close_container(&iter, &dict);
729
730         return reply;
731 }
732
733 static DBusMessage *properties_set(DBusConnection *connection,
734                                         DBusMessage *message, void *user_data)
735 {
736         struct generic_data *data = user_data;
737         DBusMessageIter iter, sub;
738         struct interface_data *iface;
739         const GDBusPropertyTable *property;
740         const char *name, *interface;
741         struct property_data *propdata;
742
743         if (!dbus_message_iter_init(message, &iter))
744                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
745                                                         "No arguments given");
746
747         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
748                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
749                                         "Invalid argument type: '%c'",
750                                         dbus_message_iter_get_arg_type(&iter));
751
752         dbus_message_iter_get_basic(&iter, &name);
753         dbus_message_iter_next(&iter);
754
755         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
756                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
757                                         "Invalid argument type: '%c'",
758                                         dbus_message_iter_get_arg_type(&iter));
759
760         dbus_message_iter_get_basic(&iter, &interface);
761         dbus_message_iter_next(&iter);
762
763         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
764                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
765                                         "Invalid argument type: '%c'",
766                                         dbus_message_iter_get_arg_type(&iter));
767
768         dbus_message_iter_recurse(&iter, &sub);
769
770         iface = find_interface(data->interfaces, interface);
771         if (iface == NULL)
772                 return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
773                                         "No such interface '%s'", interface);
774
775         property = find_property(iface->properties, name);
776         if (property == NULL)
777                 return g_dbus_create_error(message,
778                                                 DBUS_ERROR_UNKNOWN_PROPERTY,
779                                                 "No such property '%s'", name);
780
781         if (property->set == NULL)
782                 return g_dbus_create_error(message,
783                                         DBUS_ERROR_PROPERTY_READ_ONLY,
784                                         "Property '%s' is not writable", name);
785
786         if (property->exists != NULL &&
787                         !property->exists(property, iface->user_data))
788                 return g_dbus_create_error(message,
789                                                 DBUS_ERROR_UNKNOWN_PROPERTY,
790                                                 "No such property '%s'", name);
791
792         propdata = g_new(struct property_data, 1);
793         propdata->id = next_pending_property++;
794         propdata->message = dbus_message_ref(message);
795
796         property->set(property, &sub, propdata->id, iface->user_data);
797
798         return NULL;
799 }
800
801 static const GDBusMethodTable properties_methods[] = {
802         { GDBUS_METHOD("Get",
803                         GDBUS_ARGS({ "interface", "s" }, { "name", "s" }),
804                         GDBUS_ARGS({ "value", "v" }),
805                         properties_get) },
806         { GDBUS_ASYNC_METHOD("Set", NULL,
807                         GDBUS_ARGS({ "interface", "s" }, { "name", "s" },
808                                                         { "value", "v" }),
809                         properties_set) },
810         { GDBUS_METHOD("GetAll",
811                         GDBUS_ARGS({ "interface", "s" }),
812                         GDBUS_ARGS({ "properties", "a{sv}" }),
813                         properties_get_all) },
814         { }
815 };
816
817 static const GDBusSignalTable properties_signals[] = {
818         { GDBUS_SIGNAL("PropertiesChanged",
819                         GDBUS_ARGS({ "interface", "s" },
820                                         { "changed_properties", "a{sv}" },
821                                         { "invalidated_properties", "as"})) },
822         { }
823 };
824
825 static void add_interface(struct generic_data *data, const char *name,
826                                 const GDBusMethodTable *methods,
827                                 const GDBusSignalTable *signals,
828                                 const GDBusPropertyTable *properties,
829                                 void *user_data,
830                                 GDBusDestroyFunction destroy)
831 {
832         struct interface_data *iface;
833
834         iface = g_new0(struct interface_data, 1);
835         iface->name = g_strdup(name);
836         iface->methods = methods;
837         iface->signals = signals;
838         iface->properties = properties;
839         iface->user_data = user_data;
840         iface->destroy = destroy;
841
842         data->interfaces = g_slist_append(data->interfaces, iface);
843 }
844
845 static struct generic_data *object_path_ref(DBusConnection *connection,
846                                                         const char *path)
847 {
848         struct generic_data *data;
849
850         if (dbus_connection_get_object_path_data(connection, path,
851                                                 (void *) &data) == TRUE) {
852                 if (data != NULL) {
853                         data->refcount++;
854                         return data;
855                 }
856         }
857
858         data = g_new0(struct generic_data, 1);
859         data->refcount = 1;
860
861         data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
862
863         if (!dbus_connection_register_object_path(connection, path,
864                                                 &generic_table, data)) {
865                 g_free(data->introspect);
866                 g_free(data);
867                 return NULL;
868         }
869
870         invalidate_parent_data(connection, path);
871
872         add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
873                         introspect_methods, NULL, NULL, data, NULL);
874
875         add_interface(data, DBUS_INTERFACE_PROPERTIES, properties_methods,
876                                         properties_signals, NULL, data, NULL);
877
878         return data;
879 }
880
881 static gboolean remove_interface(struct generic_data *data, const char *name)
882 {
883         struct interface_data *iface;
884
885         iface = find_interface(data->interfaces, name);
886         if (iface == NULL)
887                 return FALSE;
888
889         data->interfaces = g_slist_remove(data->interfaces, iface);
890
891         if (iface->destroy)
892                 iface->destroy(iface->user_data);
893
894         g_free(iface->name);
895         g_free(iface);
896
897         return TRUE;
898 }
899
900 static void object_path_unref(DBusConnection *connection, const char *path)
901 {
902         struct generic_data *data = NULL;
903
904         if (dbus_connection_get_object_path_data(connection, path,
905                                                 (void *) &data) == FALSE)
906                 return;
907
908         if (data == NULL)
909                 return;
910
911         data->refcount--;
912
913         if (data->refcount > 0)
914                 return;
915
916         remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
917         remove_interface(data, DBUS_INTERFACE_PROPERTIES);
918
919         invalidate_parent_data(connection, path);
920
921         dbus_connection_unregister_object_path(connection, path);
922 }
923
924 static gboolean check_signal(DBusConnection *conn, const char *path,
925                                 const char *interface, const char *name,
926                                 const GDBusArgInfo **args)
927 {
928         struct generic_data *data = NULL;
929         struct interface_data *iface;
930         const GDBusSignalTable *signal;
931
932         *args = NULL;
933         if (!dbus_connection_get_object_path_data(conn, path,
934                                         (void *) &data) || data == NULL) {
935                 error("dbus_connection_emit_signal: path %s isn't registered",
936                                 path);
937                 return FALSE;
938         }
939
940         iface = find_interface(data->interfaces, interface);
941         if (iface == NULL) {
942                 error("dbus_connection_emit_signal: %s does not implement %s",
943                                 path, interface);
944                 return FALSE;
945         }
946
947         for (signal = iface->signals; signal && signal->name; signal++) {
948                 if (!strcmp(signal->name, name)) {
949                         *args = signal->args;
950                         return TRUE;
951                 }
952         }
953
954         error("No signal named %s on interface %s", name, interface);
955         return FALSE;
956 }
957
958 static dbus_bool_t emit_signal_valist(DBusConnection *conn,
959                                                 const char *path,
960                                                 const char *interface,
961                                                 const char *name,
962                                                 int first,
963                                                 va_list var_args)
964 {
965         DBusMessage *signal;
966         dbus_bool_t ret;
967         const GDBusArgInfo *args;
968
969         if (!check_signal(conn, path, interface, name, &args))
970                 return FALSE;
971
972         signal = dbus_message_new_signal(path, interface, name);
973         if (signal == NULL) {
974                 error("Unable to allocate new %s.%s signal", interface,  name);
975                 return FALSE;
976         }
977
978         ret = dbus_message_append_args_valist(signal, first, var_args);
979         if (!ret)
980                 goto fail;
981
982         if (g_dbus_args_have_signature(args, signal) == FALSE) {
983                 error("%s.%s: got unexpected signature '%s'", interface, name,
984                                         dbus_message_get_signature(signal));
985                 ret = FALSE;
986                 goto fail;
987         }
988
989         ret = dbus_connection_send(conn, signal, NULL);
990
991 fail:
992         dbus_message_unref(signal);
993
994         return ret;
995 }
996
997 gboolean g_dbus_register_interface(DBusConnection *connection,
998                                         const char *path, const char *name,
999                                         const GDBusMethodTable *methods,
1000                                         const GDBusSignalTable *signals,
1001                                         const GDBusPropertyTable *properties,
1002                                         void *user_data,
1003                                         GDBusDestroyFunction destroy)
1004 {
1005         struct generic_data *data;
1006
1007         data = object_path_ref(connection, path);
1008         if (data == NULL)
1009                 return FALSE;
1010
1011         if (find_interface(data->interfaces, name)) {
1012                 object_path_unref(connection, path);
1013                 return FALSE;
1014         }
1015
1016         add_interface(data, name, methods, signals,
1017                         properties, user_data, destroy);
1018
1019         g_free(data->introspect);
1020         data->introspect = NULL;
1021
1022         return TRUE;
1023 }
1024
1025 gboolean g_dbus_unregister_interface(DBusConnection *connection,
1026                                         const char *path, const char *name)
1027 {
1028         struct generic_data *data = NULL;
1029
1030         if (path == NULL)
1031                 return FALSE;
1032
1033         if (dbus_connection_get_object_path_data(connection, path,
1034                                                 (void *) &data) == FALSE)
1035                 return FALSE;
1036
1037         if (data == NULL)
1038                 return FALSE;
1039
1040         if (remove_interface(data, name) == FALSE)
1041                 return FALSE;
1042
1043         g_free(data->introspect);
1044         data->introspect = NULL;
1045
1046         object_path_unref(connection, path);
1047
1048         return TRUE;
1049 }
1050
1051 gboolean g_dbus_register_security(const GDBusSecurityTable *security)
1052 {
1053         if (security_table != NULL)
1054                 return FALSE;
1055
1056         security_table = security;
1057
1058         return TRUE;
1059 }
1060
1061 gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
1062 {
1063         security_table = NULL;
1064
1065         return TRUE;
1066 }
1067
1068 DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
1069                                         const char *format, va_list args)
1070 {
1071         char str[1024];
1072
1073         vsnprintf(str, sizeof(str), format, args);
1074
1075         return dbus_message_new_error(message, name, str);
1076 }
1077
1078 DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
1079                                                 const char *format, ...)
1080 {
1081         va_list args;
1082         DBusMessage *reply;
1083
1084         va_start(args, format);
1085
1086         reply = g_dbus_create_error_valist(message, name, format, args);
1087
1088         va_end(args);
1089
1090         return reply;
1091 }
1092
1093 DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
1094                                                 int type, va_list args)
1095 {
1096         DBusMessage *reply;
1097
1098         reply = dbus_message_new_method_return(message);
1099         if (reply == NULL)
1100                 return NULL;
1101
1102         if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
1103                 dbus_message_unref(reply);
1104                 return NULL;
1105         }
1106
1107         return reply;
1108 }
1109
1110 DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
1111 {
1112         va_list args;
1113         DBusMessage *reply;
1114
1115         va_start(args, type);
1116
1117         reply = g_dbus_create_reply_valist(message, type, args);
1118
1119         va_end(args);
1120
1121         return reply;
1122 }
1123
1124 gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
1125 {
1126         dbus_bool_t result;
1127
1128         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
1129                 dbus_message_set_no_reply(message, TRUE);
1130
1131         result = dbus_connection_send(connection, message, NULL);
1132
1133         dbus_message_unref(message);
1134
1135         return result;
1136 }
1137
1138 gboolean g_dbus_send_reply_valist(DBusConnection *connection,
1139                                 DBusMessage *message, int type, va_list args)
1140 {
1141         DBusMessage *reply;
1142
1143         reply = dbus_message_new_method_return(message);
1144         if (reply == NULL)
1145                 return FALSE;
1146
1147         if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
1148                 dbus_message_unref(reply);
1149                 return FALSE;
1150         }
1151
1152         return g_dbus_send_message(connection, reply);
1153 }
1154
1155 gboolean g_dbus_send_reply(DBusConnection *connection,
1156                                 DBusMessage *message, int type, ...)
1157 {
1158         va_list args;
1159         gboolean result;
1160
1161         va_start(args, type);
1162
1163         result = g_dbus_send_reply_valist(connection, message, type, args);
1164
1165         va_end(args);
1166
1167         return result;
1168 }
1169
1170 gboolean g_dbus_emit_signal(DBusConnection *connection,
1171                                 const char *path, const char *interface,
1172                                 const char *name, int type, ...)
1173 {
1174         va_list args;
1175         gboolean result;
1176
1177         va_start(args, type);
1178
1179         result = emit_signal_valist(connection, path, interface,
1180                                                         name, type, args);
1181
1182         va_end(args);
1183
1184         return result;
1185 }
1186
1187 gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
1188                                 const char *path, const char *interface,
1189                                 const char *name, int type, va_list args)
1190 {
1191         return emit_signal_valist(connection, path, interface,
1192                                                         name, type, args);
1193 }