/*
 * e-gdbus-templates.c
 *
 * This program 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 of the License, or (at your option) version 3.
 *
 * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
 *
 */

#include <gio/gio.h>

#include <stdio.h>

#include "e-data-server-util.h"
#include "e-flag.h"
#include "e-gdbus-templates.h"

gboolean
e_gdbus_signal_emission_hook_void (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, NULL, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_boolean (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 1);
	param_values++;
	item = g_variant_new_boolean (g_value_get_boolean (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_string (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 1);
	param_values++;
	item = g_variant_new_string (g_value_get_string (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_strv (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;
	const gchar * const *arg_strv;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 1);
	param_values++;
	arg_strv = g_value_get_boxed (param_values);
	item = g_variant_new_strv (arg_strv, -1);
	g_variant_builder_add_value (builder, item);
	param_values++;
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_uint (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 1);
	param_values++;
	item = g_variant_new_uint32 (g_value_get_uint (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_uint_string (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 2);
	param_values++;
	item = g_variant_new_uint32 (g_value_get_uint (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	item = g_variant_new_string (g_value_get_string (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_async_void (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;
	GError *arg_error;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 2);
	param_values++;
	item = g_variant_new_uint32 (g_value_get_uint (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	arg_error = g_value_get_boxed (param_values);
	if (arg_error) {
		gchar *dbus_error_name = g_dbus_error_encode_gerror (arg_error);
		item = g_variant_new_string (dbus_error_name ? dbus_error_name : "");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string (arg_error->message);
		g_variant_builder_add_value (builder, item);
		g_free (dbus_error_name);
	} else {
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);

		param_values++;
	}
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_async_boolean (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;
	GError *arg_error;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 3);
	param_values++;
	item = g_variant_new_uint32 (g_value_get_uint (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	arg_error = g_value_get_boxed (param_values);
	if (arg_error) {
		gchar *dbus_error_name = g_dbus_error_encode_gerror (arg_error);
		item = g_variant_new_string (dbus_error_name ? dbus_error_name : "");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string (arg_error->message);
		g_variant_builder_add_value (builder, item);
		g_free (dbus_error_name);

		/* fake value for easier processing in e_gdbus_proxy_emit_signal() */
		item = g_variant_new_boolean (FALSE);
		g_variant_builder_add_value (builder, item);
	} else {
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);

		param_values++;
		item = g_variant_new_boolean (g_value_get_boolean (param_values));
		g_variant_builder_add_value (builder, item);
		param_values++;
	}
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_async_string (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;
	GError *arg_error;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 3);
	param_values++;
	item = g_variant_new_uint32 (g_value_get_uint (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	arg_error = g_value_get_boxed (param_values);
	if (arg_error) {
		gchar *dbus_error_name = g_dbus_error_encode_gerror (arg_error);
		item = g_variant_new_string (dbus_error_name ? dbus_error_name : "");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string (arg_error->message);
		g_variant_builder_add_value (builder, item);
		g_free (dbus_error_name);

		/* fake value for easier processing in e_gdbus_proxy_emit_signal() */
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);
	} else {
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);

		param_values++;
		item = g_variant_new_string (g_value_get_string (param_values));
		g_variant_builder_add_value (builder, item);
		param_values++;
	}
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_async_strv (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;
	const GError *arg_error;
	const gchar * const *arg_strv;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 3);
	param_values++;
	item = g_variant_new_uint32 (g_value_get_uint (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	arg_error = g_value_get_boxed (param_values);
	if (arg_error) {
		const gchar *fake_strv;
		gchar *dbus_error_name = g_dbus_error_encode_gerror (arg_error);
		item = g_variant_new_string (dbus_error_name ? dbus_error_name : "");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string (arg_error->message);
		g_variant_builder_add_value (builder, item);
		g_free (dbus_error_name);

		/* fake value for easier processing in e_gdbus_proxy_emit_signal() */
		fake_strv = NULL;
		item = g_variant_new_strv (&fake_strv, -1);
		g_variant_builder_add_value (builder, item);
	} else {
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);

		param_values++;
		arg_strv = g_value_get_boxed (param_values);
		item = g_variant_new_strv (arg_strv, -1);
		g_variant_builder_add_value (builder, item);
		param_values++;
	}
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

gboolean
e_gdbus_signal_emission_hook_async_uint (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, const gchar *signal_name, const gchar *iface_name)
{
	GObject *object;
	GDBusConnection *connection;
	const gchar *path;
	GVariant *params;
	GVariant *item;
	GVariantBuilder *builder;
	GError *arg_error;

	if (n_param_values < 1 || !G_VALUE_HOLDS (&param_values[0], G_TYPE_OBJECT))
		return FALSE;

	object = g_value_get_object (&param_values[0]);
	path = g_object_get_data (object, "gdbus-codegen-path");
	connection = g_object_get_data (object, "gdbus-codegen-connection");
	if (connection == NULL || path == NULL)
		return FALSE;

	builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
	g_assert_cmpint (n_param_values - 1, ==, 3);
	param_values++;
	item = g_variant_new_uint32 (g_value_get_uint (param_values));
	g_variant_builder_add_value (builder, item);
	param_values++;
	arg_error = g_value_get_boxed (param_values);
	if (arg_error) {
		gchar *dbus_error_name = g_dbus_error_encode_gerror (arg_error);
		item = g_variant_new_string (dbus_error_name ? dbus_error_name : "");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string (arg_error->message);
		g_variant_builder_add_value (builder, item);
		g_free (dbus_error_name);

		/* fake value for easier processing in e_gdbus_proxy_emit_signal() */
		item = g_variant_new_uint32 (g_value_get_uint (0));
		g_variant_builder_add_value (builder, item);
	} else {
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);
		item = g_variant_new_string ("");
		g_variant_builder_add_value (builder, item);

		param_values++;
		item = g_variant_new_uint32 (g_value_get_uint (param_values));
		g_variant_builder_add_value (builder, item);
		param_values++;
	}
	params = g_variant_builder_end (builder);
	g_variant_builder_unref (builder);

	g_dbus_connection_emit_signal (connection, NULL, path, iface_name, signal_name, params, NULL);

	return TRUE;
}

void
e_gdbus_proxy_emit_signal (GDBusProxy *proxy, GVariant *parameters, guint signal_id, guint signal_type)
{
	gboolean arg_boolean = FALSE;
	const gchar *arg_const_string = NULL;
	const gchar **arg_const_strv = NULL;
	guint arg_uint = 0;

	g_return_if_fail (proxy != NULL);

	if ((signal_type & E_GDBUS_TYPE_IS_ASYNC) != 0) {
		/* the signal is a done signal, thus opid and error name with error message are first two parameters */
		guint arg_opid = 0;
		const gchar *dbus_error_name = NULL, *dbus_error_message = NULL;
		GError *arg_error = NULL;

		signal_type = signal_type & (~E_GDBUS_TYPE_IS_ASYNC);
		switch (signal_type) {
		case E_GDBUS_TYPE_VOID:
			g_variant_get (parameters, "(u&s&s)", &arg_opid, &dbus_error_name, &dbus_error_message);
			break;
		case E_GDBUS_TYPE_BOOLEAN:
			g_variant_get (parameters, "(u&s&sb)", &arg_opid, &dbus_error_name, &dbus_error_message, &arg_boolean);
			break;
		case E_GDBUS_TYPE_STRING:
			g_variant_get (parameters, "(u&s&s&s)", &arg_opid, &dbus_error_name, &dbus_error_message, &arg_const_string);
			break;
		case E_GDBUS_TYPE_STRV:
			/* array is newly allocated, but items are gvariant's */
			g_variant_get (parameters, "(u&s&s^a&s)", &arg_opid, &dbus_error_name, &dbus_error_message, &arg_const_strv);
			break;
		case E_GDBUS_TYPE_UINT:
			g_variant_get (parameters, "(u&s&su)", &arg_opid, &dbus_error_name, &dbus_error_message, &arg_uint);
			break;
		default:
			g_warning ("%s: Unknown E_GDBUS_TYPE %x", G_STRFUNC, signal_type);
			return;
		}

		if (dbus_error_name && *dbus_error_name && dbus_error_message)
			arg_error = g_dbus_error_new_for_dbus_error (dbus_error_name, dbus_error_message);

		switch (signal_type) {
		case E_GDBUS_TYPE_VOID:
			g_signal_emit (proxy, signal_id, 0, arg_opid, arg_error);
			break;
		case E_GDBUS_TYPE_BOOLEAN:
			g_signal_emit (proxy, signal_id, 0, arg_opid, arg_error, arg_boolean);
			break;
		case E_GDBUS_TYPE_STRING:
			g_signal_emit (proxy, signal_id, 0, arg_opid, arg_error, arg_const_string);
			break;
		case E_GDBUS_TYPE_STRV:
			g_signal_emit (proxy, signal_id, 0, arg_opid, arg_error, arg_const_strv);
			g_free (arg_const_strv);
			break;
		case E_GDBUS_TYPE_UINT:
			g_signal_emit (proxy, signal_id, 0, arg_opid, arg_error, arg_uint);
			break;
		default:
			g_warning ("%s: Unknown E_GDBUS_TYPE %x", G_STRFUNC, signal_type);
			break;
		}

		if (arg_error)
			g_error_free (arg_error);
	} else {
		switch (signal_type) {
		case E_GDBUS_TYPE_VOID:
			g_signal_emit (proxy, signal_id, 0);
			break;
		case E_GDBUS_TYPE_BOOLEAN:
			g_variant_get (parameters, "(b)", &arg_boolean);
			g_signal_emit (proxy, signal_id, 0, arg_boolean);
			break;
		case E_GDBUS_TYPE_STRING:
			g_variant_get (parameters, "(&s)", &arg_const_string);
			g_signal_emit (proxy, signal_id, 0, arg_const_string);
			break;
		case E_GDBUS_TYPE_STRV:
			/* array is newly allocated, but items are gvariant's */
			g_variant_get (parameters, "(^a&s)", &arg_const_strv);
			g_signal_emit (proxy, signal_id, 0, arg_const_strv);
			g_free (arg_const_strv);
			break;
		case E_GDBUS_TYPE_UINT:
			g_variant_get (parameters, "(u)", &arg_uint);
			g_signal_emit (proxy, signal_id, 0, arg_uint);
			break;
		case E_GDBUS_TYPE_UINT | E_GDBUS_TYPE_STRING:
			g_variant_get (parameters, "(u&s)", &arg_uint, &arg_const_string);
			g_signal_emit (proxy, signal_id, 0, arg_uint, arg_const_string);
			break;
		default:
			g_warning ("%s: Unknown E_GDBUS_TYPE %x", G_STRFUNC, signal_type);
			break;
		}
	}
}

void
e_gdbus_stub_handle_method_call (GObject *stub_object, GDBusMethodInvocation *invocation, GVariant *parameters, const gchar *method_name, guint method_id, guint method_type)
{
	gboolean handled = FALSE;
	gboolean arg_boolean = FALSE;
	const gchar *arg_const_string = NULL;
	const gchar ** arg_const_strv = NULL;
	guint arg_uint = 0;

	g_return_if_fail (stub_object != NULL);
	g_return_if_fail (method_name != NULL);

	switch (method_type & (~E_GDBUS_TYPE_IS_ASYNC)) {
	case E_GDBUS_TYPE_VOID:
		g_signal_emit (stub_object, method_id, 0, invocation, &handled);
		break;
	case E_GDBUS_TYPE_BOOLEAN:
		g_variant_get (parameters, "(b)", &arg_boolean);
		g_signal_emit (stub_object, method_id, 0, invocation, arg_boolean, &handled);
		break;
	case E_GDBUS_TYPE_STRING:
		g_variant_get (parameters, "(&s)", &arg_const_string);
		g_signal_emit (stub_object, method_id, 0, invocation, arg_const_string, &handled);
		break;
	case E_GDBUS_TYPE_STRV:
		/* array is newly allocated, but items are gvariant's */
		g_variant_get (parameters, "(^a&s)", &arg_const_strv);
		g_signal_emit (stub_object, method_id, 0, invocation, arg_const_strv, &handled);
		g_free (arg_const_strv);
		break;
	case E_GDBUS_TYPE_UINT:
		g_variant_get (parameters, "(u)", &arg_uint);
		g_signal_emit (stub_object, method_id, 0, invocation, arg_uint, &handled);
		break;
	default:
		g_warning ("%s: Unknown E_GDBUS_TYPE %x", G_STRFUNC, method_type);
		break;
	}

	if (!handled)
	      g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Method `%s' is not implemented", method_name);
}

G_DEFINE_INTERFACE (EGdbusAsyncOpKeeper, e_gdbus_async_op_keeper, G_TYPE_OBJECT)

static void
e_gdbus_async_op_keeper_default_init (EGdbusAsyncOpKeeperInterface *iface)
{
}

/* Created hash table of pending async operations. This can be freed
   with g_hash_table_destroy () in dispose. Interface asks for this
   ponter by call of e_gdbus_async_op_keeper_create_pending_ops ().
*/
GHashTable *
e_gdbus_async_op_keeper_create_pending_ops (EGdbusAsyncOpKeeper *object)
{
	g_return_val_if_fail (object != NULL, NULL);
	g_return_val_if_fail (E_IS_GDBUS_ASYNC_OP_KEEPER (object), NULL);

	return g_hash_table_new (g_direct_hash, g_direct_equal);
}

/* Returns hash table of pending async operations previously created
   by e_gdbus_async_op_keeper_create_pending_ops ().
*/
GHashTable *
e_gdbus_async_op_keeper_get_pending_ops (EGdbusAsyncOpKeeper *object)
{
	EGdbusAsyncOpKeeperInterface *iface;

	g_return_val_if_fail (E_IS_GDBUS_ASYNC_OP_KEEPER (object), 0);

	iface = E_GDBUS_ASYNC_OP_KEEPER_GET_IFACE (object);
	g_return_val_if_fail (iface->get_pending_ops != NULL, 0);

	return iface->get_pending_ops (object);
}

/* synchronously cancels one operation - sends a request from client to the server */
gboolean
e_gdbus_async_op_keeper_cancel_op_sync (EGdbusAsyncOpKeeper *object, guint in_opid, GCancellable *cancellable, GError **error)
{
	EGdbusAsyncOpKeeperInterface *iface;

	g_return_val_if_fail (E_IS_GDBUS_ASYNC_OP_KEEPER (object), FALSE);

	iface = E_GDBUS_ASYNC_OP_KEEPER_GET_IFACE (object);
	g_return_val_if_fail (iface->cancel_op_sync != NULL, FALSE);

	return iface->cancel_op_sync (object, in_opid, cancellable, error);
}

/* Used to finish asynchronous GDBus call - this might be done in the callback
   as soon as possible; method returns to a caller operation ID which was started */
void
e_gdbus_complete_async_method (gpointer object, GDBusMethodInvocation *invocation, guint opid)
{
	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", opid));
}

/* Used to finish synchronous GDBus call - this might be done in the callback
   as soon as possible */
void
e_gdbus_complete_sync_method_void (gpointer object, GDBusMethodInvocation *invocation, const GError *error)
{
	if (error)
		g_dbus_method_invocation_return_gerror (invocation, error);
	else
		g_dbus_method_invocation_return_value (invocation, NULL);
}

void
e_gdbus_complete_sync_method_boolean (gpointer object, GDBusMethodInvocation *invocation, gboolean out_boolean, const GError *error)
{
	if (error)
		g_dbus_method_invocation_return_gerror (invocation, error);
	else
		g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", out_boolean));
}

void
e_gdbus_complete_sync_method_string (gpointer object, GDBusMethodInvocation *invocation, const gchar *out_string, const GError *error)
{
	if (error)
		g_dbus_method_invocation_return_gerror (invocation, error);
	else
		g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", out_string));
}

void
e_gdbus_complete_sync_method_strv (gpointer object, GDBusMethodInvocation *invocation, const gchar * const *out_strv, const GError *error)
{
	if (error)
		g_dbus_method_invocation_return_gerror (invocation, error);
	else
		g_dbus_method_invocation_return_value (invocation, g_variant_new ("(^as)", out_strv));
}

void
e_gdbus_complete_sync_method_uint (gpointer object, GDBusMethodInvocation *invocation, guint out_uint, const GError *error)
{
	if (error)
		g_dbus_method_invocation_return_gerror (invocation, error);
	else
		g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", out_uint));
}

typedef struct _AsyncOpData
{
	EGdbusAsyncOpKeeper *proxy;
	guint opid;

	GCancellable *cancellable;
	gulong cancel_id;

	gpointer async_source_tag;
	GAsyncReadyCallback async_callback;
	gpointer async_user_data;

	guint result_type; /* any of E_GDBUS_TYPE_... except of E_GDBUS_TYPE_IS_ASYNC */
	union {
		gboolean out_boolean;
		gchar *out_string;
		gchar ** out_strv;
		guint out_uint;
	} result;
} AsyncOpData;

static void
async_op_data_free (AsyncOpData *op_data)
{
	GHashTable *pending_ops;

	g_return_if_fail (op_data != NULL);

	if (op_data->cancellable) {
		if (op_data->cancel_id)
			g_cancellable_disconnect (op_data->cancellable, op_data->cancel_id);
		g_object_unref (op_data->cancellable);
	}

	pending_ops = e_gdbus_async_op_keeper_get_pending_ops (E_GDBUS_ASYNC_OP_KEEPER (op_data->proxy));
	if (pending_ops)
		g_hash_table_remove (pending_ops, GUINT_TO_POINTER (op_data->opid));
	g_object_unref (op_data->proxy);

	switch (op_data->result_type) {
	case E_GDBUS_TYPE_STRING:
		if (op_data->result.out_string)
			g_free (op_data->result.out_string);
		break;
	case E_GDBUS_TYPE_STRV:
		if (op_data->result.out_strv)
			g_strfreev (op_data->result.out_strv);
		break;
	}

	g_free (op_data);

	g_return_if_fail (pending_ops != NULL);
}

static void
async_op_complete (AsyncOpData *op_data, const GError *error, gboolean in_idle)
{
	GSimpleAsyncResult *simple;

	g_return_if_fail (op_data != NULL);

	simple = g_simple_async_result_new (G_OBJECT (op_data->proxy), op_data->async_callback, op_data->async_user_data, op_data->async_source_tag);
	g_simple_async_result_set_op_res_gpointer (simple, op_data, (GDestroyNotify) async_op_data_free);
	if (error)
		g_simple_async_result_set_from_error (simple, error);

	if (in_idle)
		g_simple_async_result_complete_in_idle (simple);
	else
		g_simple_async_result_complete (simple);

	g_object_unref (simple);
}

static void
gdbus_op_cancelled_cb (GCancellable *cancellable, AsyncOpData *op_data)
{
	GError *call_error = NULL;

	g_return_if_fail (op_data != NULL);

	if (!e_gdbus_async_op_keeper_cancel_op_sync (op_data->proxy, op_data->opid, NULL, &call_error)) {
		/* only if failed, because otherwise will receive cancelled signal from the server */
		GError *error = NULL;

		g_return_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));

		async_op_complete (op_data, error, TRUE);
		g_error_free (error);
	}

	if (call_error) {
		g_debug ("%s: Failed to cancel operation: %s\n", G_STRFUNC, call_error->message);
		g_error_free (call_error);
	}
}

static void
gdbus_async_call_opid_ready_cb (GObject *source_proxy, GAsyncResult *result, gpointer user_data)
{
	GVariant *_result;
	GError *error = NULL;
	AsyncOpData *op_data = user_data;

	_result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_proxy), result, &error);

	if (_result != NULL && !error) {
		EGdbusAsyncOpKeeper *op_keeper = E_GDBUS_ASYNC_OP_KEEPER (source_proxy);
		GHashTable *pending_ops;
		gboolean add_pending = TRUE;

		g_return_if_fail (op_keeper != NULL);

		pending_ops = e_gdbus_async_op_keeper_get_pending_ops (op_keeper);
		g_return_if_fail (pending_ops != NULL);

		g_variant_get (_result, "(u)", &op_data->opid);
		g_variant_unref (_result);

		if (op_data->cancellable && !g_cancellable_set_error_if_cancelled (op_data->cancellable, &error))
			op_data->cancel_id = g_cancellable_connect (op_data->cancellable, G_CALLBACK (gdbus_op_cancelled_cb), op_data, NULL);
		else
			add_pending = op_data->cancellable == NULL;

		/* add to pending ops, waiting for associated 'done' signal */
		if (add_pending)
			g_hash_table_insert (pending_ops, GUINT_TO_POINTER (op_data->opid), op_data);
	} else if (_result) {
		g_variant_unref (_result);
	}

	if (error) {
		async_op_complete (op_data, error, FALSE);
		g_error_free (error);
	}
}

static gchar **
copy_strv (const gchar * const *strv)
{
	GPtrArray *array;
	gint ii;

	array = g_ptr_array_sized_new (g_strv_length ((gchar **) strv) + 1);

	for (ii = 0; strv[ii]; ii++) {
		g_ptr_array_add (array, g_strdup (strv[ii]));
	}

	/* NULL-terminated */
	g_ptr_array_add (array, NULL);

	return (gchar **) g_ptr_array_free (array, FALSE);
}

static void
gdbus_proxy_async_method_done (guint e_gdbus_type, gconstpointer out_value, EGdbusAsyncOpKeeper *object, guint arg_opid, const GError *error)
{
	AsyncOpData *op_data;
	GHashTable *pending_ops;

	g_return_if_fail (object != NULL);
	g_return_if_fail (E_IS_GDBUS_ASYNC_OP_KEEPER (object));

	pending_ops = e_gdbus_async_op_keeper_get_pending_ops (object);
	g_return_if_fail (pending_ops != NULL);

	op_data = g_hash_table_lookup (pending_ops, GUINT_TO_POINTER (arg_opid));
	if (!op_data) {
		/* it happens for cancelled operations, thus rather than track cancelled ops disable the debug warning */
		/* g_debug ("%s: Operation %d gone before got done signal for it", G_STRFUNC, arg_opid); */
		return;
	}

	if (out_value) {
		op_data->result_type = e_gdbus_type;

		switch (e_gdbus_type) {
		case E_GDBUS_TYPE_VOID:
			break;
		case E_GDBUS_TYPE_BOOLEAN:
			op_data->result.out_boolean = * ((const gboolean *) out_value);
			break;
		case E_GDBUS_TYPE_STRING:
			op_data->result.out_string = g_strdup ((const gchar *) out_value);
			break;
		case E_GDBUS_TYPE_STRV:
			op_data->result.out_strv = copy_strv ((const gchar * const *) out_value);
			break;
		case E_GDBUS_TYPE_UINT:
			op_data->result.out_uint = * ((const guint *) out_value);
			break;
		default:
			g_warning ("%s: Unknown E_GDBUS_TYPE %x", G_STRFUNC, e_gdbus_type);
			break;
		}
	}

	async_op_complete (op_data, error, TRUE);
}

void
e_gdbus_proxy_async_method_done_void (EGdbusAsyncOpKeeper *proxy, guint arg_opid, const GError *error)
{
	gdbus_proxy_async_method_done (E_GDBUS_TYPE_VOID, NULL, proxy, arg_opid, error);
}

void
e_gdbus_proxy_async_method_done_boolean (EGdbusAsyncOpKeeper *proxy, guint arg_opid, const GError *error, gboolean out_boolean)
{
	gdbus_proxy_async_method_done (E_GDBUS_TYPE_BOOLEAN, &out_boolean, proxy, arg_opid, error);
}

/* takes ownership of the out parameter */
void
e_gdbus_proxy_async_method_done_string (EGdbusAsyncOpKeeper *proxy, guint arg_opid, const GError *error, const gchar *out_string)
{
	gdbus_proxy_async_method_done (E_GDBUS_TYPE_STRING, out_string, proxy, arg_opid, error);
}

/* takes ownership of the out parameter */
void
e_gdbus_proxy_async_method_done_strv (EGdbusAsyncOpKeeper *proxy, guint arg_opid, const GError *error, const gchar * const *out_strv)
{
	gdbus_proxy_async_method_done (E_GDBUS_TYPE_STRV, out_strv, proxy, arg_opid, error);
}

void
e_gdbus_proxy_async_method_done_uint (EGdbusAsyncOpKeeper *proxy, guint arg_opid, const GError *error, guint out_uint)
{
	gdbus_proxy_async_method_done (E_GDBUS_TYPE_UINT, &out_uint, proxy, arg_opid, error);
}

/* takes ownership of _params */
static void
gdbus_proxy_call_with_params (GVariant *_params, const gchar *method_name, gpointer source_tag, EGdbusAsyncOpKeeper *proxy, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	AsyncOpData *op_data;

	op_data = g_new0 (AsyncOpData, 1);
	op_data->proxy = g_object_ref (proxy);
	op_data->opid = 0;
	op_data->async_source_tag = source_tag;
	op_data->async_callback = callback;
	op_data->async_user_data = user_data;
	op_data->cancellable = cancellable;
	if (op_data->cancellable)
		g_object_ref (op_data->cancellable);

	g_dbus_proxy_call (G_DBUS_PROXY (proxy), method_name, _params, G_DBUS_CALL_FLAGS_NONE, e_data_server_util_get_dbus_call_timeout (), cancellable, gdbus_async_call_opid_ready_cb, op_data);
}

void
e_gdbus_proxy_call_void (const gchar *method_name, gpointer source_tag, EGdbusAsyncOpKeeper *proxy, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	gdbus_proxy_call_with_params (NULL, method_name, source_tag, proxy, cancellable, callback, user_data);
}

void
e_gdbus_proxy_call_boolean (const gchar *method_name, gpointer source_tag, EGdbusAsyncOpKeeper *proxy, gboolean in_boolean, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	GVariant *_params;

	_params = g_variant_new ("(b)", in_boolean);

	gdbus_proxy_call_with_params (_params, method_name, source_tag, proxy, cancellable, callback, user_data);
}

void
e_gdbus_proxy_call_string (const gchar *method_name, gpointer source_tag, EGdbusAsyncOpKeeper *proxy, const gchar *in_string, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	GVariant *_params;

	_params = g_variant_new ("(s)", in_string);

	gdbus_proxy_call_with_params (_params, method_name, source_tag, proxy, cancellable, callback, user_data);
}

void
e_gdbus_proxy_call_strv (const gchar *method_name, gpointer source_tag, EGdbusAsyncOpKeeper *proxy, const gchar * const *in_strv, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	GVariant *_params;

	_params = g_variant_new ("(^as)", in_strv);

	gdbus_proxy_call_with_params (_params, method_name, source_tag, proxy, cancellable, callback, user_data);
}

void
e_gdbus_proxy_call_uint (const gchar *method_name, gpointer source_tag, EGdbusAsyncOpKeeper *proxy, guint in_uint, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	GVariant *_params;

	_params = g_variant_new ("(u)", in_uint);

	gdbus_proxy_call_with_params (_params, method_name, source_tag, proxy, cancellable, callback, user_data);
}

gboolean
e_gdbus_proxy_finish_call_void (EGdbusAsyncOpKeeper *proxy, GAsyncResult *result, GError **error, gpointer source_tag)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (proxy), source_tag), FALSE);

	return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
}

gboolean
e_gdbus_proxy_finish_call_boolean (EGdbusAsyncOpKeeper *proxy, GAsyncResult *result, gboolean *out_boolean, GError **error, gpointer source_tag)
{
	AsyncOpData *op_data;

	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (proxy), source_tag), FALSE);
	g_return_val_if_fail (out_boolean != NULL, FALSE);

	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
		return FALSE;

	op_data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
	g_return_val_if_fail (op_data != NULL, FALSE);
	g_return_val_if_fail (op_data->result_type == E_GDBUS_TYPE_BOOLEAN, FALSE);

	*out_boolean = op_data->result.out_boolean;

	return TRUE;
}

/* caller takes ownership and responsibility for freeing the out parameter */
gboolean
e_gdbus_proxy_finish_call_string (EGdbusAsyncOpKeeper *proxy, GAsyncResult *result, gchar **out_string, GError **error, gpointer source_tag)
{
	AsyncOpData *op_data;

	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (proxy), source_tag), FALSE);
	g_return_val_if_fail (out_string != NULL, FALSE);

	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
		return FALSE;

	op_data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
	g_return_val_if_fail (op_data != NULL, FALSE);
	g_return_val_if_fail (op_data->result_type == E_GDBUS_TYPE_STRING, FALSE);

	*out_string = op_data->result.out_string;
	op_data->result.out_string = NULL;

	return TRUE;
}

/* caller takes ownership and responsibility for freeing the out parameter */
gboolean
e_gdbus_proxy_finish_call_strv (EGdbusAsyncOpKeeper *proxy, GAsyncResult *result, gchar ***out_strv, GError **error, gpointer source_tag)
{
	AsyncOpData *op_data;

	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (proxy), source_tag), FALSE);
	g_return_val_if_fail (out_strv != NULL, FALSE);

	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
		return FALSE;

	op_data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
	g_return_val_if_fail (op_data != NULL, FALSE);
	g_return_val_if_fail (op_data->result_type == E_GDBUS_TYPE_STRV, FALSE);

	*out_strv = op_data->result.out_strv;
	op_data->result.out_strv = NULL;

	return TRUE;
}

gboolean
e_gdbus_proxy_finish_call_uint (EGdbusAsyncOpKeeper *proxy, GAsyncResult *result, guint *out_uint, GError **error, gpointer source_tag)
{
	AsyncOpData *op_data;

	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (proxy), source_tag), FALSE);
	g_return_val_if_fail (out_uint != NULL, FALSE);

	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
		return FALSE;

	op_data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
	g_return_val_if_fail (op_data != NULL, FALSE);
	g_return_val_if_fail (op_data->result_type == E_GDBUS_TYPE_STRING, FALSE);

	*out_uint = op_data->result.out_uint;

	return TRUE;
}

typedef struct _SyncOpData
{
	EFlag *flag;
	GError **error;

	guint out_type; /* one of E_GDBUS_TYPE_... except of E_GDBUS_TYPE_IS_ASYNC */
	union {
		gboolean *out_boolean;
		gchar **out_string;
		gchar ***out_strv;
		guint *out_uint;
	} out_arg;

	union {
		EGdbusCallFinishVoid finish_void;
		EGdbusCallFinishBoolean finish_boolean;
		EGdbusCallFinishString finish_string;
		EGdbusCallFinishStrv finish_strv;
		EGdbusCallFinishUint finish_uint;
	} finish_func;

	gboolean finish_result;
} SyncOpData;

static void
gdbus_proxy_sync_ready_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
{
	SyncOpData *sync_data = user_data;

	g_return_if_fail (sync_data != NULL);
	g_return_if_fail (sync_data->flag != NULL);

	switch (sync_data->out_type) {
	case E_GDBUS_TYPE_VOID:
		g_return_if_fail (sync_data->finish_func.finish_void != NULL);
		sync_data->finish_result = sync_data->finish_func.finish_void (G_DBUS_PROXY (proxy), result, sync_data->error);
		break;
	case E_GDBUS_TYPE_BOOLEAN:
		g_return_if_fail (sync_data->finish_func.finish_boolean != NULL);
		sync_data->finish_result = sync_data->finish_func.finish_boolean (G_DBUS_PROXY (proxy), result, sync_data->out_arg.out_boolean, sync_data->error);
		break;
	case E_GDBUS_TYPE_STRING:
		g_return_if_fail (sync_data->finish_func.finish_string != NULL);
		sync_data->finish_result = sync_data->finish_func.finish_string (G_DBUS_PROXY (proxy), result, sync_data->out_arg.out_string, sync_data->error);
		break;
	case E_GDBUS_TYPE_STRV:
		g_return_if_fail (sync_data->finish_func.finish_strv != NULL);
		sync_data->finish_result = sync_data->finish_func.finish_strv (G_DBUS_PROXY (proxy), result, sync_data->out_arg.out_strv, sync_data->error);
		break;
	case E_GDBUS_TYPE_UINT:
		g_return_if_fail (sync_data->finish_func.finish_uint != NULL);
		sync_data->finish_result = sync_data->finish_func.finish_uint (G_DBUS_PROXY (proxy), result, sync_data->out_arg.out_uint, sync_data->error);
		break;
	default:
		g_warning ("%s: Unknown 'out' E_GDBUS_TYPE %x", G_STRFUNC, sync_data->out_type);
		sync_data->finish_result = FALSE;
	}

	e_flag_set (sync_data->flag);
}

static gboolean
gdbus_proxy_call_sync (GDBusProxy *proxy, GCancellable *cancellable, GError **error, gpointer start_func, gpointer finish_func, guint in_type, gconstpointer in_value, guint out_type, gpointer out_value)
{
	SyncOpData sync_data = { 0 };

	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);

	switch (out_type) {
	case E_GDBUS_TYPE_VOID:
		sync_data.finish_func.finish_void = finish_func;
		break;
	case E_GDBUS_TYPE_BOOLEAN:
		sync_data.out_arg.out_boolean = out_value;
		sync_data.finish_func.finish_boolean = finish_func;
		break;
	case E_GDBUS_TYPE_STRING:
		sync_data.out_arg.out_string = out_value;
		sync_data.finish_func.finish_string = finish_func;
		break;
	case E_GDBUS_TYPE_STRV:
		sync_data.out_arg.out_strv = out_value;
		sync_data.finish_func.finish_strv = finish_func;
		break;
	case E_GDBUS_TYPE_UINT:
		sync_data.out_arg.out_uint = out_value;
		sync_data.finish_func.finish_uint = finish_func;
		break;
	default:
		g_warning ("%s: Unknown 'out' E_GDBUS_TYPE %x", G_STRFUNC, out_type);
		return FALSE;
	}

	sync_data.flag = e_flag_new ();
	sync_data.error = error;
	sync_data.out_type = out_type;

	switch (in_type) {
	case E_GDBUS_TYPE_VOID: {
		EGdbusCallStartVoid start = start_func;
		start (proxy, cancellable, gdbus_proxy_sync_ready_cb, &sync_data);
	} break;
	case E_GDBUS_TYPE_BOOLEAN: {
		EGdbusCallStartBoolean start = start_func;
		start (proxy, * ((gboolean *) in_value), cancellable, gdbus_proxy_sync_ready_cb, &sync_data);
	} break;
	case E_GDBUS_TYPE_STRING: {
		EGdbusCallStartString start = start_func;
		start (proxy, (const gchar *) in_value, cancellable, gdbus_proxy_sync_ready_cb, &sync_data);
	} break;
	case E_GDBUS_TYPE_STRV: {
		EGdbusCallStartStrv start = start_func;
		start (proxy, (const gchar * const *) in_value, cancellable, gdbus_proxy_sync_ready_cb, &sync_data);
	} break;
	case E_GDBUS_TYPE_UINT: {
		EGdbusCallStartUint start = start_func;
		start (proxy, * ((guint *) in_value), cancellable, gdbus_proxy_sync_ready_cb, &sync_data);
	} break;
	default:
		g_warning ("%s: Unknown 'in' E_GDBUS_TYPE %x", G_STRFUNC, in_type);
		e_flag_free (sync_data.flag);
		return FALSE;
	}

	/* check if called from the main thread */
	if (g_main_context_is_owner (g_main_context_default ())
	    || g_main_context_default () == g_main_context_get_thread_default ()
	    || !g_main_context_get_thread_default ()) {
		/* Might not be the best thing here, but as the async operation
		   is divided into two-steps process, invoking the method and
		   waiting for its "done" signal, then if the sync method is called
		   from the main thread, then there is probably no other option.
		*/
		while (!e_flag_is_set (sync_data.flag)) {
			g_usleep (250000);
			g_main_context_iteration (NULL, FALSE);
		}
	} else {
		/* is called in a dedicated thread */
		e_flag_wait (sync_data.flag);
	}
	e_flag_free (sync_data.flag);

	return sync_data.finish_result;
}

gboolean
e_gdbus_proxy_call_sync_void__void (GDBusProxy *proxy, GCancellable *cancellable, GError **error, EGdbusCallStartVoid start_func, EGdbusCallFinishVoid finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_VOID, NULL, E_GDBUS_TYPE_VOID, NULL);
}

gboolean
e_gdbus_proxy_call_sync_void__boolean (GDBusProxy *proxy, gboolean *out_boolean, GCancellable *cancellable, GError **error, EGdbusCallStartVoid start_func, EGdbusCallFinishBoolean finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (out_boolean != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_VOID, NULL, E_GDBUS_TYPE_BOOLEAN, out_boolean);
}

gboolean
e_gdbus_proxy_call_sync_void__string (GDBusProxy *proxy, gchar **out_string, GCancellable *cancellable, GError **error, EGdbusCallStartVoid start_func, EGdbusCallFinishString finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (out_string != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_VOID, NULL, E_GDBUS_TYPE_STRING, out_string);
}

gboolean
e_gdbus_proxy_call_sync_void__strv (GDBusProxy *proxy, gchar ***out_strv, GCancellable *cancellable, GError **error, EGdbusCallStartVoid start_func, EGdbusCallFinishStrv finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (out_strv != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_VOID, NULL, E_GDBUS_TYPE_STRV, out_strv);
}

gboolean
e_gdbus_proxy_call_sync_void__uint (GDBusProxy *proxy, guint *out_uint, GCancellable *cancellable, GError **error, EGdbusCallStartVoid start_func, EGdbusCallFinishUint finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (out_uint != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_VOID, NULL, E_GDBUS_TYPE_UINT, out_uint);
}

gboolean
e_gdbus_proxy_call_sync_boolean__void (GDBusProxy *proxy, gboolean in_boolean, GCancellable *cancellable, GError **error, EGdbusCallStartBoolean start_func, EGdbusCallFinishVoid finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_BOOLEAN, &in_boolean, E_GDBUS_TYPE_VOID, NULL);
}

gboolean
e_gdbus_proxy_call_sync_string__void (GDBusProxy *proxy, const gchar *in_string, GCancellable *cancellable, GError **error, EGdbusCallStartString start_func, EGdbusCallFinishVoid finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (in_string != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_STRING, in_string, E_GDBUS_TYPE_VOID, NULL);
}

gboolean
e_gdbus_proxy_call_sync_strv__void (GDBusProxy *proxy, const gchar * const *in_strv, GCancellable *cancellable, GError **error, EGdbusCallStartStrv start_func, EGdbusCallFinishVoid finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (in_strv != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_STRV, in_strv, E_GDBUS_TYPE_VOID, NULL);
}

gboolean
e_gdbus_proxy_call_sync_uint__void (GDBusProxy *proxy, guint in_uint, GCancellable *cancellable, GError **error, EGdbusCallStartUint start_func, EGdbusCallFinishVoid finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_UINT, &in_uint, E_GDBUS_TYPE_VOID, NULL);
}

gboolean
e_gdbus_proxy_call_sync_string__string (GDBusProxy *proxy, const gchar *in_string, gchar **out_string, GCancellable *cancellable, GError **error, EGdbusCallStartString start_func, EGdbusCallFinishString finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (in_string != NULL, FALSE);
	g_return_val_if_fail (out_string != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_STRING, in_string, E_GDBUS_TYPE_STRING, out_string);
}

gboolean
e_gdbus_proxy_call_sync_string__strv (GDBusProxy *proxy, const gchar *in_string, gchar ***out_strv, GCancellable *cancellable, GError **error, EGdbusCallStartString start_func, EGdbusCallFinishStrv finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (in_string != NULL, FALSE);
	g_return_val_if_fail (out_strv != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_STRING, in_string, E_GDBUS_TYPE_STRV, out_strv);
}

gboolean
e_gdbus_proxy_call_sync_strv__string (GDBusProxy *proxy, const gchar * const *in_strv, gchar **out_string, GCancellable *cancellable, GError **error, EGdbusCallStartStrv start_func, EGdbusCallFinishString finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (in_strv != NULL, FALSE);
	g_return_val_if_fail (out_string != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_STRV, in_strv, E_GDBUS_TYPE_STRING, out_string);
}

gboolean
e_gdbus_proxy_call_sync_strv__strv (GDBusProxy *proxy, const gchar * const *in_strv, gchar ***out_strv, GCancellable *cancellable, GError **error, EGdbusCallStartStrv start_func, EGdbusCallFinishStrv finish_func)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (start_func != NULL, FALSE);
	g_return_val_if_fail (finish_func != NULL, FALSE);
	g_return_val_if_fail (in_strv != NULL, FALSE);
	g_return_val_if_fail (out_strv != NULL, FALSE);

	return gdbus_proxy_call_sync (proxy, cancellable, error, start_func, finish_func, E_GDBUS_TYPE_STRV, in_strv, E_GDBUS_TYPE_STRV, out_strv);
}

static void
proxy_method_call (const gchar *method_name, guint param_type, gconstpointer param_value, GDBusProxy *proxy, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	GVariant *params = NULL;
	GVariant *item;
	GVariantBuilder *builder = NULL;

	g_return_if_fail (method_name != NULL);
	g_return_if_fail (proxy != NULL);
	g_return_if_fail (G_IS_DBUS_PROXY (proxy));
	if (param_type != E_GDBUS_TYPE_VOID)
		g_return_if_fail (param_value != NULL);

	switch (param_type) {
	case E_GDBUS_TYPE_VOID:
		break;
	case E_GDBUS_TYPE_BOOLEAN:
		builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
		item = g_variant_new_boolean (* ((const gboolean *) param_value));
		g_variant_builder_add_value (builder, item);
		break;
	case E_GDBUS_TYPE_STRING:
		builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
		item = g_variant_new_string ((const gchar *) param_value);
		g_variant_builder_add_value (builder, item);
		break;
	case E_GDBUS_TYPE_STRV:
		builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
		item = g_variant_new_strv ((const gchar * const *) param_value, -1);
		g_variant_builder_add_value (builder, item);
		break;
	case E_GDBUS_TYPE_UINT:
		builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
		item = g_variant_new_uint32 (* ((const guint *) param_value));
		g_variant_builder_add_value (builder, item);
		break;
	default:
		g_warning ("%s: Unknown 'param' E_GDBUS_TYPE %x", G_STRFUNC, param_type);
		return;
	}

	if (builder != NULL) {
		params = g_variant_builder_end (builder);
		g_variant_builder_unref (builder);
	}

	g_dbus_proxy_call (G_DBUS_PROXY (proxy), method_name, params, G_DBUS_CALL_FLAGS_NONE, e_data_server_util_get_dbus_call_timeout (), cancellable, callback, user_data);
}

void
e_gdbus_proxy_method_call_void (const gchar *method_name, GDBusProxy *proxy, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	proxy_method_call (method_name, E_GDBUS_TYPE_VOID, NULL, proxy, cancellable, callback, user_data);
}

void
e_gdbus_proxy_method_call_boolean (const gchar *method_name, GDBusProxy *proxy, gboolean in_boolean, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	proxy_method_call (method_name, E_GDBUS_TYPE_BOOLEAN, &in_boolean, proxy, cancellable, callback, user_data);
}

void
e_gdbus_proxy_method_call_string (const gchar *method_name, GDBusProxy *proxy, const gchar *in_string, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	proxy_method_call (method_name, E_GDBUS_TYPE_STRING, in_string, proxy, cancellable, callback, user_data);
}

void
e_gdbus_proxy_method_call_strv (const gchar *method_name, GDBusProxy *proxy, const gchar * const *in_strv, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	proxy_method_call (method_name, E_GDBUS_TYPE_STRV, in_strv, proxy, cancellable, callback, user_data);
}

void
e_gdbus_proxy_method_call_uint (const gchar *method_name, GDBusProxy *proxy, guint in_uint, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
	proxy_method_call (method_name, E_GDBUS_TYPE_VOID, &in_uint, proxy, cancellable, callback, user_data);
}

static gboolean
process_result (const gchar *caller_func_name, guint out_type, gpointer out_value, GVariant *_result)
{
	if (out_type != E_GDBUS_TYPE_VOID)
		g_return_val_if_fail (out_value != NULL, FALSE);

	if (_result == NULL)
		return FALSE;

	switch (out_type) {
	case E_GDBUS_TYPE_VOID:
		break;
	case E_GDBUS_TYPE_BOOLEAN:
		g_variant_get (_result, "(b)", (gboolean *) out_value);
		break;
	case E_GDBUS_TYPE_STRING:
		g_variant_get (_result, "(s)", (gchar **) out_value);
		break;
	case E_GDBUS_TYPE_STRV:
		g_variant_get (_result, "(^as)", (gchar ***) out_value);
		break;
	case E_GDBUS_TYPE_UINT:
		g_variant_get (_result, "(u)", (guint *) out_value);
		break;
	default:
		g_warning ("%s: Unknown 'out' E_GDBUS_TYPE %x", caller_func_name ? caller_func_name : G_STRFUNC, out_type);
		break;
	}

	g_variant_unref (_result);

	return TRUE;
}

static gboolean
proxy_method_call_finish (guint out_type, gpointer out_param, GDBusProxy *proxy, GAsyncResult *result, GError **error)
{
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), FALSE);
	if (out_type != E_GDBUS_TYPE_VOID)
		g_return_val_if_fail (out_param != NULL, FALSE);

	return process_result (G_STRFUNC, out_type, out_param, g_dbus_proxy_call_finish (proxy, result, error));
}

gboolean
e_gdbus_proxy_method_call_finish_void (GDBusProxy *proxy, GAsyncResult *result, GError **error)
{
	return proxy_method_call_finish (E_GDBUS_TYPE_VOID, NULL, proxy, result, error);
}

gboolean
e_gdbus_proxy_method_call_finish_boolean (GDBusProxy *proxy, GAsyncResult *result, gboolean *out_boolean, GError **error)
{
	return proxy_method_call_finish (E_GDBUS_TYPE_BOOLEAN, out_boolean, proxy, result, error);
}

gboolean
e_gdbus_proxy_method_call_finish_string (GDBusProxy *proxy, GAsyncResult *result, gchar **out_string, GError **error)
{
	return proxy_method_call_finish (E_GDBUS_TYPE_STRING, out_string, proxy, result, error);
}

gboolean
e_gdbus_proxy_method_call_finish_strv (GDBusProxy *proxy, GAsyncResult *result, gchar ***out_strv, GError **error)
{
	return proxy_method_call_finish (E_GDBUS_TYPE_STRV, out_strv, proxy, result, error);
}

gboolean
e_gdbus_proxy_method_call_finish_uint (GDBusProxy *proxy, GAsyncResult *result, guint *out_uint, GError **error)
{
	return proxy_method_call_finish (E_GDBUS_TYPE_UINT, out_uint, proxy, result, error);
}

static gboolean
proxy_method_call_sync (const gchar *method_name, guint in_type, gconstpointer in_value, guint out_type, gpointer out_value, GDBusProxy *proxy, GCancellable *cancellable, GError **error)
{
	GVariant *params = NULL;
	GVariant *item;
	GVariantBuilder *builder = NULL;

	g_return_val_if_fail (method_name != NULL, FALSE);
	g_return_val_if_fail (proxy != NULL, FALSE);
	g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), FALSE);
	if (in_type != E_GDBUS_TYPE_VOID)
		g_return_val_if_fail (in_value != NULL, FALSE);
	if (out_type != E_GDBUS_TYPE_VOID)
		g_return_val_if_fail (out_value != NULL, FALSE);

	switch (in_type) {
	case E_GDBUS_TYPE_VOID:
		break;
	case E_GDBUS_TYPE_BOOLEAN:
		builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
		item = g_variant_new_boolean (* ((const gboolean *) in_value));
		g_variant_builder_add_value (builder, item);
		break;
	case E_GDBUS_TYPE_STRING:
		builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
		item = g_variant_new_string ((const gchar *) in_value);
		g_variant_builder_add_value (builder, item);
		break;
	case E_GDBUS_TYPE_STRV:
		builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
		item = g_variant_new_strv ((const gchar * const *) in_value, -1);
		g_variant_builder_add_value (builder, item);
		break;
	case E_GDBUS_TYPE_UINT:
		builder = g_variant_builder_new (G_VARIANT_TYPE_TUPLE);
		item = g_variant_new_uint32 (* ((const guint *) in_value));
		g_variant_builder_add_value (builder, item);
		break;
	default:
		g_warning ("%s: Unknown 'in' E_GDBUS_TYPE %x", G_STRFUNC, in_type);
		return FALSE;
	}

	if (builder != NULL) {
		params = g_variant_builder_end (builder);
		g_variant_builder_unref (builder);
	}

	return process_result (G_STRFUNC, out_type, out_value, g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy), method_name, params, G_DBUS_CALL_FLAGS_NONE, e_data_server_util_get_dbus_call_timeout (), cancellable, error));
}

gboolean
e_gdbus_proxy_method_call_sync_void__void (const gchar *method_name, GDBusProxy *proxy, GCancellable *cancellable, GError **error)
{
	return proxy_method_call_sync (method_name, E_GDBUS_TYPE_VOID, NULL, E_GDBUS_TYPE_VOID, NULL, proxy, cancellable, error);
}

gboolean
e_gdbus_proxy_method_call_sync_boolean__void (const gchar *method_name, GDBusProxy *proxy, gboolean in_boolean, GCancellable *cancellable, GError **error)
{
	return proxy_method_call_sync (method_name, E_GDBUS_TYPE_BOOLEAN, &in_boolean, E_GDBUS_TYPE_VOID, NULL, proxy, cancellable, error);
}

gboolean
e_gdbus_proxy_method_call_sync_string__void (const gchar *method_name, GDBusProxy *proxy, const gchar *in_string, GCancellable *cancellable, GError **error)
{
	return proxy_method_call_sync (method_name, E_GDBUS_TYPE_STRING, in_string, E_GDBUS_TYPE_VOID, NULL, proxy, cancellable, error);
}

gboolean
e_gdbus_proxy_method_call_sync_strv__void (const gchar *method_name, GDBusProxy *proxy, const gchar * const *in_strv, GCancellable *cancellable, GError **error)
{
	return proxy_method_call_sync (method_name, E_GDBUS_TYPE_STRV, in_strv, E_GDBUS_TYPE_VOID, NULL, proxy, cancellable, error);
}

gboolean
e_gdbus_proxy_method_call_sync_uint__void (const gchar *method_name, GDBusProxy *proxy, guint in_uint, GCancellable *cancellable, GError **error)
{
	return proxy_method_call_sync (method_name, E_GDBUS_TYPE_UINT, &in_uint, E_GDBUS_TYPE_VOID, NULL, proxy, cancellable, error);
}

gboolean
e_gdbus_proxy_method_call_sync_string__string (const gchar *method_name, GDBusProxy *proxy, const gchar *in_string, gchar **out_string, GCancellable *cancellable, GError **error)
{
	return proxy_method_call_sync (method_name, E_GDBUS_TYPE_STRING, in_string, E_GDBUS_TYPE_STRING, out_string, proxy, cancellable, error);
}

gboolean
e_gdbus_proxy_method_call_sync_strv__string (const gchar *method_name, GDBusProxy *proxy, const gchar * const *in_strv, gchar **out_string, GCancellable *cancellable, GError **error)
{
	return proxy_method_call_sync (method_name, E_GDBUS_TYPE_STRV, in_strv, E_GDBUS_TYPE_STRING, out_string, proxy, cancellable, error);
}

/* free returned pointer with g_strfreev() */
gchar **
e_gdbus_templates_encode_error (const GError *in_error)
{
	gchar **strv;

	strv = g_new0 (gchar *, 3);

	if (!in_error) {
		strv[0] = g_strdup ("");
		strv[1] = g_strdup ("");
	} else {
		gchar *dbus_error_name = g_dbus_error_encode_gerror (in_error);

		strv[0] = e_util_utf8_make_valid (dbus_error_name ? dbus_error_name : "");
		strv[1] = e_util_utf8_make_valid (in_error->message);

		g_free (dbus_error_name);
	}

	return strv;
}

/* free *out_error with g_error_free(), if not NULL */
gboolean
e_gdbus_templates_decode_error (const gchar * const *in_strv, GError **out_error)
{
	const gchar *error_name, *error_message;

	g_return_val_if_fail (out_error != NULL, FALSE);

	*out_error = NULL;

	g_return_val_if_fail (in_strv != NULL, FALSE);
	g_return_val_if_fail (in_strv[0] != NULL, FALSE);
	g_return_val_if_fail (in_strv[1] != NULL, FALSE);
	g_return_val_if_fail (in_strv[2] == NULL, FALSE);

	error_name = in_strv[0];
	error_message = in_strv[1];

	if (error_name && *error_name && error_message)
		*out_error = g_dbus_error_new_for_dbus_error (error_name, error_message);

	return TRUE;
}

/* free returned pointer with g_strfreev() */
gchar **
e_gdbus_templates_encode_two_strings (const gchar *in_str1, const gchar *in_str2)
{
	gchar **strv;

	strv = g_new0 (gchar *, 3);
	strv[0] = e_util_utf8_make_valid (in_str1 ? in_str1 : "");
	strv[1] = e_util_utf8_make_valid (in_str2 ? in_str2 : "");
	strv[2] = NULL;

	return strv;
}

/* free *out_str1 and *out_str2 with g_free() */
gboolean
e_gdbus_templates_decode_two_strings (const gchar * const *in_strv, gchar **out_str1, gchar **out_str2)
{
	g_return_val_if_fail (in_strv != NULL, FALSE);
	g_return_val_if_fail (in_strv[0] != NULL, FALSE);
	g_return_val_if_fail (in_strv[1] != NULL, FALSE);
	g_return_val_if_fail (in_strv[2] == NULL, FALSE);
	g_return_val_if_fail (out_str1 != NULL, FALSE);
	g_return_val_if_fail (out_str2 != NULL, FALSE);

	*out_str1 = g_strdup (in_strv[0]);
	*out_str2 = g_strdup (in_strv[1]);

	return TRUE;
}
