/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/***************************************************************************
 *            camel-kolab-imapx-store.c
 *
 *  Fri Sep  3 12:48:31 2010
 *  Copyright  2010  Christian Hilberg
 *  <hilberg@kernelconcepts.de>
 ****************************************************************************/

/*
 * 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 main.c; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
 */

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

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib/gi18n-lib.h>

#include <libekolabutil/camel-system-headers.h>

#include <camel/providers/imapx/camel-imapx-store-summary.h>
#include <camel/providers/imapx/camel-imapx-extd-server.h>
#include <camel/providers/imapx/camel-imapx-utils.h>

#include <libekolabutil/kolab-util-error.h>

#include "camel-kolab-imapx-conn-manager.h"
#include "camel-kolab-imapx-metadata.h"
#include "camel-kolab-imapx-settings.h"
#include "camel-kolab-imapx-store.h"

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

static GInitableIface *parent_initable_iface = NULL;
static CamelNetworkServiceInterface *parent_service_iface = NULL;
static CamelSubscribableInterface *parent_subscribable_iface = NULL;

static CamelServiceClass *parent_service_class = NULL;
static CamelStoreClass *parent_store_class = NULL;

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

static gboolean imapx_store_shutdown (CamelKolabIMAPXStore *self, GError **err);

static void kolab_imapx_store_initable_init (GInitableIface *interface);
static void kolab_imapx_store_network_service_init (CamelNetworkServiceInterface *interface);
static void kolab_imapx_store_subscribable_init (CamelSubscribableInterface *interface);

typedef struct _CamelKolabIMAPXStorePrivate CamelKolabIMAPXStorePrivate;
struct _CamelKolabIMAPXStorePrivate {
	CamelKolabIMAPXServer *server;

	/* Used for syncronizing get_folder_info.
	 * TODO check whether we can re-use any other lock
	 */
	GMutex *kolab_finfo_lock;

	KolabFolderTypeID folder_create_type;
	KolabFolderContextID folder_context;
	gboolean folder_types_do_care[KOLAB_FOLDER_LAST_TYPE];
	GList *folder_names_do_care;
	CamelKolabImapxMetadata *kmd; /* Kolab metadata (differs from IMAPX metadata!) */

	gboolean is_initialized;
};

#define CAMEL_KOLAB_IMAPX_STORE_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CAMEL_TYPE_KOLAB_IMAPX_STORE, CamelKolabIMAPXStorePrivate))

G_DEFINE_TYPE_WITH_CODE (CamelKolabIMAPXStore,
                         camel_kolab_imapx_store,
                         CAMEL_TYPE_IMAPX_EXTD_STORE,
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                kolab_imapx_store_initable_init)
                         G_IMPLEMENT_INTERFACE (CAMEL_TYPE_NETWORK_SERVICE,
                                                kolab_imapx_store_network_service_init)
                         G_IMPLEMENT_INTERFACE (CAMEL_TYPE_SUBSCRIBABLE,
                                                kolab_imapx_store_subscribable_init))

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

static void
camel_kolab_imapx_store_init (CamelKolabIMAPXStore *self)
{
	CamelIMAPXStore *istore = NULL;
	CamelKolabIMAPXConnManager *cm = NULL;
	CamelKolabIMAPXStorePrivate *priv = NULL;
	gint ii = 0;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	/* replace existing connection manager with
	 * our own Kolab version. in case we need
	 * to explicitly reference the Kolab
	 * version of the connection manager, we will need
	 * to override the respective parent class functions
	 */
	istore = CAMEL_IMAPX_STORE (self);
	if (istore->con_man != NULL) {
		camel_imapx_extd_conn_manager_close_connections (istore->con_man);
		g_object_unref (istore->con_man);
	}
	cm = camel_kolab_imapx_conn_manager_new (self);
	istore->con_man = CAMEL_IMAPX_CONN_MANAGER (cm);

	/* folder info lock */
	priv->kolab_finfo_lock = g_mutex_new ();

	/* default folder type to create (for use in Evo) */
	priv->folder_create_type = KOLAB_FOLDER_TYPE_EMAIL;

	/* default folder context (for use in Evo) */
	priv->folder_context = KOLAB_FOLDER_CONTEXT_EMAIL;

	/* folder types to care for with this CamelKolabIMAPXStore
	 * instance.
	 * Default: Email and unknown (so no need to reconfigure
	 * this instance for use in Evo). Needs to be reconfigured
	 * when used in ECal/EBook backends
	 */
	for (ii = 0; ii < KOLAB_FOLDER_LAST_TYPE; ii++)
		priv->folder_types_do_care[ii] = FALSE;
	priv->folder_types_do_care[KOLAB_FOLDER_TYPE_UNKNOWN]         = TRUE;
	priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL]           = TRUE;
	priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL_INBOX]     = TRUE;
	priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL_DRAFTS]    = TRUE;
	priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL_SENTITEMS] = TRUE;
	priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL_JUNKEMAIL] = TRUE;

	priv->folder_names_do_care = NULL;

	/* metadata db and lookup table */
	priv->kmd = camel_kolab_imapx_metadata_new ();

	priv->is_initialized = FALSE;
}

