static DBusMessage*
service_method_change_lock (GkdSecretService *self, DBusMessage *message)
{
	GkdSecretChange *change;
	DBusMessage *reply;
	const char *caller;
	const gchar *path;
	GckObject *collection;

	caller = dbus_message_get_sender (message);
	if (!dbus_message_get_args (message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
		return NULL;

	/* Make sure it exists */
	collection = gkd_secret_objects_lookup_collection (self->objects, caller, path);
	if (!collection)
		return dbus_message_new_error (message, SECRET_ERROR_NO_SUCH_OBJECT,
		                               "The collection does not exist");
	g_object_unref (collection);

	change = gkd_secret_change_new (self, caller, path);
	path = gkd_secret_dispatch_get_object_path (GKD_SECRET_DISPATCH (change));
	gkd_secret_service_publish_dispatch (self, caller,
	                                     GKD_SECRET_DISPATCH (change));

	reply = dbus_message_new_method_return (message);
	dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);

	g_object_unref (change);
	return reply;
}
static gboolean
method_change_lock_internal (GkdSecretService *self,
			     GDBusMethodInvocation *invocation,
			     const gchar *collection_path)
{
	GkdSecretChange *change;
	const char *caller;
	const gchar *path;
	GckObject *collection;

	caller = g_dbus_method_invocation_get_sender (invocation);

	/* Make sure it exists */
	collection = gkd_secret_objects_lookup_collection (self->objects, caller, collection_path);
	if (!collection) {
		g_dbus_method_invocation_return_error_literal (invocation, GKD_SECRET_ERROR,
							       GKD_SECRET_ERROR_NO_SUCH_OBJECT,
							       "The collection does not exist");
		return TRUE;
	}

	g_object_unref (collection);

	change = gkd_secret_change_new (self, caller, collection_path);
	path = gkd_secret_dispatch_get_object_path (GKD_SECRET_DISPATCH (change));
	gkd_secret_service_publish_dispatch (self, caller,
					     GKD_SECRET_DISPATCH (change));

	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", path));
	g_object_unref (change);

	return TRUE;
}
static DBusMessage*
service_method_create_collection (GkdSecretService *self, DBusMessage *message)
{
	DBusMessageIter iter, array;
	GckAttributes *attrs;
	GkdSecretCreate *create;
	DBusMessage *reply;
	const gchar *path;
	const gchar *alias;
	const char *caller;
	const gchar *coll;

	/* Parse the incoming message */
	if (!dbus_message_has_signature (message, "a{sv}s"))
		return NULL;
	if (!dbus_message_iter_init (message, &iter))
		g_return_val_if_reached (NULL);
	attrs = gck_attributes_new ();
	dbus_message_iter_recurse (&iter, &array);
	if (!gkd_secret_property_parse_all (&array, SECRET_COLLECTION_INTERFACE, attrs)) {
		gck_attributes_unref (attrs);
		return dbus_message_new_error_printf (message, DBUS_ERROR_INVALID_ARGS,
		                                      "Invalid properties");
	}
	if (!dbus_message_iter_next (&iter))
		g_return_val_if_reached (NULL);
	dbus_message_iter_get_basic (&iter, &alias);

	/* Empty alias is no alias */
	if (alias) {
		if (!alias[0]) {
			alias = NULL;
		} else if (!g_str_equal (alias, "default")) {
			gck_attributes_unref (attrs);
			return dbus_message_new_error (message, DBUS_ERROR_NOT_SUPPORTED,
			                               "Only the 'default' alias is supported");
		}
	}

	gck_attributes_add_boolean (attrs, CKA_TOKEN, TRUE);

	/* Create the prompt object, for the password */
	caller = dbus_message_get_sender (message);
	create = gkd_secret_create_new (self, caller, attrs, alias);
	gck_attributes_unref (attrs);

	path = gkd_secret_dispatch_get_object_path (GKD_SECRET_DISPATCH (create));
	gkd_secret_service_publish_dispatch (self, caller,
	                                     GKD_SECRET_DISPATCH (create));

	coll = "/";
	reply = dbus_message_new_method_return (message);
	dbus_message_append_args (reply,
	                          DBUS_TYPE_OBJECT_PATH, &coll,
	                          DBUS_TYPE_OBJECT_PATH, &path,
	                          DBUS_TYPE_INVALID);

	g_object_unref (create);
	return reply;
}
static void
unlock_or_complete_this_prompt (GkdSecretCreate *self)
{
	GkdSecretUnlock *unlock;
	GkdSecretPrompt *prompt;

	g_object_ref (self);
	prompt = GKD_SECRET_PROMPT (self);

	unlock = gkd_secret_unlock_new (gkd_secret_prompt_get_service (prompt),
	                                gkd_secret_prompt_get_caller (prompt),
	                                gkd_secret_dispatch_get_object_path (GKD_SECRET_DISPATCH (self)));
	gkd_secret_unlock_queue (unlock, self->result_path);

	/*
	 * If any need to be unlocked, then replace this prompt
	 * object with an unlock prompt object, and call the prompt
	 * method.
	 */
	if (gkd_secret_unlock_have_queued (unlock)) {
		gkd_secret_service_publish_dispatch (gkd_secret_prompt_get_service (prompt),
		                                     gkd_secret_prompt_get_caller (prompt),
		                                     GKD_SECRET_DISPATCH (unlock));
		gkd_secret_unlock_call_prompt (unlock, gkd_secret_prompt_get_window_id (prompt));
	}

	g_object_unref (unlock);
	g_object_unref (self);
}
static gboolean
service_method_create_collection (GkdExportedService *skeleton,
				  GDBusMethodInvocation *invocation,
				  GVariant *properties,
				  gchar *alias,
				  GkdSecretService *self)
{
	GckBuilder builder = GCK_BUILDER_INIT;
	GckAttributes *attrs;
	GkdSecretCreate *create;
	const gchar *path;
	const char *caller;

	if (!gkd_secret_property_parse_all (properties, SECRET_COLLECTION_INTERFACE, &builder)) {
		gck_builder_clear (&builder);
		g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
							       G_DBUS_ERROR_INVALID_ARGS,
							       "Invalid properties");
		return TRUE;
	}

	/* Empty alias is no alias */
	if (alias) {
		if (!alias[0]) {
			alias = NULL;
		} else if (!g_str_equal (alias, "default")) {
			gck_builder_clear (&builder);
			g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
								       G_DBUS_ERROR_NOT_SUPPORTED,
								       "Only the 'default' alias is supported");
			return TRUE;
		}
	}

	gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
	attrs = gck_attributes_ref_sink (gck_builder_end (&builder));

	/* Create the prompt object, for the password */
	caller = g_dbus_method_invocation_get_sender (invocation);
	create = gkd_secret_create_new (self, caller, attrs, alias);
	gck_attributes_unref (attrs);

	path = gkd_secret_dispatch_get_object_path (GKD_SECRET_DISPATCH (create));
	gkd_secret_service_publish_dispatch (self, caller,
					     GKD_SECRET_DISPATCH (create));

	gkd_exported_service_complete_create_collection (skeleton, invocation,
							 "/", path);
	return TRUE;
}
static DBusMessage*
service_method_unlock (GkdSecretService *self, DBusMessage *message)
{
	GkdSecretUnlock *unlock;
	DBusMessage *reply;
	const char *caller;
	const gchar *path;
	int n_objpaths, i;
	char **objpaths;

	if (!dbus_message_get_args (message, NULL,
	                            DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objpaths, &n_objpaths,
	                            DBUS_TYPE_INVALID))
		return NULL;

	caller = dbus_message_get_sender (message);
	unlock = gkd_secret_unlock_new (self, caller, NULL);
	for (i = 0; i < n_objpaths; ++i)
		gkd_secret_unlock_queue (unlock, objpaths[i]);
	dbus_free_string_array (objpaths);

	/* So do we need to prompt? */
	if (gkd_secret_unlock_have_queued (unlock)) {
		gkd_secret_service_publish_dispatch (self, caller,
		                                     GKD_SECRET_DISPATCH (unlock));
		path = gkd_secret_dispatch_get_object_path (GKD_SECRET_DISPATCH (unlock));

	/* No need to prompt */
	} else {
		path = "/";
	}

	reply = dbus_message_new_method_return (message);
	objpaths = gkd_secret_unlock_get_results (unlock, &n_objpaths);
	dbus_message_append_args (reply,
	                          DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objpaths, n_objpaths,
	                          DBUS_TYPE_OBJECT_PATH, &path,
	                          DBUS_TYPE_INVALID);

	gkd_secret_unlock_reset_results (unlock);
	g_object_unref (unlock);

	return reply;
}
static DBusMessage*
service_method_open_session (GkdSecretService *self, DBusMessage *message)
{
	GkdSecretSession *session;
	DBusMessage *reply = NULL;
	const gchar *caller;

	if (!dbus_message_has_signature (message, "sv"))
		return NULL;

	caller = dbus_message_get_sender (message);

	/* Now we can create a session with this information */
	session = gkd_secret_session_new (self, caller);
	reply = gkd_secret_session_handle_open (session, message);

	if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
		gkd_secret_service_publish_dispatch (self, caller,
		                                     GKD_SECRET_DISPATCH (session));

	g_object_unref (session);
	return reply;
}
static gboolean
service_method_unlock (GkdExportedService *skeleton,
		       GDBusMethodInvocation *invocation,
		       gchar **objpaths,
		       GkdSecretService *self)
{
	GkdSecretUnlock *unlock;
	const char *caller;
	const gchar *path;
	int i, n_unlocked;
	gchar **unlocked;

	caller = g_dbus_method_invocation_get_sender (invocation);
	unlock = gkd_secret_unlock_new (self, caller, NULL);
	for (i = 0; objpaths[i] != NULL; ++i)
		gkd_secret_unlock_queue (unlock, objpaths[i]);

	/* So do we need to prompt? */
	if (gkd_secret_unlock_have_queued (unlock)) {
		gkd_secret_service_publish_dispatch (self, caller,
						     GKD_SECRET_DISPATCH (unlock));
		path = gkd_secret_dispatch_get_object_path (GKD_SECRET_DISPATCH (unlock));

	/* No need to prompt */
	} else {
		path = "/";
	}

	unlocked = gkd_secret_unlock_get_results (unlock, &n_unlocked);
	gkd_exported_service_complete_unlock (skeleton, invocation,
					      (const gchar **) unlocked, path);

	gkd_secret_unlock_reset_results (unlock);
	g_object_unref (unlock);

	return TRUE;
}
static gboolean
service_method_open_session (GkdExportedService *skeleton,
			     GDBusMethodInvocation *invocation,
			     gchar *algorithm,
			     GVariant *input,
			     GkdSecretService *self)
{
	GkdSecretSession *session;
	GVariant *output = NULL;
	gchar *result = NULL;
	GError *error = NULL;
	const gchar *caller;
	GVariant *input_payload;

	caller = g_dbus_method_invocation_get_sender (invocation);

	/* Now we can create a session with this information */
	session = gkd_secret_session_new (self, caller);
	input_payload = g_variant_get_variant (input);
	gkd_secret_session_handle_open (session, algorithm, input_payload,
					&output, &result,
					&error);
	g_variant_unref (input_payload);

	if (error != NULL) {
		g_dbus_method_invocation_take_error (invocation, error);
	} else {
		gkd_secret_service_publish_dispatch (self, caller,
						     GKD_SECRET_DISPATCH (session));
		gkd_exported_service_complete_open_session (skeleton, invocation, output, result);
		g_free (result);
	}

	g_object_unref (session);
	return TRUE;
}