/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/***************************************************************************
 *            e-book-backend-kolab.c
 *
 *  2010
 *  Copyright  2010  Christian Hilberg
 *  <hilberg@kernelconcepts.de>
 * and Silvan Marco Fin <silvan@kernelconcepts.de> in 2011
 ****************************************************************************/

/*
 * 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.1 of the License, or (at your option) any later version.
 *
 * 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
 */

/*----------------------------------------------------------------------------*/

#include <glib.h>
#include <glib-object.h>

#include <libedata-book/e-data-book-view.h>
#include <libedata-book/e-book-backend-sync.h>
#include <libedata-book/e-book-backend-cache.h>

#include <libekolabutil/camel-system-headers.h>
#include <libekolabutil/kolab-util-camel.h>
#include <libekolabutil/kolab-util-http.h>
#include <libekolabutil/kolab-util-glib.h>

#include <libekolab/kolab-types.h>
#include <libekolab/kolab-mail-access.h>
#include <libekolab/kolab-settings-handler.h>

#include "kolab-util-contact.h"
#include "kolab-util-contact-cache.h"

#include "e-book-backend-kolab.h"

/*----------------------------------------------------------------------------*/
/* table of KolabMailAccess objects */

static GHashTable *koma_objects = NULL;
static GMutex *koma_objects_lock = NULL;
static GMutex *active_book_views_lock = NULL;

/*----------------------------------------------------------------------------*/

typedef struct _EBookBackendKolabPrivate EBookBackendKolabPrivate;
struct _EBookBackendKolabPrivate
{
	/* GNOME_Evolution_Addressbook_BookMode	book_mode; */
	EBookBackendCache *book_cache;
	KolabMailAccess *book_koma;
	gchar *book_uri;
	gboolean auth_received;
	GError *mode_switch_err;
};

#define E_BOOK_BACKEND_KOLAB_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), E_TYPE_BOOK_BACKEND_KOLAB, EBookBackendKolabPrivate))


G_DEFINE_TYPE (EBookBackendKolab, e_book_backend_kolab, E_TYPE_BOOK_BACKEND_SYNC)

/*----------------------------------------------------------------------------*/
/* forward declarations */

static void
e_book_backend_kolab_open (EBookBackendSync *backend,
                           EDataBook *book,
                           GCancellable *cancellable,
                           gboolean only_if_exists,
                           GError **error);

/*----------------------------------------------------------------------------*/
/* internal statics */

static void
book_backend_kolab_signal_online_cb (GObject *object)
{
	EBackend *backend = NULL;
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	gboolean online = FALSE;

	g_return_if_fail (E_IS_BOOK_BACKEND_KOLAB (object));

	backend = E_BACKEND (object);
	self = E_BOOK_BACKEND_KOLAB (object);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	online = e_backend_get_online (backend);

	g_debug ("%s()[%u] backend mode: %i",
	         __func__, __LINE__, online);

	/* FIXME
	 *
	 * This callback is for online state notifications only.
	 * Doing I/O, which can fail, is the wrong thing to do
	 * here since we do not have a proper way to let the user
	 * cancel the I/O. Getting KolabMailAccess online can hang.
	 * Thus, cancellation would be needed.
	 *
	 * e_backend_get_online (backend) could be called in the
	 * "other" operations and the KolabMailAccess state changed
	 * if needed, Problem: KolabMailAccess online/offline state
	 * change involves synchronization with the server, which
	 * may take long time and is not expected by the user if
	 * they e.g. just add a contact or event.
	 *
	 * For now, we'll leave the cancellation issue aside
	 * (we could not have cancelled in 2.30 either), but
	 * at least bind a GError to our backend object, if we
	 * have one. We can then propagate this error in the
	 * first "other" operation the user triggers.
	 *
	 * It has been supposed by upstream to use a global static
	 * GCancellable for the cancellation issue, but it will
	 * not help us here since the Evolution online/offline
	 * toggle button does not seem to expect heavy I/O to
	 * be triggered. Our local GCancellabe would need transfer
	 * to Evo and be registered there so the online/offline
	 * switching in evo-kolab could be cancelled from the
	 * frontend. Moreover, there is no telling how long a
	 * synchronization run with the Kolab server may take,
	 * so we also cannot set a sensible timeout here.
	 *
	 */

	(void) kolab_util_backend_deploy_mode_by_backend (priv->book_koma,
	                                                  online,
	                                                  NULL, /* GCancellable (see above )*/
	                                                  &(priv->mode_switch_err));
	if (priv->mode_switch_err) {
		g_warning ("%s()[%u]: Online mode switching error pending, (%s), Domain %s Code %i",
		           __func__, __LINE__,
		           priv->mode_switch_err->message,
		           g_quark_to_string (priv->mode_switch_err->domain),
		           priv->mode_switch_err->code);
		g_error_free (priv->mode_switch_err);
		priv->mode_switch_err = NULL;
	}
}