static void
camel_kolab_imapx_store_dispose (GObject *object)
{
	CamelKolabIMAPXStore *self = NULL;
	CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (self);

	self = CAMEL_KOLAB_IMAPX_STORE (object);
	istore = CAMEL_IMAPX_STORE (self);

	/* disconnect service and unref the connection manager.
	 * see imapx_store_dispose() in camel-imapx-store.c
	 */
	if (istore->con_man != NULL) {
		camel_service_disconnect_sync (CAMEL_SERVICE (self),
		                               TRUE,
		                               NULL);
		g_object_unref (istore->con_man);
		istore->con_man = NULL;
		/* this part will now be skipped
		 * in the parent's dispose() function
		 */
	}

	/* Chain up to parent's dispose() method. */
	G_OBJECT_CLASS (camel_kolab_imapx_store_parent_class)->dispose (object);
}

static void
camel_kolab_imapx_store_finalize (GObject *object)
{
	CamelKolabIMAPXStore *self = NULL;
	CamelKolabIMAPXStorePrivate *priv = NULL;
	GError *tmp_err = NULL;
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (object));

	self = CAMEL_KOLAB_IMAPX_STORE (object);
	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	ok = imapx_store_shutdown (self, &tmp_err);
	if (! ok) {
		/* TODO
		 * Uninitialization, which can fail,
		 * should be done in a place where we can
		 * propagate an error - just, GInitable does
		 * not provide for uninitialization either...
		 */
		g_warning ("%s: %s",
		           __func__, tmp_err->message);
		g_error_free (tmp_err);
	}
	camel_kolab_imapx_metadata_free (priv->kmd);

	while (! g_mutex_trylock (priv->kolab_finfo_lock));
	g_mutex_unlock (priv->kolab_finfo_lock);
	g_mutex_free (priv->kolab_finfo_lock);

	if (priv->folder_names_do_care != NULL) {
		GList *list_ptr = priv->folder_names_do_care;
		while (list_ptr != NULL) {
			g_free (list_ptr);
			list_ptr = g_list_next (list_ptr);
		}
		g_free (priv->folder_names_do_care);
	}

	/* Chain up to parent's finalize() method. */
	G_OBJECT_CLASS (camel_kolab_imapx_store_parent_class)->finalize (object);
}

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

static gboolean
imapx_store_shutdown (CamelKolabIMAPXStore *self,
                      GError **err)
{
	CamelKolabIMAPXStorePrivate *priv = NULL;
	GError *tmp_err = NULL;
	gboolean ok = FALSE;

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

	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	if (priv->is_initialized == FALSE) {
		return TRUE;
	}

	ok = camel_kolab_imapx_metadata_shutdown (priv->kmd,
	                                          &tmp_err);

	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	priv->is_initialized = FALSE;

	return TRUE;
}

static CamelFolder*
imapx_store_get_folder_offline (CamelStore *self,
                                const gchar *folder_name,
                                guint32 flags,
                                GError **err)
{
	/* This function is a dupe of extd_store_get_folder_offline() in
	 * CamelIMAPXExtdStore, which is a dupe of get_folder_offline()
	 * in CamelIMAPXStore.
	 * We need to dupe it in order to return a CamelKolabIMAPXFolder
	 * (disguised as a CamelFolder). Upstream fixes need to be applied
	 * here, too.
	 */

	CamelKolabIMAPXStore *myself = CAMEL_KOLAB_IMAPX_STORE (self);
	CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (self);
	CamelService *service = CAMEL_SERVICE (self);
	CamelFolder *new_folder = NULL;
	CamelStoreInfo *si = NULL;
	const gchar *user_cache_dir = NULL;

	g_assert (folder_name != NULL);
	(void)flags; /* FIXME where do we need this? */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	user_cache_dir = camel_service_get_user_cache_dir (service);

	si = camel_store_summary_path (CAMEL_STORE_SUMMARY (imapx_store->summary), folder_name);
	if (si) {
		gchar *folder_dir, *storage_path;

		/* Note: Although the INBOX is defined to be case-insensitive in the IMAP RFC
		 * it is still up to the server how to acutally name it in a LIST response. Since
		 * we stored the name as the server provided it us in the summary we take that name
		 * to look up the folder.
		 * But for the on-disk cache we do always capitalize the Inbox no matter what the
		 * server provided.
		 */
		if (!g_ascii_strcasecmp (folder_name, "INBOX"))
			folder_name = "INBOX";

		storage_path = g_build_filename (user_cache_dir, "folders", NULL);
		folder_dir = imapx_path_to_physical (storage_path, folder_name);
		g_free (storage_path);
		/* FIXME */ /* (create CamelKolabIMAPXFolder) */
		new_folder = CAMEL_FOLDER (camel_kolab_imapx_folder_new (myself,
		                                                         folder_dir,
		                                                         folder_name,
		                                                         err));
		g_free (folder_dir);
		camel_store_summary_info_free (CAMEL_STORE_SUMMARY (imapx_store->summary), si);
	} else {
		g_set_error (err,
		             CAMEL_STORE_ERROR,
		             CAMEL_STORE_ERROR_NO_FOLDER,
		             _("No such folder %s"), folder_name);
	}

	return new_folder;
}

