/* * This file is part of Cockpit. * * Copyright (C) 2016 Red Hat, Inc. * * Cockpit is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * Cockpit is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Cockpit; If not, see . */ #include "config.h" #include "cockpitdbusmeta.h" #include "common/cockpitjson.h" #include static JsonArray * build_meta_arguments (GDBusArgInfo **args) { JsonArray *arguments = json_array_new (); while (*args) { json_array_add_string_element (arguments, (*args)->signature); args++; } return arguments; } static JsonObject * build_meta_method (GDBusMethodInfo *meth) { JsonObject *method = json_object_new (); if (meth->in_args) { json_object_set_array_member (method, "in", build_meta_arguments (meth->in_args)); } if (meth->out_args) { json_object_set_array_member (method, "out", build_meta_arguments (meth->out_args)); } return method; } static JsonObject * build_meta_signal (GDBusSignalInfo *sig) { JsonObject *signal = json_object_new (); if (sig->args) { json_object_set_array_member (signal, "in", build_meta_arguments (sig->args)); } return signal; } static JsonObject * build_meta_property (GDBusPropertyInfo *prop) { JsonObject *property = json_object_new ();; GString *flags = g_string_new (""); if (prop->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) g_string_append_c (flags, 'r'); if (prop->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE) g_string_append_c (flags, 'w'); json_object_set_string_member (property, "flags", flags->str); if (prop->signature) json_object_set_string_member (property, "type", prop->signature); g_string_free (flags, TRUE); return property; } JsonObject * cockpit_dbus_meta_build (GDBusInterfaceInfo *iface) { JsonObject *interface; JsonObject *methods; JsonObject *properties; JsonObject *signals; guint i; g_return_val_if_fail (iface != NULL, NULL); interface = json_object_new (); if (iface->methods) { methods = json_object_new (); for (i = 0; iface->methods[i] != NULL; i++) { json_object_set_object_member (methods, iface->methods[i]->name, build_meta_method (iface->methods[i])); } json_object_set_object_member (interface, "methods", methods); } if (iface->properties) { properties = json_object_new (); for (i = 0; iface->properties[i] != NULL; i++) { json_object_set_object_member (properties, iface->properties[i]->name, build_meta_property (iface->properties[i])); } json_object_set_object_member (interface, "properties", properties); } if (iface->signals) { signals = json_object_new (); for (i = 0; iface->signals[i] != NULL; i++) { json_object_set_object_member (signals, iface->signals[i]->name, build_meta_signal (iface->signals[i])); } json_object_set_object_member (interface, "signals", signals); } return interface; } static GDBusArgInfo ** parse_meta_arguments (JsonArray *arguments, GError **error) { const gchar *signature; GDBusArgInfo *arg; GPtrArray *args; guint i, length; JsonNode *node; args = g_ptr_array_new (); g_ptr_array_set_free_func (args, (GDestroyNotify)g_dbus_arg_info_unref); length = json_array_get_length (arguments); for (i = 0; i < length; i++) { node = json_array_get_element (arguments, i); if (!JSON_NODE_HOLDS_VALUE(node) || json_node_get_value_type (node) != G_TYPE_STRING) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid argument in dbus meta field"); break; } signature = json_node_get_string (node); if (!g_variant_is_signature (signature)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "argument in dbus meta field has invalid signature: %s", signature); break; } arg = g_new0 (GDBusArgInfo, 1); arg->ref_count = 1; arg->name = g_strdup_printf ("argument_%u", i); arg->signature = g_strdup (signature); g_ptr_array_add (args, arg); } if (i != length) { g_ptr_array_free (args, TRUE); return NULL; } else { g_ptr_array_add (args, NULL); return (GDBusArgInfo **)g_ptr_array_free (args, FALSE); } } static GDBusMethodInfo * parse_meta_method (const gchar *method_name, JsonObject *method, GError **error) { GDBusMethodInfo *ret = NULL; GDBusMethodInfo *meth; JsonArray *args; meth = g_new0 (GDBusMethodInfo, 1); meth->ref_count = 1; meth->name = g_strdup (method_name); if (!cockpit_json_get_array (method, "in", NULL, &args)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid \"in\" field in dbus meta method: %s", method_name); goto out; } if (args && json_array_get_length (args) > 0) { meth->in_args = parse_meta_arguments (args, error); if (!meth->in_args) goto out; } if (!cockpit_json_get_array (method, "out", NULL, &args)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid \"out\" field in dbus meta method: %s", method_name); goto out; } if (args && json_array_get_length (args) > 0) { meth->out_args = parse_meta_arguments (args, error); if (!meth->out_args) goto out; } ret = meth; meth = NULL; out: if (meth) g_dbus_method_info_unref (meth); return ret; } static GDBusSignalInfo * parse_meta_signal (const gchar *signal_name, JsonObject *signal, GError **error) { GDBusSignalInfo *ret = NULL; GDBusSignalInfo *sig; JsonArray *args; sig = g_new0 (GDBusSignalInfo, 1); sig->ref_count = 1; sig->name = g_strdup (signal_name); if (!cockpit_json_get_array (signal, "in", NULL, &args)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid \"in\" field in dbus meta signal: %s", signal_name); goto out; } if (args && json_array_get_length (args) > 0) { sig->args = parse_meta_arguments (args, error); if (!sig->args) goto out; } ret = sig; sig = NULL; out: if (sig) g_dbus_signal_info_unref (sig); return ret; } static GDBusPropertyInfo * parse_meta_property (const gchar *property_name, JsonObject *property, GError **error) { GDBusPropertyInfo *prop; GDBusPropertyInfo *ret = NULL; const gchar *flags; const gchar *type; prop = g_new0 (GDBusPropertyInfo, 1); prop->ref_count = 1; prop->name = g_strdup (property_name); if (!cockpit_json_get_string (property, "flags", NULL, &flags)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid \"flags\" field in dbus property: %s", property_name); goto out; } if (flags && strchr (flags, 'r')) prop->flags |= G_DBUS_PROPERTY_INFO_FLAGS_READABLE; if (flags && strchr (flags, 'w')) prop->flags |= G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE; if (!cockpit_json_get_string (property, "type", NULL, &type)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid \"type\" field in dbus property: %s", property_name); goto out; } else if (!type) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "missing \"type\" field in dbus property: %s", property_name); goto out; } else if (!g_variant_is_signature (type)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "the \"type\" field in dbus property is not a dbus signature: %s", type); goto out; } prop->signature = g_strdup (type); ret = prop; prop = NULL; out: if (prop) g_dbus_property_info_unref (prop); return ret; } GDBusInterfaceInfo * cockpit_dbus_meta_parse (const gchar *iface_name, JsonObject *interface, GError **error) { GDBusInterfaceInfo *ret = NULL; GDBusInterfaceInfo *iface; GDBusMethodInfo *meth; GDBusSignalInfo *sig; GDBusPropertyInfo *prop; JsonObject *methods; JsonObject *method; JsonObject *signals; JsonObject *signal; JsonObject *properties; JsonObject *property; GPtrArray *array = NULL; GList *names = NULL, *l; g_return_val_if_fail (iface_name != NULL, NULL); g_return_val_if_fail (interface != NULL, NULL); g_return_val_if_fail (!error || !*error, NULL); iface = g_new0 (GDBusInterfaceInfo, 1); iface->name = g_strdup (iface_name); iface->ref_count = 1; if (!cockpit_json_get_object (interface, "methods", NULL, &methods)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid \"methods\" field in dbus meta structure"); goto out; } if (methods) { array = g_ptr_array_new (); g_ptr_array_set_free_func (array, (GDestroyNotify)g_dbus_method_info_unref); names = json_object_get_members (methods); for (l = names; l != NULL; l = g_list_next (l)) { if (!cockpit_json_get_object (methods, l->data, NULL, &method)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid method field in dbus meta structure: %s", (const gchar *)l->data); goto out; } g_assert (method != NULL); meth = parse_meta_method (l->data, method, error); if (!meth) goto out; g_ptr_array_add (array, meth); } g_list_free (names); names = NULL; g_ptr_array_add (array, NULL); iface->methods = (GDBusMethodInfo **)g_ptr_array_free (array, FALSE); array = NULL; } if (!cockpit_json_get_object (interface, "signals", NULL, &signals)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid \"signals\" field in dbus meta structure"); goto out; } if (signals) { array = g_ptr_array_new (); g_ptr_array_set_free_func (array, (GDestroyNotify)g_dbus_signal_info_unref); names = json_object_get_members (signals); for (l = names; l != NULL; l = g_list_next (l)) { if (!cockpit_json_get_object (signals, l->data, NULL, &signal)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid signal field in dbus meta structure: %s", (const gchar *)l->data); goto out; } g_assert (signal != NULL); sig = parse_meta_signal (l->data, signal, error); if (!sig) goto out; g_ptr_array_add (array, sig); } g_list_free (names); names = NULL; g_ptr_array_add (array, NULL); iface->signals = (GDBusSignalInfo **)g_ptr_array_free (array, FALSE); array = NULL; } if (!cockpit_json_get_object (interface, "properties", NULL, &properties)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid \"properties\" field in dbus meta structure"); goto out; } if (properties) { array = g_ptr_array_new (); g_ptr_array_set_free_func (array, (GDestroyNotify)g_dbus_property_info_unref); names = json_object_get_members (properties); for (l = names; l != NULL; l = g_list_next (l)) { if (!cockpit_json_get_object (properties, l->data, NULL, &property)) { g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "invalid property field in dbus meta structure: %s", (const gchar *)l->data); goto out; } g_assert (property != NULL); prop = parse_meta_property (l->data, property, error); if (!prop) goto out; g_ptr_array_add (array, prop); } g_list_free (names); names = NULL; g_ptr_array_add (array, NULL); iface->properties = (GDBusPropertyInfo **)g_ptr_array_free (array, FALSE); array = NULL; } ret = iface; iface = NULL; out: if (iface) g_dbus_interface_info_unref (iface); if (array) g_ptr_array_free (array, TRUE); if (names) g_list_free (names); return ret; }