technology: return already enabled when tethering is enabled
[framework/connectivity/connman.git] / gdbus / object.c
index 4375ec5..900e7ab 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  D-Bus helper library
  *
- *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -45,98 +45,84 @@ struct generic_data {
 
 struct interface_data {
        char *name;
-       GDBusMethodTable *methods;
-       GDBusSignalTable *signals;
-       GDBusPropertyTable *properties;
+       const GDBusMethodTable *methods;
+       const GDBusSignalTable *signals;
+       const GDBusPropertyTable *properties;
        void *user_data;
        GDBusDestroyFunction destroy;
 };
 
-static void print_arguments(GString *gstr, const char *sig,
+struct security_data {
+       GDBusPendingReply pending;
+       DBusMessage *message;
+       const GDBusMethodTable *method;
+       void *iface_user_data;
+};
+
+static void print_arguments(GString *gstr, const GDBusArgInfo *args,
                                                const char *direction)
 {
-       int i;
-
-       for (i = 0; sig[i]; i++) {
-               char type[32];
-               int len, struct_level, dict_level;
-               gboolean complete;
-
-               complete = FALSE;
-               struct_level = dict_level = 0;
-               memset(type, 0, sizeof(type));
-
-               /* Gather enough data to have a single complete type */
-               for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
-                       switch (sig[i]){
-                       case '(':
-                               struct_level++;
-                               break;
-                       case ')':
-                               struct_level--;
-                               if (struct_level <= 0 && dict_level <= 0)
-                                       complete = TRUE;
-                               break;
-                       case '{':
-                               dict_level++;
-                               break;
-                       case '}':
-                               dict_level--;
-                               if (struct_level <= 0 && dict_level <= 0)
-                                       complete = TRUE;
-                               break;
-                       case 'a':
-                               break;
-                       default:
-                               if (struct_level <= 0 && dict_level <= 0)
-                                       complete = TRUE;
-                               break;
-                       }
-
-                       type[len] = sig[i];
-
-                       if (complete)
-                               break;
-               }
-
+       for (; args && args->name; args++) {
+               g_string_append_printf(gstr,
+                                       "\t\t\t<arg name=\"%s\" type=\"%s\"",
+                                       args->name, args->signature);
 
                if (direction)
                        g_string_append_printf(gstr,
-                                       "\t\t\t<arg type=\"%s\" direction=\"%s\"/>\n",
-                                       type, direction);
+                                       " direction=\"%s\"/>\n", direction);
                else
-                       g_string_append_printf(gstr,
-                                       "\t\t\t<arg type=\"%s\"/>\n",
-                                       type);
+                       g_string_append_printf(gstr, "/>\n");
+
        }
 }
 
 static void generate_interface_xml(GString *gstr, struct interface_data *iface)
 {
-       GDBusMethodTable *method;
-       GDBusSignalTable *signal;
+       const GDBusMethodTable *method;
+       const GDBusSignalTable *signal;
 
        for (method = iface->methods; method && method->name; method++) {
-               if (!strlen(method->signature) && !strlen(method->reply))
+               gboolean deprecated = method->flags &
+                                               G_DBUS_METHOD_FLAG_DEPRECATED;
+               gboolean noreply = method->flags &
+                                               G_DBUS_METHOD_FLAG_NOREPLY;
+
+               if (!deprecated && !noreply &&
+                               !(method->in_args && method->in_args->name) &&
+                               !(method->out_args && method->out_args->name))
                        g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
                                                                method->name);
                else {
                        g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
                                                                method->name);
-                       print_arguments(gstr, method->signature, "in");
-                       print_arguments(gstr, method->reply, "out");
+                       print_arguments(gstr, method->in_args, "in");
+                       print_arguments(gstr, method->out_args, "out");
+
+                       if (deprecated)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
+                       if (noreply)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n");
+
                        g_string_append_printf(gstr, "\t\t</method>\n");
                }
        }
 
        for (signal = iface->signals; signal && signal->name; signal++) {
-               if (!strlen(signal->signature))
+               gboolean deprecated = signal->flags &
+                                               G_DBUS_SIGNAL_FLAG_DEPRECATED;
+
+               if (!deprecated && !(signal->args && signal->args->name))
                        g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
                                                                signal->name);
                else {
                        g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
                                                                signal->name);