static KolabFolderTypeID
imapx_store_get_foldertype (CamelKolabIMAPXStore *self,
                            const gchar *foldername,
                            gboolean do_updatedb,
                            GCancellable *cancellable,
                            GError **err)
{
	/* TODO better error reporting */
	CamelKolabIMAPXStorePrivate *priv = NULL;
	CamelIMAPXServer *is = NULL;
	CamelIMAPXExtdServer *es = NULL;
	CamelImapxMetadata *md = NULL;
	CamelKolabImapxFolderMetadata *kfmd = NULL;
	CamelImapxMetadataSpec *spec = NULL;
	camel_imapx_metadata_proto_t proto = CAMEL_IMAPX_METADATA_PROTO_INVAL;
	gboolean db_ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	g_assert (foldername != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	/* hash table lookup */
	kfmd = g_hash_table_lookup (priv->kmd->kolab_metadata,
	                            foldername);
	if (kfmd != NULL)
		return kfmd->folder_type;

	/* if not in hash table: sqlite db lookup */
	kfmd = camel_kolab_imapx_metadata_db_lookup (priv->kmd->mdb,
	                                             foldername,
	                                             &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return KOLAB_FOLDER_TYPE_INVAL;
	}
	if (kfmd != NULL) {
		g_hash_table_insert (priv->kmd->kolab_metadata,
		                     g_strdup (foldername),
		                     kfmd);
		return kfmd->folder_type;
	}

	/* check whether we are online and get server */
	if (! camel_offline_store_get_online (CAMEL_OFFLINE_STORE (self))) {
		/* If we're here, we're trying to get the
		 * type of the folder in offline mode and
		 * the folder is not yet in the database -
		 * means we cannot return a folder type
		 * other than UNKNOWN
		 */
		g_debug ("%s: must be online to complete this operation",
		         __func__);
		return KOLAB_FOLDER_TYPE_UNKNOWN;
	}
	is = camel_imapx_store_get_server (CAMEL_IMAPX_STORE (self),
	                                   foldername,
	                                   cancellable,
	                                   &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return KOLAB_FOLDER_TYPE_INVAL;
	}
	es = CAMEL_IMAPX_EXTD_SERVER (is);

	/* if not in sqlite db: issue IMAP query */
	proto = camel_imapx_extd_server_metadata_get_proto (es);
	spec = camel_imapx_metadata_spec_new (proto,
	                                      foldername,
	                                      "/vendor/kolab/folder-type",
	                                      "value",
	                                      &tmp_err);
	if (spec == NULL) {
		g_propagate_error (err, tmp_err);
		return KOLAB_FOLDER_TYPE_INVAL;
	}

	/* resect all metadata gathered so far from CamelIMAPXServer */
	md = camel_imapx_extd_server_get_metadata (es,
	                                           spec,
	                                           TRUE,
	                                           cancellable,
	                                           &tmp_err);
	camel_imapx_metadata_spec_free (spec);

	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return KOLAB_FOLDER_TYPE_INVAL;
	}

	if (md != NULL) {
		/* create kolab "flat" data structure */
		camel_kolab_imapx_metadata_update (priv->kmd,
		                                   md,
		                                   proto);
		camel_imapx_metadata_free (md);

		/* stuff folder types into metadata_db */
		if (do_updatedb) {
			db_ok = camel_kolab_imapx_metadata_db_update (priv->kmd->mdb,
			                                              priv->kmd->kolab_metadata,
			                                              &tmp_err);
			if (!db_ok) {
				g_propagate_error (err, tmp_err);
				return KOLAB_FOLDER_TYPE_INVAL;
			}
		}
	}

	/* final hash table lookup */
	kfmd = g_hash_table_lookup (priv->kmd->kolab_metadata,
	                            foldername);
	if (kfmd == NULL)
		return KOLAB_FOLDER_TYPE_UNKNOWN;

	return kfmd->folder_type;
}