static gboolean
book_backend_kolab_notify_opened (EBookBackendKolab *self,
                                  GError **err)
{
	EBookBackend *bbackend = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	KolabMailAccessOpmodeID tmp_mode = KOLAB_MAIL_ACCESS_OPMODE_INVAL;
	GError *tmp_err = NULL;

	g_assert (E_IS_BOOK_BACKEND_KOLAB (self));
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);
	bbackend = E_BOOK_BACKEND (self);

	tmp_mode = kolab_mail_access_get_opmode (priv->book_koma, &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}
	e_book_backend_notify_online (bbackend,
	                              tmp_mode == KOLAB_MAIL_ACCESS_OPMODE_ONLINE ? TRUE : FALSE);
	e_book_backend_notify_readonly (bbackend, FALSE);
	e_book_backend_notify_opened (bbackend, NULL);

	return TRUE;
}

static void
book_backend_kolab_notify_removed (EBookBackendKolab *self)
{
	EBookBackend *bbackend = NULL;

	g_assert (E_IS_BOOK_BACKEND_KOLAB (self));

	bbackend = E_BOOK_BACKEND (self);

	e_book_backend_notify_readonly (bbackend, TRUE);
	e_book_backend_notify_online (bbackend, FALSE);
}

/*----------------------------------------------------------------------------*/
/* class functions */

static void
e_book_backend_kolab_open (EBookBackendSync *backend,
                           EDataBook *book,
                           GCancellable *cancellable,
                           gboolean only_if_exists,
                           GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	ESource *esource = NULL;
	CamelURL *c_url = NULL;
	KolabSettingsHandler *ksettings = NULL;
	KolabMailAccess *tmp_koma = NULL;
	KolabSyncStrategyID sync_value = KOLAB_SYNC_STRATEGY_DEFAULT;
	gchar *sourcename = NULL;
	gchar *servername = NULL;
	gchar *username = NULL;
	gchar *user_at_server = NULL;
	gchar *tmp_key = NULL;
	const gchar *prop_str = NULL;
	gboolean online = FALSE;
	gboolean auth_required = FALSE;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	(void)only_if_exists; /* FIXME */
	e_return_data_book_error_if_fail (error == NULL || *error == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_mutex_lock (koma_objects_lock);

	/* to trigger eds to pass along username and password, set the property
	 * "auth" to "true" in the source setting:
	 * <property name="auth" value="true"/>
	 * <property name="username" value="..."/>
	 */

	esource = e_backend_get_source (E_BACKEND (backend));
	if (! E_IS_SOURCE (esource)) {
		/* FIXME mark this as a translatable string */
		tmp_err = e_data_book_create_error (E_DATA_BOOK_STATUS_OTHER_ERROR,
		                                    "Could not get ESource for backend");
		goto exit;
	}

	priv->book_uri = e_source_get_uri (esource);
	c_url = camel_url_new (priv->book_uri, &tmp_err);
	if (c_url == NULL)
		goto exit;

	servername = g_strdup (c_url->host);
	username = g_strdup (c_url->user);
	g_debug ("%s()[%u] servername = %s", __func__, __LINE__, servername);
	g_debug ("%s()[%u]   username = %s", __func__, __LINE__, username);

	/* Initialize backend cache */
	if (priv->book_cache != NULL) {
		g_object_unref (priv->book_cache);
	}
	priv->book_cache = e_book_backend_cache_new (priv->book_uri);
	ok = e_file_cache_clean (E_FILE_CACHE (priv->book_cache));
	g_debug (" + Book cache cleaning %s.", ok ? "was successful" : "FAILED");

	/* Prepare data from sync strategy property */
	prop_str = e_source_get_property (esource, KOLAB_SYNC_STRATEGY_PROP);
	sync_value = kolab_util_misc_sync_value_from_property (prop_str);
	sourcename = kolab_util_backend_get_relative_path_from_uri (priv->book_uri);

	/* Check whether we have a KolabMailAccess (KoMA) instance already */
	user_at_server = g_strdup_printf ("%s@%s",
	                                  username, servername);
	ok = g_hash_table_lookup_extended (koma_objects,
	                                   user_at_server,
	                                   (gpointer *) &tmp_key,
	                                   (gpointer *) &tmp_koma);

	if (ok) {
		gchar *passwd = NULL;

		/* There already is a KoMA instance for user_at_server.
		 * Use it and return
		 */
		g_object_ref (tmp_koma);
		priv->book_koma = tmp_koma;
		ksettings = kolab_mail_access_get_settings_handler (priv->book_koma);
		kolab_util_backend_prepare_settings (ksettings,
		                                     NULL,
		                                     NULL,
		                                     NULL,
		                                     NULL,
		                                     sourcename,
		                                     &sync_value);
		/* We may find that we do not yet have a password set.
		 * In case we're requested to go online, we will need
		 * one, so we need to get authentication data first.
		 */
		passwd = g_strdup (kolab_settings_handler_get_char_field (ksettings,
		                                                          KOLAB_SETTINGS_HANDLER_CHAR_FIELD_KOLAB_USER_PASSWORD,
		                                                          NULL));
		if (passwd == NULL) {
			auth_required = TRUE;
			goto exit;
		}
		g_free (passwd);

		(void) kolab_util_backend_deploy_mode_by_backend (priv->book_koma,
		                                                  online,
		                                                  cancellable,
		                                                  &tmp_err);
		goto exit;
	}

	/* Nope, we need to setup a new KoMA instance and a settings handler */

	/* init subsystems (these are no-ops if already called before) */
	kolab_util_glib_init ();
	kolab_util_http_init ();
	/* libcamel
	 * Curl init may configure the underlying SSL lib,
	 * but as far as SSL goes, we want Camel to rule here
	 * TODO check whether Camel session needs to be initialized before or after libcurl.
	 */
	ok = kolab_util_camel_init (&tmp_err);
	if (! ok)
		goto exit;

	/* Configure settings handler */
	ksettings = KOLAB_SETTINGS_HANDLER (g_object_new (KOLAB_TYPE_SETTINGS_HANDLER, NULL));
	ok = kolab_settings_handler_configure (ksettings,
	                                       KOLAB_FOLDER_CONTEXT_CONTACT,
	                                       &tmp_err);
	if (! ok)
		goto exit;

	ok = kolab_settings_handler_bringup (ksettings, &tmp_err);
	if (! ok)
		goto exit;

	kolab_util_backend_prepare_settings (ksettings,
	                                     esource,
	                                     servername,
	                                     username,
	                                     NULL,
	                                     sourcename,
	                                     &sync_value);

	/* create new KolabMailAccess instance */
	priv->book_koma = KOLAB_MAIL_ACCESS (g_object_new (KOLAB_TYPE_MAIL_ACCESS, NULL));
	g_object_add_toggle_ref (G_OBJECT (priv->book_koma),
	                         kolab_util_backend_koma_table_cleanup_cb,
	                         koma_objects);
	g_hash_table_insert (koma_objects,
	                     user_at_server,
	                     priv->book_koma);

	/* configure and bring up KolabMailAccess instance */
	ok = kolab_mail_access_configure (priv->book_koma,
	                                  ksettings,
	                                  &tmp_err);
	if (! ok)
		goto exit;

	ok = kolab_mail_access_bringup (priv->book_koma,
	                                cancellable,
	                                &tmp_err);
	if (! ok)
		goto exit;

	/* If we are here (just created a fresh KolabMailAccess),
	 * we did not yet receive authentication data. Though we
	 * do not need it if we want to stay in offline mode, once
	 * we're asked to go online, we'll need the credentials
	 * (password in our case) anyway, so we can just request
	 * it once the book gets opened.
	 */
	auth_required = TRUE;

 exit:

	if (ksettings != NULL)
		g_object_unref (ksettings);

	g_mutex_unlock (koma_objects_lock);

	if (c_url != NULL)
		camel_url_free (c_url);
	if (servername != NULL)
		g_free (servername);
	if (username != NULL)
		g_free (username);
	if (user_at_server != NULL)
		g_free (user_at_server);

	/* do we have an error set? if so, propagate and return */
	if (tmp_err != NULL) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
		return;
	}

	/* do we need authentication data? if so, notify and return */
	if (auth_required) {
		e_book_backend_notify_auth_required (E_BOOK_BACKEND (self), TRUE, NULL);
		return;
	}

	/* all good, notify that we're open for business */
	ok = book_backend_kolab_notify_opened (self, &tmp_err);
	if (! ok) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
	}
}