-                       print_arguments(gstr, signal->signature, NULL);
+                       print_arguments(gstr, signal->args, NULL);
+
+                       if (deprecated)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
                        g_string_append_printf(gstr, "\t\t</signal>\n");
                }
        }
@@ -154,7 +140,7 @@ static void generate_introspection_xml(DBusConnection *conn,
 
        gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
 
-       g_string_append_printf(gstr, "<node name=\"%s\">\n", path);
+       g_string_append_printf(gstr, "<node>\n");
 
        for (list = data->interfaces; list; list = list->next) {
                struct interface_data *iface = list->data;
@@ -182,34 +168,202 @@ done:
        data->introspect = g_string_free(gstr, FALSE);
 }
 
-static DBusHandlerResult introspect(DBusConnection *connection,
-                               DBusMessage *message, struct generic_data *data)
+static DBusMessage *introspect(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
 {
+       struct generic_data *data = user_data;
        DBusMessage *reply;
 
-       if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
-               error("Unexpected signature to introspect call");
-               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-       }
-
-       if (!data->introspect)
+       if (data->introspect == NULL)
                generate_introspection_xml(connection, data,
                                                dbus_message_get_path(message));
 
        reply = dbus_message_new_method_return(message);
-       if (!reply)
-               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       if (reply == NULL)
+               return NULL;
 
        dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
                                        DBUS_TYPE_INVALID);
 
-       dbus_connection_send(connection, reply, NULL);
+       return reply;
+}
+
+static DBusHandlerResult process_message(DBusConnection *connection,
+                       DBusMessage *message, const GDBusMethodTable *method,
+                                                       void *iface_user_data)
+{
+       DBusMessage *reply;
 
+       reply = method->function(connection, message, iface_user_data);
+
+       if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
+               if (reply != NULL)
+                       dbus_message_unref(reply);
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+               if (reply == NULL)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (reply == NULL)
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+       dbus_connection_send(connection, reply, NULL);
        dbus_message_unref(reply);
 
        return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               process_message(connection, secdata->message,
+                               secdata->method, secdata->iface_user_data);
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+               DBusMessage *reply;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               reply = g_dbus_create_error_valist(secdata->message,
+                                                       name, format, args);
+               if (reply != NULL) {
+                       dbus_connection_send(connection, reply, NULL);
+                       dbus_message_unref(reply);
+               }
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+
+       g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+       va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+struct builtin_security_data {
+       DBusConnection *conn;
+       GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+       struct builtin_security_data *data = user_data;
+
+       if (authorized == TRUE)
+               g_dbus_pending_success(data->conn, data->pending);
+       else
+               g_dbus_pending_error(data->conn, data->pending,
+                                               DBUS_ERROR_AUTH_FAILED, NULL);
+
+       g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending)
+{
+       struct builtin_security_data *data;
+
+       data = g_new0(struct builtin_security_data, 1);
+       data->conn = conn;
+       data->pending = pending;
+
+       if (polkit_check_authorization(conn, action, interaction,
+                               builtin_security_result, data, 30000) < 0)
+               g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+                       const GDBusMethodTable *method, void *iface_user_data)
+{
+       const GDBusSecurityTable *security;
+
+       for (security = security_table; security && security->privilege;
+                                                               security++) {
+               struct security_data *secdata;
+               gboolean interaction;
+
+               if (security->privilege != method->privilege)
+                       continue;
+
+               secdata = g_new(struct security_data, 1);
+               secdata->pending = next_pending++;
+               secdata->message = dbus_message_ref(msg);
+               secdata->method = method;
+               secdata->iface_user_data = iface_user_data;
+
+               pending_security = g_slist_prepend(pending_security, secdata);
+
+               if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+                       interaction = TRUE;
+               else
+                       interaction = FALSE;
+
+               if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+                                                       security->function)
+                       security->function(conn, security->action,
+                                               interaction, secdata->pending);
+               else
+                       builtin_security_function(conn, security->action,
+                                               interaction, secdata->pending);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
 static void generic_unregister(DBusConnection *connection, void *user_data)
 {
        struct generic_data *data = user_data;
@@ -223,6 +377,9 @@ static struct interface_data *find_interface(GSList *interfaces,
 {
        GSList *list;
 
+       if (name == NULL)
+               return NULL;
+
        for (list = interfaces; list; list = list->next) {
                struct interface_data *iface = list->data;
                if (!strcmp(name, iface->name))
@@ -232,57 +389,57 @@ static struct interface_data *find_interface(GSList *interfaces,
        return NULL;
 }
 
+static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
+                                                       DBusMessage *message)
+{
+       const char *sig = dbus_message_get_signature(message);
+       const char *p = NULL;
+
+       for (; args && args->signature && *sig; args++) {
+               p = args->signature;
+
+               for (; *sig && *p; sig++, p++) {
+                       if (*p != *sig)
+                               return FALSE;
+               }
+       }
+
+       if (*sig || (p && *p) || (args && args->signature))
+               return FALSE;
+
+       return TRUE;
+}
+
 static DBusHandlerResult generic_message(DBusConnection *connection,
                                        DBusMessage *message, void *user_data)
 {
        struct generic_data *data = user_data;
        struct interface_data *iface;
-       GDBusMethodTable *method;
+       const GDBusMethodTable *method;
        const char *interface;
 
-       if (dbus_message_is_method_call(message,
-                                       DBUS_INTERFACE_INTROSPECTABLE,
-                                                               "Introspect"))
-               return introspect(connection, message, data);
-
        interface = dbus_message_get_interface(message);
 
        iface = find_interface(data->interfaces, interface);
-       if (!iface)
+       if (iface == NULL)
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
        for (method = iface->methods; method &&
                        method->name && method->function; method++) {
-               DBusMessage *reply;
-
                if (dbus_message_is_method_call(message, iface->name,
                                                        method->name) == FALSE)
                        continue;
 
-               if (dbus_message_has_signature(message,
-                                               method->signature) == FALSE)
+               if (g_dbus_args_have_signature(method->in_args,
+                                                       message) == FALSE)
                        continue;
 
-               reply = method->function(connection, message, iface->user_data);
-
-               if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
-                       if (reply != NULL)
-                               dbus_message_unref(reply);
+               if (check_privilege(connection, message, method,
+                                               iface->user_data) == TRUE)
                        return DBUS_HANDLER_RESULT_HANDLED;
-               }
-
-               if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
-                       if (reply == NULL)
-                               return DBUS_HANDLER_RESULT_HANDLED;
-               }
 
-               if (reply == NULL)
-                       return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
-               dbus_connection_send(connection, reply, NULL);
-               dbus_message_unref(reply);
-
-               return DBUS_HANDLER_RESULT_HANDLED;
+               return process_message(connection, message, method,
+                                                       iface->user_data);
        }
 
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -300,18 +457,25 @@ static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
 
        parent_path = g_strdup(child_path);
        slash = strrchr(parent_path, '/');
-       if (!slash)
+       if (slash == NULL)
                goto done;
 
-       *slash = '\0';
+       if (slash == parent_path && parent_path[1] != '\0')
+               parent_path[1] = '\0';
+       else
+               *slash = '\0';
+
        if (!strlen(parent_path))
                goto done;
 
-       if (!dbus_connection_get_object_path_data(conn, parent_path,
-                                                       (void *) &data))
+       if (dbus_connection_get_object_path_data(conn, parent_path,
+                                                       (void *) &data) == FALSE) {
                goto done;
+       }
+
+       invalidate_parent_data(conn, parent_path);
 
-       if (!data)
+       if (data == NULL)
                goto done;
 
        g_free(data->introspect);
@@ -321,6 +485,32 @@ done:
        g_free(parent_path);
 }
 
+static const GDBusMethodTable introspect_methods[] = {
+       { GDBUS_METHOD("Introspect", NULL,
+                       GDBUS_ARGS({ "xml", "s" }), introspect) },
+       { }
+};
+
+static void add_interface(struct generic_data *data, const char *name,
+                               const GDBusMethodTable *methods,
+                               const GDBusSignalTable *signals,
+                               const GDBusPropertyTable *properties,
+                               void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct interface_data *iface;
+
+       iface = g_new0(struct interface_data, 1);
+       iface->name = g_strdup(name);
+       iface->methods = methods;
+       iface->signals = signals;
+       iface->properties = properties;
+       iface->user_data = user_data;
+       iface->destroy = destroy;
+
+       data->interfaces = g_slist_append(data->interfaces, iface);
+}
+
 static struct generic_data *object_path_ref(DBusConnection *connection,
                                                        const char *path)
 {
@@ -335,11 +525,10 @@ static struct generic_data *object_path_ref(DBusConnection *connection,
        }
 
        data = g_new0(struct generic_data, 1);
+       data->refcount = 1;
 
        data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
 
-       data->refcount = 1;
-
        if (!dbus_connection_register_object_path(connection, path,
                                                &generic_table, data)) {
                g_free(data->introspect);
@@ -349,9 +538,31 @@ static struct generic_data *object_path_ref(DBusConnection *connection,
 
        invalidate_parent_data(connection, path);
 
+       add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
+                       introspect_methods, NULL, NULL, data, NULL);
+
        return data;
 }
 
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+       struct interface_data *iface;
+
+       iface = find_interface(data->interfaces, name);
+       if (iface == NULL)
+               return FALSE;
+
+       data->interfaces = g_slist_remove(data->interfaces, iface);
+
+       if (iface->destroy)
+               iface->destroy(iface->user_data);
+
+       g_free(iface->name);
+       g_free(iface);
+
+       return TRUE;
+}
+
 static void object_path_unref(DBusConnection *connection, const char *path)
 {
        struct generic_data *data = NULL;
@@ -368,6 +579,8 @@ static void object_path_unref(DBusConnection *connection, const char *path)
        if (data->refcount > 0)
                return;
 
+       remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+
        invalidate_parent_data(connection, path);
 
        dbus_connection_unregister_object_path(connection, path);
@@ -375,23 +588,22 @@ static void object_path_unref(DBusConnection *connection, const char *path)
 
 static gboolean check_signal(DBusConnection *conn, const char *path,
                                const char *interface, const char *name,
-                               const char **args)
+                               const GDBusArgInfo **args)
 {
        struct generic_data *data = NULL;
        struct interface_data *iface;
-       GDBusSignalTable *signal;
+       const GDBusSignalTable *signal;
 
        *args = NULL;
        if (!dbus_connection_get_object_path_data(conn, path,
-                                       (void *) &data) || !data) {
+                                       (void *) &data) || data == NULL) {
                error("dbus_connection_emit_signal: path %s isn't registered",
                                path);
                return FALSE;
        }
 
        iface = find_interface(data->interfaces, interface);
-
-       if (!iface) {
+       if (iface == NULL) {
                error("dbus_connection_emit_signal: %s does not implement %s",
                                path, interface);
                return FALSE;
@@ -399,17 +611,13 @@ static gboolean check_signal(DBusConnection *conn, const char *path,
 
        for (signal = iface->signals; signal && signal->name; signal++) {
                if (!strcmp(signal->name, name)) {
-                       *args = signal->signature;
-                       break;
+                       *args = signal->args;
+                       return TRUE;
                }
        }
 
-       if (!*args) {
-               error("No signal named %s on interface %s", name, interface);
-               return FALSE;
-       }
-
-       return TRUE;
+       error("No signal named %s on interface %s", name, interface);
+       return FALSE;
 }
 
 static dbus_bool_t emit_signal_valist(DBusConnection *conn,
@@ -421,13 +629,13 @@ static dbus_bool_t emit_signal_valist(DBusConnection *conn,
 {
        DBusMessage *signal;
        dbus_bool_t ret;
-       const char *signature, *args;
+       const GDBusArgInfo *args;
 
        if (!check_signal(conn, path, interface, name, &args))
                return FALSE;
 
        signal = dbus_message_new_signal(path, interface, name);
-       if (!signal) {
+       if (signal == NULL) {
                error("Unable to allocate new %s.%s signal", interface,  name);
                return FALSE;
        }
@@ -436,8 +644,7 @@ static dbus_bool_t emit_signal_valist(DBusConnection *conn,
        if (!ret)
                goto fail;
 
-       signature = dbus_message_get_signature(signal);
-       if (strcmp(args, signature) != 0) {
+       if (g_dbus_args_have_signature(args, signal) == FALSE) {
                error("%s.%s: expected signature'%s' but got '%s'",
                                interface, name, args, signature);
                ret = FALSE;
@@ -454,32 +661,25 @@ fail:
 
 gboolean g_dbus_register_interface(DBusConnection *connection,
                                        const char *path, const char *name,
-                                       GDBusMethodTable *methods,
-                                       GDBusSignalTable *signals,
-                                       GDBusPropertyTable *properties,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
                                        void *user_data,
                                        GDBusDestroyFunction destroy)
 {
        struct generic_data *data;
-       struct interface_data *iface;
 
        data = object_path_ref(connection, path);
        if (data == NULL)
                return FALSE;
 
-       if (find_interface(data->interfaces, name))
+       if (find_interface(data->interfaces, name)) {
+               object_path_unref(connection, path);
                return FALSE;
+       }
 
-       iface = g_new0(struct interface_data, 1);
-
-       iface->name = g_strdup(name);
-       iface->methods = methods;
-       iface->signals = signals;
-       iface->properties = properties;
-       iface->user_data = user_data;
-       iface->destroy = destroy;
-
-       data->interfaces = g_slist_append(data->interfaces, iface);
+       add_interface(data, name, methods, signals,
+                       properties, user_data, destroy);
 
        g_free(data->introspect);
        data->introspect = NULL;
@@ -491,7 +691,9 @@ gboolean g_dbus_unregister_interface(DBusConnection *connection,
                                        const char *path, const char *name)
 {
        struct generic_data *data = NULL;
-       struct interface_data *iface;
+
+       if (path == NULL)
+               return FALSE;
 
        if (dbus_connection_get_object_path_data(connection, path,
                                                (void *) &data) == FALSE)
@@ -500,18 +702,9 @@ gboolean g_dbus_unregister_interface(DBusConnection *connection,
        if (data == NULL)
                return FALSE;
 
-       iface = find_interface(data->interfaces, name);
-       if (!iface)
+       if (remove_interface(data, name) == FALSE)
                return FALSE;
 
-       data->interfaces = g_slist_remove(data->interfaces, iface);
-
-       if (iface->destroy)
-               iface->destroy(iface->user_data);
-
-       g_free(iface->name);
-       g_free(iface);
-
        g_free(data->introspect);
        data->introspect = NULL;
 
@@ -520,6 +713,23 @@ gboolean g_dbus_unregister_interface(DBusConnection *connection,
        return TRUE;
 }
 
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+       if (security_table != NULL)
+               return FALSE;
+
+       security_table = security;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+       security_table = NULL;
+
+       return TRUE;
+}
+
 DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
                                        const char *format, va_list args)
 {