+ if (!dbus_message_iter_init(message, &iter))
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "No arguments given");
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid argument type: '%c'",
+ dbus_message_iter_get_arg_type(&iter));
+
+ dbus_message_iter_get_basic(&iter, &interface);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid argument type: '%c'",
+ dbus_message_iter_get_arg_type(&iter));
+
+ dbus_message_iter_get_basic(&iter, &name);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid argument type: '%c'",
+ dbus_message_iter_get_arg_type(&iter));
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "No such interface '%s'", interface);
+
+ property = find_property(iface->properties, name);
+ if (property == NULL)
+ return g_dbus_create_error(message,
+ DBUS_ERROR_UNKNOWN_PROPERTY,
+ "No such property '%s'", name);
+
+ if (property->set == NULL)
+ return g_dbus_create_error(message,
+ DBUS_ERROR_PROPERTY_READ_ONLY,
+ "Property '%s' is not writable", name);
+
+ if (property->exists != NULL &&
+ !property->exists(property, iface->user_data))
+ return g_dbus_create_error(message,
+ DBUS_ERROR_UNKNOWN_PROPERTY,
+ "No such property '%s'", name);
+
+ signature = dbus_message_iter_get_signature(&sub);
+ valid_signature = strcmp(signature, property->type) ? FALSE : TRUE;
+ dbus_free(signature);
+ if (!valid_signature)
+ return g_dbus_create_error(message,
+ DBUS_ERROR_INVALID_SIGNATURE,
+ "Invalid signature for '%s'", name);
+
+ propdata = g_new(struct property_data, 1);
+ propdata->id = next_pending_property++;
+ propdata->message = dbus_message_ref(message);
+ propdata->conn = connection;
+ pending_property_set = g_slist_prepend(pending_property_set, propdata);
+
+ property->set(property, &sub, propdata->id, iface->user_data);
+
+ return NULL;
+}
+
+static const GDBusMethodTable properties_methods[] = {
+ { GDBUS_METHOD("Get",
+ GDBUS_ARGS({ "interface", "s" }, { "name", "s" }),
+ GDBUS_ARGS({ "value", "v" }),
+ properties_get) },
+ { GDBUS_ASYNC_METHOD("Set",
+ GDBUS_ARGS({ "interface", "s" }, { "name", "s" },
+ { "value", "v" }),
+ NULL,
+ properties_set) },
+ { GDBUS_METHOD("GetAll",
+ GDBUS_ARGS({ "interface", "s" }),
+ GDBUS_ARGS({ "properties", "a{sv}" }),
+ properties_get_all) },
+ { }
+};
+
+static const GDBusSignalTable properties_signals[] = {
+ { GDBUS_SIGNAL("PropertiesChanged",
+ GDBUS_ARGS({ "interface", "s" },
+ { "changed_properties", "a{sv}" },
+ { "invalidated_properties", "as"})) },
+ { }
+};
+
+static void append_name(gpointer data, gpointer user_data)
+{
+ char *name = data;
+ DBusMessageIter *iter = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
+}
+
+static void emit_interfaces_removed(struct generic_data *data)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter, array;
+
+ if (root == NULL || data == root)
+ return;
+
+ signal = dbus_message_new_signal(root->path,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ "InterfacesRemoved");
+ if (signal == NULL)
+ return;
+
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &data->path);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array);
+
+ g_slist_foreach(data->removed, append_name, &array);
+ g_slist_free_full(data->removed, g_free);
+ data->removed = NULL;
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */
+ dbus_connection_send(data->conn, signal, NULL);
+ dbus_message_unref(signal);
+}
+
+static void remove_pending(struct generic_data *data)
+{
+ if (data->process_id > 0) {
+ g_source_remove(data->process_id);
+ data->process_id = 0;
+ }
+
+ pending = g_slist_remove(pending, data);
+}
+
+static gboolean process_changes(gpointer user_data)
+{
+ struct generic_data *data = user_data;
+
+ remove_pending(data);
+
+ if (data->added != NULL)
+ emit_interfaces_added(data);
+
+ /* Flush pending properties */
+ if (data->pending_prop == TRUE)
+ process_property_changes(data);
+
+ if (data->removed != NULL)
+ emit_interfaces_removed(data);
+
+ data->process_id = 0;
+
+ return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+ struct generic_data *data = user_data;
+ struct generic_data *parent = data->parent;
+
+ if (parent != NULL)
+ parent->objects = g_slist_remove(parent->objects, data);
+
+ if (data->process_id > 0) {
+ g_source_remove(data->process_id);
+ data->process_id = 0;
+ process_changes(data);
+ }
+
+ g_slist_foreach(data->objects, reset_parent, data->parent);
+ g_slist_free(data->objects);
+
+ dbus_connection_unref(data->conn);
+ g_free(data->introspect);
+ g_free(data->path);
+ g_free(data);
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ struct interface_data *iface;
+ const GDBusMethodTable *method;
+ const char *interface;