static gboolean
imapx_store_set_foldertype (CamelKolabIMAPXStore *self,
                            const gchar *foldername,
                            KolabFolderTypeID foldertype,
                            GCancellable *cancellable,
                            GError **err)
{
	CamelKolabIMAPXStorePrivate *priv = NULL;
	CamelIMAPXServer *is = NULL;
	CamelIMAPXExtdServer *es = NULL;
	CamelImapxMetadata *md = NULL;
	CamelImapxMetadataAnnotation *man = NULL;
	CamelImapxMetadataEntry *me = NULL;
	CamelImapxMetadataAttrib *ma = NULL;
	const gchar *typestring = NULL;
	camel_imapx_metadata_access_t acc = CAMEL_IMAPX_METADATA_ACCESS_SHARED;
	camel_imapx_metadata_proto_t proto = CAMEL_IMAPX_METADATA_PROTO_INVAL;
	gboolean metadata_ok = FALSE;
	gboolean db_ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	g_assert (foldername != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	/* check whether we are online and get server */
	if (! camel_offline_store_get_online (CAMEL_OFFLINE_STORE (self))) {
		/* If we're here, we're trying to set the
		 * type of the folder in offline mode -
		 * to be sure the folder type can be set,
		 * we must be online
		 */
		g_set_error (err,
		             KOLAB_CAMEL_KOLAB_ERROR,
		             KOLAB_CAMEL_KOLAB_ERROR_SERVER,
		             "Must be online to complete this operation");
		return FALSE;
	}
	is = camel_imapx_store_get_server (CAMEL_IMAPX_STORE (self),
	                                   foldername,
	                                   cancellable,
	                                   &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return KOLAB_FOLDER_TYPE_INVAL;
	}
	es = CAMEL_IMAPX_EXTD_SERVER (is);

	/* create local CamelImapxMetadata for setting type */
	acc = CAMEL_IMAPX_METADATA_ACCESS_SHARED;
	ma = camel_imapx_metadata_attrib_new ();
	ma->type[acc] = CAMEL_IMAPX_METADATA_ATTRIB_TYPE_UTF8;
	typestring = kolab_util_folder_type_get_string (foldertype);
	ma->data[acc] = g_byte_array_new ();
	g_byte_array_append (ma->data[acc],
	                     (guchar *) g_strdup (typestring),
	                     strlen (typestring));
	me = camel_imapx_metadata_entry_new ();
	g_hash_table_insert (me->attributes,
	                     g_strdup ("value"),
	                     ma);
	man = camel_imapx_metadata_annotation_new ();
	g_hash_table_insert (man->entries,
	                     g_strdup ("/vendor/kolab/folder-type"),
	                     me);
	proto = camel_imapx_extd_server_metadata_get_proto (es);
	md = camel_imapx_metadata_new (proto, FALSE);
	g_hash_table_insert (md->mboxes,
	                     g_strdup (foldername),
	                     man);

	/* set folder type on the server */
	metadata_ok = camel_imapx_extd_server_set_metadata (es,
	                                                    md,
	                                                    cancellable,
	                                                    &tmp_err);
	if (! metadata_ok) {
		camel_imapx_metadata_free (md);
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	/* create kolab "flat" data structure */
	camel_kolab_imapx_metadata_update (priv->kmd, md, proto);

	/* stuff folder types into metadata_db */
	db_ok = camel_kolab_imapx_metadata_db_update (priv->kmd->mdb,
	                                              priv->kmd->kolab_metadata,
	                                              &tmp_err);
	if (! db_ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	return TRUE;
}

static CamelFolderInfo*
imapx_store_folder_info_build_restricted (CamelKolabIMAPXStore *self,
                                          const CamelFolderInfo *fi,
                                          GCancellable *cancellable,
                                          GError **err)
{
	CamelKolabIMAPXStorePrivate *priv = NULL;
	KolabFolderTypeID folder_type = KOLAB_FOLDER_TYPE_INVAL;
	CamelFolderInfo *self_fi = NULL;
	CamelFolderInfo *next_fi = NULL;
	CamelFolderInfo *chld_fi = NULL;
	GError *tmp_err = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	/* fi may be NULL */
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	if (fi == NULL)
		return NULL;

	folder_type = imapx_store_get_foldertype (self,
	                                          fi->full_name,
	                                          TRUE,
	                                          cancellable,
	                                          &tmp_err);
	if (tmp_err != NULL) { /* FIXME Is err set if operation got cancelled? */
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	next_fi = imapx_store_folder_info_build_restricted (self,
	                                                    fi->next,
	                                                    cancellable,
	                                                    &tmp_err);
	if (tmp_err != NULL) {/* FIXME Is err set if operation got cancelled? */
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	chld_fi = imapx_store_folder_info_build_restricted (self,
	                                                    fi->child,
	                                                    cancellable,
	                                                    &tmp_err);
	if (tmp_err != NULL) {  /* FIXME Is err set if operation got cancelled? */
		if (next_fi != NULL)
			camel_store_free_folder_info (CAMEL_STORE (self), next_fi);
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	if ((chld_fi != NULL) || (priv->folder_types_do_care[folder_type])) {

		/* new CamelFolderInfo */
		self_fi = camel_folder_info_new ();
		/* pointers */
		self_fi->next = next_fi;
		self_fi->child = chld_fi;
		if (self_fi->child != NULL)
			self_fi->child->parent = self_fi;
		/* payload data */
		if (fi->full_name)
			self_fi->full_name = g_strdup (fi->full_name);
		if (fi->display_name)
			self_fi->display_name = g_strdup (fi->display_name);
		self_fi->flags = fi->flags;
		self_fi->unread = fi->unread;
		self_fi->total = fi->total;
		/* visibility tweaks */
		if (! priv->folder_types_do_care[folder_type]) {
			self_fi->flags |= CAMEL_FOLDER_NOSELECT;
			/* TODO more to do here? */
		}

		/* update local list of folder names to care for */
		if ((priv->folder_context != KOLAB_FOLDER_CONTEXT_EMAIL) &&
		    priv->folder_types_do_care[folder_type]) {
			priv->folder_names_do_care = g_list_prepend (priv->folder_names_do_care,
			                                             g_strdup (self_fi->full_name));
		}

		return self_fi;
	}

	return next_fi;
}

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

static gchar*
kolab_imapx_store_get_name (CamelService *service,
                            gboolean brief)
{
	/* This is an adjusted dupe of the CamelIMAPXStore
	 * imapx_get_name() function. Upstream fixes need
	 * to be applied here, too
	 */

	CamelNetworkSettings *network_settings;
	CamelSettings *settings;
	gchar *host;
	gchar *user;
	gchar *name;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (service));

	settings = camel_service_get_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	user = camel_network_settings_dup_user (network_settings);

	if (brief)
		name = g_strdup_printf (_("Kolab server %s"), host);
	else
		name = g_strdup_printf (_("Kolab service for %s on %s"), user, host);

	g_free (host);
	g_free (user);

	return name;
}

static gboolean
kolab_imapx_store_connect_sync (CamelService *service,
                                GCancellable *cancellable,
                                GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (service));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_service_class->connect_sync (service,
	                                         cancellable,
	                                         err);
	return ok;
}

static gboolean
kolab_imapx_store_disconnect_sync (CamelService *service,
                                   gboolean clean,
                                   GCancellable *cancellable,
                                   GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (service));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_service_class->disconnect_sync (service,
	                                            clean,
	                                            cancellable,
	                                            err);
	return ok;
}

static CamelAuthenticationResult
kolab_imapx_store_authenticate_sync (CamelService *service,
                                     const gchar *mechanism,
                                     GCancellable *cancellable,
                                     GError **err)
{
	CamelAuthenticationResult result = CAMEL_AUTHENTICATION_ERROR;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (service));
	/* mechanism may be NULL */
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, CAMEL_AUTHENTICATION_REJECTED);

	result = parent_service_class->authenticate_sync (service,
	                                                  mechanism,
	                                                  cancellable,
	                                                  err);
	return result;
}

static GList*
kolab_imapx_store_query_auth_types_sync (CamelService *service,
                                         GCancellable *cancellable,
                                         GError **err)
{
	GList *auth_types = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (service));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	auth_types = parent_service_class->query_auth_types_sync (service,
	                                                          cancellable,
	                                                          err);
	return auth_types;
}