static void
e_book_backend_kolab_remove (EBookBackendSync *backend,
                             EDataBook *book,
                             GCancellable *cancellable,
                             GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_warning ("%s: FIXME implement me", __func__);

#if 0 /* FIXME old */
	g_debug ("%s()[%u] called.", __func__, __LINE__);
	if (e_book_backend_is_loaded (E_BOOK_BACKEND (backend)) != FALSE) {
		g_object_unref (priv->book_koma);
		priv->book_koma = NULL;
	}

	/* Removing backend cache */
	if (priv->book_cache != NULL) {
		(void) e_file_cache_remove (E_FILE_CACHE (priv->book_cache));
		g_object_unref (priv->book_cache);
		priv->book_cache = NULL;
	}

	e_book_backend_set_is_writable (E_BOOK_BACKEND (backend), FALSE);
	e_book_backend_notify_writable (E_BOOK_BACKEND (backend), FALSE);
	e_book_backend_notify_connection_status (E_BOOK_BACKEND (backend), FALSE);
	e_book_backend_set_is_loaded (E_BOOK_BACKEND (backend), FALSE);
#endif /* FIXME*/

	book_backend_kolab_notify_removed (self);
}

static void
e_book_backend_kolab_refresh (EBookBackendSync *backend,
                              EDataBook *book,
                              GCancellable *cancellable,
                              GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_warning ("%s: FIXME implement me", __func__);
}