static guint
kolab_imapx_store_name_hash (gconstpointer key)
{
	if (g_ascii_strcasecmp (key, "INBOX") == 0)
		return g_str_hash ("INBOX");
	else
		return g_str_hash (key);
}

static gint
kolab_imapx_store_name_equal (gconstpointer a,
                              gconstpointer b)
{
	gconstpointer aname = a, bname = b;

	if (g_ascii_strcasecmp(a, "INBOX") == 0)
		aname = "INBOX";
	if (g_ascii_strcasecmp(b, "INBOX") == 0)
		bname = "INBOX";
	return g_str_equal (aname, bname);
}

static gboolean
kolab_imapx_store_can_refresh_folder (CamelStore *self,
                                      CamelFolderInfo *finfo,
                                      GError **err)
{
	gboolean can = FALSE;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	g_assert (finfo != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	can = parent_store_class->can_refresh_folder (self,
	                                              finfo,
	                                              err);
	return can;
}

static CamelFolder*
kolab_imapx_store_get_folder_sync (CamelStore *self,
                                   const gchar *foldername,
                                   CamelStoreGetFolderFlags flags,
                                   GCancellable *cancellable,
                                   GError **err)
{
	CamelFolder *folder = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (self));
	g_assert (foldername != NULL);
	(void)cancellable; /* FIXME */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	folder = imapx_store_get_folder_offline (self,
	                                         foldername,
	                                         flags,
	                                         err);
	if (folder != NULL)
		g_assert (CAMEL_IS_KOLAB_IMAPX_FOLDER (folder));

	return folder;
}

static CamelFolderInfo*
kolab_imapx_store_get_folder_info_sync (CamelStore *self,
                                        const gchar *top,
                                        CamelStoreGetFolderInfoFlags flags,
                                        GCancellable *cancellable,
                                        GError **err)
{
	CamelKolabIMAPXStore *myself = NULL;
	CamelKolabIMAPXStorePrivate *priv = NULL;
	CamelFolderInfo *fi = NULL;
	CamelFolderInfo *k_fi = NULL;
	GError *tmp_err = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	/* top may be NULL */
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	myself = CAMEL_KOLAB_IMAPX_STORE (self);
	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (myself);

	g_mutex_lock (priv->kolab_finfo_lock);

	fi = parent_store_class->get_folder_info_sync (self,
	                                               top,
	                                               flags,
	                                               cancellable,
	                                               &tmp_err);
	if (tmp_err != NULL)
		goto exit;
	if (fi == NULL)
		goto exit;

	k_fi = imapx_store_folder_info_build_restricted (myself,
	                                                 fi,
	                                                 cancellable,
	                                                 &tmp_err);
 exit:
	if (fi != NULL)
		camel_store_free_folder_info (self, fi);

	g_mutex_unlock (priv->kolab_finfo_lock);

	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	return k_fi;
}

static CamelFolder*
kolab_imapx_store_get_junk_folder_sync (CamelStore *self,
                                        GCancellable *cancellable,
                                        GError **err)
{
	CamelFolder *folder = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	folder = parent_store_class->get_junk_folder_sync (self,
	                                                   cancellable,
	                                                   err);
	/* CamelIMAPXFolder gets the junk folder from
	 * its parent class, CamelFolder, and does not
	 * change it to a CamelIMAPXFolder. That means,
	 * if we really need a CamelKolabIMAPXFolder
	 * here, we need to clone the CamelFolder into
	 * a locally created instance
	 */
#if 0
	if (folder != NULL)
		g_assert (CAMEL_IS_KOLAB_IMAPX_FOLDER (folder));
#endif
	return folder;
}

static CamelFolder*
kolab_imapx_store_get_trash_folder_sync (CamelStore *self,
                                         GCancellable *cancellable,
                                         GError **err)
{
	CamelFolder *folder = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	folder = parent_store_class->get_trash_folder_sync (self,
	                                                    cancellable,
	                                                    err);
	/* see kolab_imapx_store_get_junk_folder_sync()
	 * why we do not have a CamelIMAPXExtdFolder
	 * here
	 */
#if 0
	if (folder != NULL)
		g_assert (CAMEL_IS_KOLAB_IMAPX_FOLDER (folder));
#endif
	return folder;
}

static CamelFolderInfo*
kolab_imapx_store_create_folder_sync (CamelStore *self,
                                      const gchar *parentname,
                                      const gchar *foldername,
                                      GCancellable *cancellable,
                                      GError **err)
{
	CamelKolabIMAPXStore *myself = NULL;
	CamelKolabIMAPXStorePrivate *priv = NULL;
	CamelFolderInfo *k_fi = NULL;
	CamelFolderInfo *fi = NULL;
	gchar *fullname = NULL;
	GError *tmp_err = NULL;
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	/* parentname may be NULL */ /* FIXME correct? */
	g_assert (foldername != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	myself = CAMEL_KOLAB_IMAPX_STORE (self);
	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (myself);

	fi = parent_store_class->create_folder_sync (self,
	                                             parentname,
	                                             foldername,
	                                             cancellable,
	                                             &tmp_err);
	if (tmp_err != NULL) {
		if (fi != NULL)
			/* should not happen */
			camel_store_free_folder_info (self, fi);
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	/* FIXME use Camel function(s) to create full_name */
	fullname = g_strdup_printf ("%s/%s", parentname, foldername);
	ok = imapx_store_set_foldertype (myself,
	                                 fullname,
	                                 priv->folder_create_type,
	                                 cancellable,
	                                 &tmp_err);
	g_free (fullname);
	if (! ok) {
		g_warning ("%s: setting type [%i] for [%s] on server failed.",
		           __func__, priv->folder_create_type, fullname);
		camel_store_free_folder_info (self, fi);
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	k_fi = imapx_store_folder_info_build_restricted (myself,
	                                                 fi,
	                                                 cancellable,
	                                                 &tmp_err);
	camel_store_free_folder_info (self, fi);

	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	return k_fi;
}

static gboolean
kolab_imapx_store_delete_folder_sync (CamelStore *self,
                                      const gchar *foldername,
                                      GCancellable *cancellable,
                                      GError **err)
{
	GError *tmp_err = NULL;
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	/* parentname may be NULL */ /* correct? */
	g_assert (foldername != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_store_class->delete_folder_sync (self,
	                                             foldername,
	                                             cancellable,
	                                             &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	/* FIXME remove metadata of deleted folder from CamelKolabIMAPXServer */

	return TRUE;
}

static gboolean
kolab_imapx_store_rename_folder_sync (CamelStore *self,
                                      const gchar *foldername_old,
                                      const gchar *foldername_new,
                                      GCancellable *cancellable,
                                      GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	g_assert (foldername_old != NULL);
	g_assert (foldername_new != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_store_class->rename_folder_sync (self,
	                                             foldername_old,
	                                             foldername_new,
	                                             cancellable,
	                                             err);

	/* FIXME update metadata of deleted folder on CamelKolabIMAPXServer */

	return ok;
}

static gboolean
kolab_imapx_store_noop_sync (CamelStore *self,
                             GCancellable *cancellable,
                             GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_store_class->noop_sync (self,
	                                    cancellable,
	                                    err);
	return ok;
}

static gboolean
kolab_imapx_store_set_folder_creation_type (CamelKolabIMAPXStore *self,
                                            KolabFolderTypeID type_id)
{
	CamelKolabIMAPXStorePrivate *priv = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	g_assert ((type_id > KOLAB_FOLDER_TYPE_UNKNOWN) &&
	          (type_id < KOLAB_FOLDER_LAST_TYPE));

	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	/* check that the given folder type id lies within the configured folder context */
	if (! kolab_util_folder_type_match_with_context_id (type_id, priv->folder_context))
		return FALSE;

	priv->folder_create_type = type_id;

	return TRUE;
}

static gboolean
kolab_imapx_store_set_folder_context (CamelKolabIMAPXStore *self,
                                      KolabFolderContextID context)
{
	CamelKolabIMAPXStorePrivate *priv = NULL;
	gint ii = 0;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	g_assert ((context > KOLAB_FOLDER_CONTEXT_INVAL) &&
	          (context < KOLAB_FOLDER_LAST_CONTEXT));

	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	for (ii = 0; ii < KOLAB_FOLDER_LAST_TYPE; ii++)
		priv->folder_types_do_care[ii] = FALSE;

	priv->folder_context = context;

	switch (context) {
	case KOLAB_FOLDER_CONTEXT_EMAIL:
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_UNKNOWN]		= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL]		= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL_INBOX]	= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL_DRAFTS]	= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL_SENTITEMS]	= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EMAIL_JUNKEMAIL]	= TRUE;
		break;
	case KOLAB_FOLDER_CONTEXT_CALENDAR:
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EVENT]		= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_EVENT_DEFAULT]	= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_JOURNAL]		= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_JOURNAL_DEFAULT]	= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_TASK]		= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_TASK_DEFAULT]	= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_NOTE]		= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_NOTE_DEFAULT]	= TRUE;
		break;
	case KOLAB_FOLDER_CONTEXT_CONTACT:
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_CONTACT]		= TRUE;
		priv->folder_types_do_care[KOLAB_FOLDER_TYPE_CONTACT_DEFAULT]	= TRUE;
		break;
	default:
		/* can't happen */
		g_assert_not_reached ();
	}

	return TRUE;
}

static KolabFolderTypeID
kolab_imapx_store_get_folder_type (CamelKolabIMAPXStore *self,
                                   const gchar *foldername,
                                   GCancellable *cancellable,
                                   GError **err)
{
	KolabFolderTypeID folder_type = KOLAB_FOLDER_TYPE_INVAL;
	GError *tmp_err = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));
	g_assert (foldername != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, KOLAB_FOLDER_TYPE_INVAL);

	folder_type = imapx_store_get_foldertype (self,
	                                          foldername,
	                                          TRUE,
	                                          cancellable,
	                                          &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return KOLAB_FOLDER_TYPE_INVAL;
	}

	return folder_type;
}