#if 0 /* probably not needed */
static gboolean
e_book_backend_kolab_get_backend_property (EBookBackendSync *backend,
                                           EDataBook *book,
                                           GCancellable *cancellable,
                                           const gchar *prop_name,
                                           gchar **prop_value,
                                           GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
	e_return_data_book_error_val_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_val_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_val_if_fail (prop_name != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_val_if_fail (prop_value != NULL && *prop_value == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_error ("%s: FIXME implement me", __func__);

	return TRUE;
}
#endif

#if 0 /* probably not needed */
static gboolean
e_book_backend_kolab_set_backend_property (EBookBackendSync *backend,
                                           EDataBook *book,
                                           GCancellable *cancellable,
                                           const gchar *prop_name,
                                           const gchar *prop_value,
                                           GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
	e_return_data_book_error_val_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_val_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_val_if_fail (prop_name != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_val_if_fail (prop_value != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_error ("%s: FIXME implement me", __func__);
}
#endif

static void
e_book_backend_kolab_create_contacts (EBookBackendSync *backend,
                                      EDataBook *book,
                                      GCancellable *cancellable,
                                      const GSList *vcards,
                                      GSList **added_contacts,
                                      GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	EContact *econtact = NULL;
	const GSList *it = NULL;
	const gchar *vcard = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_if_fail (vcards != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (added_contacts != NULL && *added_contacts == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	for (it = vcards; it != NULL; it = g_slist_next (it)) {
		vcard = (const gchar *) it->data;
		econtact = e_contact_new_from_vcard (vcard);
		if (econtact == NULL) {
			g_warning ("%s()[%u] error creating contact from vcard:\n%s",
			           __func__, __LINE__, vcard);
			tmp_err = e_data_book_create_error (E_DATA_BOOK_STATUS_OTHER_ERROR, NULL);
			goto exit;
		}

		ok = kolab_util_contact_cache_assure_uid_on_econtact (priv->book_cache,
		                                                      priv->book_koma,
		                                                      econtact,
		                                                      priv->book_uri,
		                                                      FALSE,
		                                                      cancellable,
		                                                      &tmp_err);
		if (! ok)
			goto exit;

		ok = kolab_util_contact_store (econtact,
		                               priv->book_koma,
		                               priv->book_uri,
		                               cancellable,
		                               &tmp_err);
		if (! ok)
			goto exit;

		*added_contacts = g_slist_append (*added_contacts, econtact);
	}

 exit:

	if (tmp_err != NULL) {
		if (econtact != NULL)
			g_object_unref (econtact);
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
	}
}

static void
e_book_backend_kolab_remove_contacts (EBookBackendSync *backend,
                                      EDataBook *book,
                                      GCancellable *cancellable,
                                      const GSList *id_list,
                                      GSList **removed_ids,
                                      GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	const GSList *it = NULL;
	gchar *sourcename = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_if_fail (id_list != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (removed_ids != NULL && *removed_ids == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	sourcename = kolab_util_backend_get_relative_path_from_uri (priv->book_uri);

	for (it = id_list; it != NULL; it = g_slist_next (it)) {
		const gchar *uid = it->data;
		ok = kolab_mail_access_delete_by_uid (priv->book_koma,
		                                      uid,
		                                      sourcename,
		                                      cancellable,
		                                      &tmp_err);
		if (! ok) {
			if (tmp_err->code != KOLAB_BACKEND_ERROR_NOTFOUND)
				goto exit;

			g_warning ("%s()[%u]: %s", __func__, __LINE__, tmp_err->message);
			g_error_free (tmp_err);
			tmp_err = NULL;
		}

		*removed_ids = g_slist_append (*removed_ids, g_strdup(uid));
	}

 exit:
	g_free (sourcename);

	if (tmp_err != NULL) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
	}
}

static void
e_book_backend_kolab_modify_contacts (EBookBackendSync *backend,
                                      EDataBook *book,
                                      GCancellable *cancellable,
                                      const GSList *vcards,
                                      GSList **modified_contacts,
                                      GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	EContact *econtact = NULL;
	const GSList *it = NULL;
	const gchar *vcard = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_if_fail (vcards != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (modified_contacts != NULL && *modified_contacts == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	for (it = vcards; it != NULL; it = g_slist_next (vcards)) {
		vcard = (const gchar *) it->data;
		econtact = e_contact_new_from_vcard (vcard);
		if (econtact == NULL) {
			g_warning ("%s()[%u] error creating contact from vcard:\n%s",
			           __func__, __LINE__, vcard);
			tmp_err = e_data_book_create_error (E_DATA_BOOK_STATUS_OTHER_ERROR, NULL);
			goto exit;
		}

		ok = kolab_util_contact_store (econtact,
		                               priv->book_koma,
		                               priv->book_uri,
		                               cancellable,
		                               &tmp_err);
		if (! ok)
			goto exit;

		*modified_contacts = g_slist_append (*modified_contacts,
		                                     econtact);
	}

 exit:
	if (tmp_err != NULL) {
		if (econtact != NULL)
			g_object_unref (econtact);
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
	}
}

static void
e_book_backend_kolab_get_contact (EBookBackendSync *backend,
                                  EDataBook *book,
                                  GCancellable *cancellable,
                                  const gchar *id,
                                  gchar **vcard,
                                  GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	EContact *econtact = NULL;
	GError *tmp_err = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_if_fail (id != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (vcard != NULL && *vcard == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_debug ("%s()[%u] called.", __func__, __LINE__);

	econtact = kolab_util_contact_cache_get_object (priv->book_cache,
	                                                priv->book_koma,
	                                                priv->book_uri,
	                                                id,
	                                                FALSE,
	                                                cancellable,
	                                                &tmp_err);
	if (tmp_err != NULL) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
		return;
	}

	*vcard = e_vcard_to_string (E_VCARD (econtact),
	                            EVC_FORMAT_VCARD_30);
	g_object_unref (econtact);
}

static void
e_book_backend_kolab_get_contact_list (EBookBackendSync *backend,
                                       EDataBook *book,
                                       GCancellable *cancellable,
                                       const gchar *query,
                                       GSList **contacts,
                                       GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	GList *econtact_list = NULL;
	GList *it = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_if_fail (query != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (contacts != NULL && *contacts == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_debug ("%s()[%u] called.", __func__, __LINE__);
	g_debug (" + query: %s", query);

	/* Update the cache to contain all data requested. */
	ok = kolab_util_contact_cache_update_on_query (priv->book_cache,
	                                               priv->book_koma,
	                                               query,
	                                               priv->book_uri,
	                                               cancellable,
	                                               &tmp_err);
	if (! ok) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
		return;
	}

	/* Fetch information from BackendCache */;
	econtact_list = kolab_util_contact_cache_get_contacts (priv->book_cache,
	                                                       priv->book_koma,
	                                                       query,
	                                                       priv->book_uri,
	                                                       cancellable,
	                                                       &tmp_err);
	if (tmp_err != NULL) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
		return;
	}

	for (it = econtact_list; it != NULL; it = g_list_next (it)) {
		EContact *econtact = it->data;
		gchar *vcard = e_vcard_to_string (E_VCARD (econtact),
		                                  EVC_FORMAT_VCARD_30);
		*contacts = g_slist_append (*contacts, vcard);
		g_object_unref (econtact);
	}

	g_list_free (econtact_list);
}

static void
e_book_backend_kolab_get_contact_list_uids (EBookBackendSync *backend,
                                            EDataBook *book,
                                            GCancellable *cancellable,
                                            const gchar *query,
                                            GSList **contacts_uids,
                                            GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	GList *econtact_list = NULL;
	GList *it = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_if_fail (query != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_if_fail (contacts_uids != NULL && *contacts_uids == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	/* TODO This function currently works the same way as
	 *      e_book_backend_kolab_get_contact_list, since
	 *      we need to update the local cache information
	 *      when querying UIDs from KolabMailAccess.
	 *
	 *      Once this is issue is resolved, we can simply
	 *      query the KolabMailAccess instance for UIDs,
	 *      which is a far less i/o intensive operation
	 *      than actually querying the EContact objects
	 *      (the latter involves reading Kolab mail data
	 *      from the disk cache and conversion to EContact)
	 */

	/* Update the cache to contain all data requested. */
	ok = kolab_util_contact_cache_update_on_query (priv->book_cache,
	                                               priv->book_koma,
	                                               query,
	                                               priv->book_uri,
	                                               cancellable,
	                                               &tmp_err);
	if (! ok) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
		return;
	}

	/* Fetch information from BackendCache */;
	econtact_list = kolab_util_contact_cache_get_contacts (priv->book_cache,
	                                                       priv->book_koma,
	                                                       query,
	                                                       priv->book_uri,
	                                                       cancellable,
	                                                       &tmp_err);
	if (tmp_err != NULL) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
		return;
	}

	for (it = econtact_list; it != NULL; it = g_list_next (it)) {
		EContact *econtact = it->data;
		gchar *uid = e_contact_get (econtact,
		                            E_CONTACT_UID);
		*contacts_uids = g_slist_append (*contacts_uids, uid);
		g_object_unref (econtact);
	}

	g_list_free (econtact_list);
}

static gboolean
e_book_backend_kolab_get_backend_property (EBookBackendSync *backend,
                                           EDataBook *book,
                                           GCancellable *cancellable,
                                           const gchar *prop_name,
                                           gchar **prop_value,
                                           GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	gboolean processed = TRUE;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
	e_return_data_book_error_val_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_val_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
	(void)cancellable; /* cancellable not used */
	e_return_data_book_error_val_if_fail (prop_name != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_return_data_book_error_val_if_fail (prop_value != NULL && *prop_value == NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
		*prop_value = g_strdup ("net,bulk-removes,contact-lists,do-initial-query");
#if 0
		if (e_book_backend_mapi_is_marked_for_offline (ebma))
			*prop_value = g_strdup ("net,bulk-removes,contact-lists,do-initial-query");
		else
			*prop_value = g_strdup ("net,bulk-removes,contact-lists");
#endif
	} else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
		*prop_value = g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
	} else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
		GSList *fields = kolab_utils_contact_get_supported_fields ();
		*prop_value = e_data_book_string_slist_to_comma_string (fields);
		g_slist_free (fields);
	} else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_AUTH_METHODS)) {
		*prop_value = g_strdup ("plain/password");
	} else {
		processed = FALSE;
	}

	return processed;
}

static void
e_book_backend_kolab_authenticate_user (EBookBackendSync *backend,
                                        GCancellable *cancellable,
                                        ECredentials *credentials,
                                        GError **error)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	KolabSettingsHandler *ksettings = NULL;
	const gchar *cred_user = NULL;
	const gchar *cred_pwd  = NULL;
	const gchar *kset_user = NULL;
	gboolean online = FALSE;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_return_if_fail (error == NULL || *error == NULL);
	e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
	/* cancellable may be NULL */
	e_return_data_book_error_if_fail (credentials != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_debug ("%s()[%u] called.", __func__, __LINE__);

	ksettings = kolab_mail_access_get_settings_handler (priv->book_koma);

	/* warn about a possible inconsistency in user names */
	kset_user = kolab_settings_handler_get_char_field (ksettings,
	                                                   KOLAB_SETTINGS_HANDLER_CHAR_FIELD_KOLAB_USER_NAME,
	                                                   &tmp_err);
	if (tmp_err != NULL) {
		g_object_unref (ksettings);
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
		return;
	}
	cred_user = e_credentials_peek (credentials,
	                                E_CREDENTIALS_KEY_USERNAME);
	if (g_strcmp0 (kset_user, cred_user) != 0) {
		g_warning ("%s()[%u] username from argument and username in "
		           "KolabSettingsHandler do not match: %s vs. %s",
		           __func__, __LINE__, cred_user, kset_user);
	}

	cred_pwd = e_credentials_peek (credentials,
	                               E_CREDENTIALS_KEY_PASSWORD);

	kolab_util_backend_prepare_settings (ksettings,
	                                     NULL,
	                                     NULL,
	                                     NULL,
	                                     cred_pwd,
	                                     NULL,
	                                     NULL);
	g_object_unref (ksettings);

	priv->auth_received = TRUE;

	online = e_backend_get_online (E_BACKEND (backend));
	(void) kolab_util_backend_deploy_mode_by_backend (priv->book_koma,
	                                                  online,
	                                                  cancellable,
	                                                  &tmp_err);
	if (tmp_err != NULL) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
		return;
	}

	ok = book_backend_kolab_notify_opened (self, &tmp_err);
	if (! ok) {
		kolab_util_contact_err_to_edb_err (error, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
	}

#if 0 /* FIXME old */
	g_debug (" + user: %s\n + auth_method: %s",
	         user, auth_method);
	g_debug ("%s()[%u]    username = %s",
	         __func__, __LINE__, user);
	g_debug ("%s()[%u] auth_method = %s",
	         __func__, __LINE__, auth_method);
	password_placeholder = g_strdup (passwd);
	password_placeholder = g_strcanon (password_placeholder, "", '*');
	g_debug ("%s()[%u]    password = %s",
	         __func__, __LINE__, password_placeholder);
	g_free (password_placeholder);

	ksettings = kolab_mail_access_get_settings_handler (priv->book_koma);
	tmp_user = kolab_settings_handler_get_char_field (ksettings,
	                                                  KOLAB_SETTINGS_HANDLER_CHAR_FIELD_KOLAB_USER_NAME,
	                                                  &error);
	if (error != NULL) {
		g_warning ("%s()[%u]: %s",
		           __func__, __LINE__, error->message);
		g_error_free (error);
		error = NULL;
	}
	if (g_strcmp0 (tmp_user, user) != 0)
		g_warning (" + username from argument and username in "
		           "KolabSettingsHandler do not match: %s vs. %s",
		           tmp_user, user);
	sourcename = kolab_util_backend_get_relative_path_from_uri (priv->book_uri);

	kolab_util_backend_prepare_settings (ksettings,
	                                     NULL,
	                                     NULL,
	                                     NULL,
	                                     passwd,
	                                     NULL,
	                                     NULL);

	priv->auth_received = TRUE;
	ok = kolab_util_backend_deploy_mode_by_backend (priv->book_koma,
	                                                priv->book_mode);

	return GNOME_Evolution_Addressbook_Success;
#endif /* FIXME */
}

static void
e_book_backend_kolab_start_book_view (EBookBackend *backend,
                                      EDataBookView *book_view)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	GList *econtact_list = NULL;
	GList *it = NULL;
	const gchar *query = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;
	GError *notify_err = NULL;

	g_return_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend));
	g_return_if_fail (E_IS_DATA_BOOK_VIEW (book_view));

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_mutex_lock (active_book_views_lock);

	g_object_ref (book_view); /* unref()d in stop_view() */

	query = e_data_book_view_get_card_query (book_view);

	/* First update the BackendCache to the current situation. */
	ok = kolab_util_contact_cache_update_on_query (priv->book_cache,
	                                               priv->book_koma,
	                                               query,
	                                               priv->book_uri,
	                                               NULL, /* FIXME GCancellable */
	                                               &tmp_err);
	if (! ok)
		goto exit;

	econtact_list = kolab_util_contact_cache_get_contacts (priv->book_cache,
	                                                       priv->book_koma,
	                                                       query,
	                                                       priv->book_uri,
	                                                       NULL, /* FIXME GCancellable */
	                                                       &tmp_err);
	if (tmp_err != NULL)
		goto exit;

	/* Then deliver requests from there. */
	for (it = econtact_list; it != NULL; it = g_list_next (it)) {
		EContact *econtact = E_CONTACT (it->data);
		e_data_book_view_notify_update (book_view, econtact);
		g_object_unref (econtact);
	}

 exit:

	if (tmp_err != NULL) {
		kolab_util_contact_err_to_edb_err (&notify_err, tmp_err, __func__, __LINE__);
		g_error_free (tmp_err);
	}

	e_data_book_view_notify_complete (book_view, notify_err);

	g_mutex_unlock (active_book_views_lock);

	if (notify_err != NULL)
		g_error_free (notify_err);

	if (econtact_list != NULL)
		g_list_free (econtact_list);

}

static void
e_book_backend_kolab_stop_book_view (EBookBackend *backend,
                                     EDataBookView *book_view)
{
	EBookBackendKolab *self = NULL;
	EBookBackendKolabPrivate *priv = NULL;
	EDataBookView *bv = NULL;

	g_return_if_fail (E_IS_BOOK_BACKEND_KOLAB (backend));
	g_return_if_fail (E_IS_DATA_BOOK_VIEW (book_view));

	self = E_BOOK_BACKEND_KOLAB (backend);
	priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_mutex_lock (active_book_views_lock);

	e_data_book_view_notify_complete (book_view, NULL);

	g_object_unref (book_view); /* ref()d in start_view() */

	g_mutex_unlock (active_book_views_lock);
}

/*----------------------------------------------------------------------------*/
/* object/class init */

static void
e_book_backend_kolab_init (EBookBackendKolab *backend)
{
	EBookBackendKolabPrivate *priv = E_BOOK_BACKEND_KOLAB_PRIVATE (backend);

	/* We're inside a thread, and each of our siblings
	 * (other configured ECalBackendKolab instances) is
	 * so as well. Depending on how the threads are
	 * started, checking for lock existence (and creating
	 * one if it is not) in *this* place can still be
	 * racy. Let's hope the threads are started one
	 * at a time, in which case this solution will work.
	 * The whole handling of living KolabMailAccess
	 * instances would better be moved to a separate
	 * library...
	 *
	 */
	if (koma_objects_lock == NULL)
		koma_objects_lock = g_mutex_new ();
	g_mutex_lock (koma_objects_lock);
	if (koma_objects == NULL)
		koma_objects = g_hash_table_new_full (g_direct_hash,
		                                      g_direct_equal,
		                                      g_free,
		                                      g_object_unref);
	g_mutex_unlock (koma_objects_lock);
	/* active cal views are shared in the same way,
	 * so we do the locking for them analogous
	 * to the KolabMailAccess instances
	 */
	if (active_book_views_lock == NULL)
		active_book_views_lock = g_mutex_new ();

	g_debug ("%s()[%u] called.", __func__, __LINE__);

	priv->book_cache = NULL;
	priv->book_uri = NULL;
	priv->book_koma = NULL;
	priv->auth_received = FALSE;
	priv->mode_switch_err = NULL;

	g_signal_connect (E_BACKEND (backend), "notify::online", G_CALLBACK (book_backend_kolab_signal_online_cb), NULL);
} /* e_book_backend_kolab_init () */

static void
e_book_backend_kolab_dispose (GObject *object)
{
	EBookBackend *self = E_BOOK_BACKEND (object);
	EBookBackendKolabPrivate *priv = E_BOOK_BACKEND_KOLAB_PRIVATE (self);

	g_debug ("%s()[%u] called.", __func__, __LINE__);
#if 0 /* FIXME */
	if (e_book_backend_is_loaded (self)) {
		e_book_backend_set_is_writable (self, FALSE);
		e_book_backend_notify_writable (self, FALSE);
		e_book_backend_notify_connection_status (self, FALSE);
		e_book_backend_set_is_loaded (self, FALSE);
	}
#endif

	g_warning ("%s()[%u] FIXME KolabMailAccess (table) disposal", __func__, __LINE__);
#if 0 /* FIXME (check g_object_toggle_ref cleanup callback) */
	if (priv->book_koma != NULL) {
		g_object_unref (priv->book_koma);
		priv->book_koma = NULL;
	}
#endif
	if (priv->book_cache != NULL) {
		(void) e_file_cache_remove (E_FILE_CACHE (priv->book_cache));
		g_object_unref (priv->book_cache);
		priv->book_cache = NULL;
	}
}

static void
e_book_backend_kolab_finalize (GObject *object)
{
	EBookBackendKolabPrivate *priv = E_BOOK_BACKEND_KOLAB_PRIVATE (object);

	g_debug ("%s()[%u] called.", __func__, __LINE__);

	if (priv->book_uri != NULL)
		g_free (priv->book_uri);
	if (priv->mode_switch_err != NULL) {
		g_warning ("%s()[%u]: %s",
		           __func__, __LINE__, priv->mode_switch_err->message);
		g_error_free (priv->mode_switch_err);
	}

	G_OBJECT_CLASS (e_book_backend_kolab_parent_class)->finalize (object);
} /* e_book_backend_kolab_finalize () */

static void
e_book_backend_kolab_class_init (EBookBackendKolabClass *klass)
{
	GObjectClass* object_class = G_OBJECT_CLASS (klass);
	EBookBackendClass *backend_class = E_BOOK_BACKEND_CLASS (klass);
	EBookBackendSyncClass *sync_class = E_BOOK_BACKEND_SYNC_CLASS (klass);

	g_debug ("%s()[%u] called.", __func__, __LINE__);

	g_type_class_add_private (klass, sizeof (EBookBackendKolabPrivate));

	object_class->dispose = e_book_backend_kolab_dispose;
	object_class->finalize = e_book_backend_kolab_finalize;

	/* Backend parent class methods methods not covered in the sync backend part */
	backend_class->start_book_view = e_book_backend_kolab_start_book_view;
	backend_class->stop_book_view = e_book_backend_kolab_stop_book_view;

	/* Sync backend class functions */
	sync_class->open_sync = e_book_backend_kolab_open;
	sync_class->remove_sync = e_book_backend_kolab_remove;
	sync_class->refresh_sync = e_book_backend_kolab_refresh;
	sync_class->get_backend_property_sync = e_book_backend_kolab_get_backend_property;
#if 0 /* probably not needed */
	sync_class->set_backend_property_sync = e_book_backend_kolab_set_backend_property;
#endif
	sync_class->create_contacts_sync = e_book_backend_kolab_create_contacts;
	sync_class->remove_contacts_sync = e_book_backend_kolab_remove_contacts;
	sync_class->modify_contacts_sync = e_book_backend_kolab_modify_contacts;
	sync_class->get_contact_sync = e_book_backend_kolab_get_contact;
	sync_class->get_contact_list_sync = e_book_backend_kolab_get_contact_list;
	sync_class->get_contact_list_uids_sync = e_book_backend_kolab_get_contact_list_uids;
	sync_class->authenticate_user_sync = e_book_backend_kolab_authenticate_user;
} /* e_book_backend_kolab_class_init () */


/*----------------------------------------------------------------------------*/
/* public API */


/*----------------------------------------------------------------------------*/

#if 0 /* FIXME old */

/**
 * e_book_backend_kolab_set_mode:
 * @backend: A Kolab addressbook backend.
 * @mode: Mode to change to.
 *
 * Sets the mode of the addressbook backend to the given mode.
 */
static void
e_book_backend_kolab_set_mode (EBookBackend *backend,
                               GNOME_Evolution_Addressbook_BookMode mode)
{
	EBookBackendKolab *kolab = E_BOOK_BACKEND_KOLAB (backend);
	EBookBackendKolabPrivate *priv = E_BOOK_BACKEND_KOLAB_PRIVATE (kolab);
	KolabMailAccessOpmodeID koma_mode;
	KolabMailAccessOpmodeID tmp_mode;
	g_debug ("%s()[%u] called with mode: %u.", __func__, __LINE__, mode);
	switch (mode) {
	case GNOME_Evolution_Addressbook_MODE_ANY:
	case GNOME_Evolution_Addressbook_MODE_REMOTE:
		g_debug (" + got argument E_DATA_BOOK_MODE_ANY/E_DATA_BOOK_MODE_REMOTE, "
		         "switching backend_mode to E_DATA_BOOK_MODE_REMOTE.");
		priv->book_mode = GNOME_Evolution_Addressbook_MODE_REMOTE;
		koma_mode = KOLAB_MAIL_ACCESS_OPMODE_ONLINE;
		break;
	case GNOME_Evolution_Addressbook_MODE_LOCAL:
		g_debug (" + got argument E_DATA_BOOK_MODE_LOCAL, switching backend_mode.");
		priv->book_mode = GNOME_Evolution_Addressbook_MODE_LOCAL;
		koma_mode = KOLAB_MAIL_ACCESS_OPMODE_OFFLINE;
		break;
	default:
		g_error ("%s() ran into an unknown arguments for mode, value %u. "
		         "This should not happen.", __func__, mode);
	}

	if (priv->auth_received == TRUE)
		tmp_mode = kolab_util_backend_deploy_mode_by_koma (priv->book_koma,
		                                                   koma_mode);
	else
		tmp_mode = KOLAB_MAIL_ACCESS_OPMODE_OFFLINE;

	e_book_backend_notify_connection_status (backend,
	                                         tmp_mode = KOLAB_MAIL_ACCESS_OPMODE_ONLINE ? TRUE : FALSE);
} /* e_book_backend_kolab_set_mode () */

#endif /* FIXME old */