static GList*
kolab_imapx_store_resect_folder_list (CamelKolabIMAPXStore *self)
{
	CamelKolabIMAPXStorePrivate *priv = NULL;
	GList *folder_list = NULL;

	g_assert (CAMEL_IS_KOLAB_IMAPX_STORE (self));

	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	g_mutex_lock (priv->kolab_finfo_lock);

	folder_list = priv->folder_names_do_care;
	priv->folder_names_do_care = NULL;

	g_mutex_unlock (priv->kolab_finfo_lock);

	return folder_list;
}

/*----------------------------------------------------------------------------*/
/* interface functions */

static gboolean
kolab_imapx_store_initable_initialize (GInitable *initable,
                                       GCancellable *cancellable,
                                       GError **err)
{
	CamelKolabIMAPXStore *self = NULL;
	CamelKolabIMAPXStorePrivate *priv = NULL;
	CamelService *service = NULL;
	const gchar *cachepath = NULL;
	GError *tmp_err = NULL;
	gboolean ok = FALSE;

	g_assert (G_IS_INITABLE (initable));
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	self = CAMEL_KOLAB_IMAPX_STORE (initable);
	priv = CAMEL_KOLAB_IMAPX_STORE_PRIVATE (self);

	if (priv->is_initialized == TRUE) {
		return TRUE;
	}

	/* chain up to parent interface's init() method. */
	ok = parent_initable_iface->init (initable,
	                                  cancellable,
	                                  &tmp_err);
	if (! ok)
		goto exit;

	/* initialize metadata (db) */
	service = CAMEL_SERVICE (self);
	cachepath = camel_service_get_user_cache_dir (service);
	if (cachepath == NULL) {
		/* FIXME mark this as a translatable string */
		g_set_error (&tmp_err,
		             KOLAB_CAMEL_ERROR,
		             KOLAB_CAMEL_ERROR_GENERIC,
		             "Could not get user cache directory from Camel service");
		goto exit;
	}

	ok = camel_kolab_imapx_metadata_init (priv->kmd,
	                                      cachepath,
	                                      &tmp_err);
	if (! ok) {
		g_warning ("%s: %s",
		           __func__, tmp_err->message);
		goto exit;
	}

	priv->is_initialized = TRUE;
	g_debug ("%s: metadata database initialized", __func__);

 exit:
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	return TRUE;
}

static const gchar*
kolab_imapx_store_get_service_name (CamelNetworkService *service,
                                    CamelNetworkSecurityMethod method)
{
	const gchar *sn = NULL;

	g_assert (CAMEL_IS_NETWORK_SERVICE (service));

	/* use parent function for now */
	sn = parent_service_iface->get_service_name (service,
	                                             method);

	return sn;
}

static guint16
kolab_imapx_store_get_default_port (CamelNetworkService *service,
                                    CamelNetworkSecurityMethod method)
{
	guint16 port = 0;

	g_assert (CAMEL_IS_NETWORK_SERVICE (service));

	/* use parent function for now */
	port = parent_service_iface->get_default_port (service,
	                                               method);

	return port;
}

static gboolean
kolab_imapx_store_folder_is_subscribed (CamelSubscribable *subscribable,
                                        const gchar *foldername)
{
	gboolean subscribed = FALSE;

	g_assert (CAMEL_IS_SUBSCRIBABLE (subscribable));
	g_assert (foldername != NULL);

	/* use parent function for now */
	subscribed = parent_subscribable_iface->folder_is_subscribed (subscribable,
	                                                              foldername);

	return subscribed;
}

static gboolean
kolab_imapx_store_subscribe_folder_sync (CamelSubscribable *subscribable,
                                         const gchar *foldername,
                                         GCancellable *cancellable,
                                         GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_SUBSCRIBABLE (subscribable));
	g_assert (foldername != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	/* use parent function for now */
	ok = parent_subscribable_iface->subscribe_folder_sync (subscribable,
	                                                       foldername,
	                                                       cancellable,
	                                                       err);
	return ok;
}

static gboolean
kolab_imapx_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
                                           const gchar *foldername,
                                           GCancellable *cancellable,
                                           GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_SUBSCRIBABLE (subscribable));
	g_assert (foldername != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	/* use parent function for now */
	ok = parent_subscribable_iface->unsubscribe_folder_sync (subscribable,
	                                                         foldername,
	                                                         cancellable,
	                                                         err);
	return ok;
}

static void
kolab_imapx_store_initable_init (GInitableIface *interface)
{
	parent_initable_iface = g_type_interface_peek_parent (interface);
	interface->init = kolab_imapx_store_initable_initialize;
}

static void
kolab_imapx_store_network_service_init (CamelNetworkServiceInterface *interface)
{
	g_assert (CAMEL_IS_NETWORK_SERVICE_INTERFACE (interface));

	parent_service_iface = g_type_interface_peek_parent (interface);
	interface->get_service_name = kolab_imapx_store_get_service_name;
	interface->get_default_port = kolab_imapx_store_get_default_port;
}

static void
kolab_imapx_store_subscribable_init (CamelSubscribableInterface *interface)
{
	g_assert (CAMEL_IS_SUBSCRIBABLE_INTERFACE (interface));

	parent_subscribable_iface = g_type_interface_peek_parent (interface);
	interface->folder_is_subscribed = kolab_imapx_store_folder_is_subscribed;
	interface->subscribe_folder_sync = kolab_imapx_store_subscribe_folder_sync;
	interface->unsubscribe_folder_sync = kolab_imapx_store_unsubscribe_folder_sync;
}

/*----------------------------------------------------------------------------*/
/* class initialization */

static void
camel_kolab_imapx_store_class_init (CamelKolabIMAPXStoreClass *klass)
{
	GObjectClass *object_class;
	CamelServiceClass *service_class;
	CamelStoreClass *store_class;

	parent_service_class = CAMEL_SERVICE_CLASS (camel_kolab_imapx_store_parent_class);
	parent_store_class = CAMEL_STORE_CLASS (camel_kolab_imapx_store_parent_class);

	g_type_class_add_private (klass, sizeof (CamelKolabIMAPXStorePrivate));

	object_class = G_OBJECT_CLASS (klass);
	object_class->dispose = camel_kolab_imapx_store_dispose;
	object_class->finalize = camel_kolab_imapx_store_finalize;

	service_class = CAMEL_SERVICE_CLASS (klass);
	service_class->settings_type = CAMEL_TYPE_KOLAB_IMAPX_SETTINGS;
	service_class->get_name = kolab_imapx_store_get_name;
	service_class->connect_sync = kolab_imapx_store_connect_sync;
	service_class->disconnect_sync = kolab_imapx_store_disconnect_sync;
	service_class->authenticate_sync = kolab_imapx_store_authenticate_sync;
	service_class->query_auth_types_sync = kolab_imapx_store_query_auth_types_sync;

	store_class = CAMEL_STORE_CLASS (klass);
	store_class->hash_folder_name = kolab_imapx_store_name_hash;
	store_class->compare_folder_name = kolab_imapx_store_name_equal;
	store_class->can_refresh_folder = kolab_imapx_store_can_refresh_folder;
	store_class->free_folder_info = camel_store_free_folder_info_full;
	store_class->get_folder_sync = kolab_imapx_store_get_folder_sync;
	store_class->get_folder_info_sync = kolab_imapx_store_get_folder_info_sync;
	store_class->get_junk_folder_sync = kolab_imapx_store_get_junk_folder_sync;
	store_class->get_trash_folder_sync = kolab_imapx_store_get_trash_folder_sync;
	store_class->create_folder_sync = kolab_imapx_store_create_folder_sync;
	store_class->delete_folder_sync = kolab_imapx_store_delete_folder_sync;
	store_class->rename_folder_sync = kolab_imapx_store_rename_folder_sync;
	store_class->noop_sync = kolab_imapx_store_noop_sync;

	klass->set_folder_creation_type = kolab_imapx_store_set_folder_creation_type;
	klass->set_folder_context = kolab_imapx_store_set_folder_context;
	klass->get_folder_type = kolab_imapx_store_get_folder_type;
	klass->resect_folder_list = kolab_imapx_store_resect_folder_list;
}

/*----------------------------------------------------------------------------*/
/* API functions */

gboolean
camel_kolab_imapx_store_set_folder_creation_type (CamelKolabIMAPXStore *self,
                                                  KolabFolderTypeID type_id)
{
	CamelKolabIMAPXStoreClass *klass = NULL;
	gboolean ok = FALSE;

	g_return_val_if_fail (CAMEL_IS_KOLAB_IMAPX_STORE (self), FALSE);

	klass = CAMEL_KOLAB_IMAPX_STORE_GET_CLASS (self);
	ok = klass->set_folder_creation_type (self, type_id);

	return ok;
}

gboolean
camel_kolab_imapx_store_set_folder_context (CamelKolabIMAPXStore *self,
                                            KolabFolderContextID context)
{
	CamelKolabIMAPXStoreClass *klass = NULL;
	gboolean ok = FALSE;

	g_return_val_if_fail (CAMEL_IS_KOLAB_IMAPX_STORE (self), FALSE);

	klass = CAMEL_KOLAB_IMAPX_STORE_GET_CLASS (self);
	ok = klass->set_folder_context (self, context);

	return ok;
}

KolabFolderTypeID
camel_kolab_imapx_store_get_folder_type (CamelKolabIMAPXStore *self,
                                         const gchar *foldername,
                                         GCancellable *cancellable,
                                         GError **err)
{
	CamelKolabIMAPXStoreClass *klass = NULL;
	KolabFolderTypeID foldertype = KOLAB_FOLDER_TYPE_INVAL;

	g_return_val_if_fail (CAMEL_IS_KOLAB_IMAPX_STORE (self), KOLAB_FOLDER_TYPE_INVAL);

	klass = CAMEL_KOLAB_IMAPX_STORE_GET_CLASS (self);
	foldertype = klass->get_folder_type (self,
	                                     foldername,
	                                     cancellable,
	                                     err);
	return foldertype;
}

GList*
camel_kolab_imapx_store_resect_folder_list (CamelKolabIMAPXStore *self)
{
	CamelKolabIMAPXStoreClass *klass = NULL;
	GList *list = NULL;

	g_return_val_if_fail (CAMEL_IS_KOLAB_IMAPX_STORE (self), NULL);

	klass = CAMEL_KOLAB_IMAPX_STORE_GET_CLASS (self);
	list = klass->resect_folder_list (self);

	return list;
}

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