/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/***************************************************************************
 *            camel-imapx-extd-store.c
 *
 *  2011-12-05, 19:11:27
 *  Copyright 2011, Christian Hilberg
 *  <hilberg@unix-ag.org>
 ****************************************************************************/

/*
 * 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-imapx-settings.h"
#include "camel-imapx-utils.h"
#include "camel-imapx-extd-conn-manager.h"
#include "camel-imapx-extd-folder.h"
#include "camel-imapx-extd-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 void imapx_extd_store_initable_init (GInitableIface *interface);
static void imapx_extd_store_network_service_init (CamelNetworkServiceInterface *interface);
static void imapx_extd_store_subscribable_init (CamelSubscribableInterface *interface);

/* externs */
extern CamelServiceAuthType camel_imapx_password_authtype;

G_DEFINE_TYPE_WITH_CODE (CamelIMAPXExtdStore,
                         camel_imapx_extd_store,
                         CAMEL_TYPE_IMAPX_STORE,
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                imapx_extd_store_initable_init)
                         G_IMPLEMENT_INTERFACE (CAMEL_TYPE_NETWORK_SERVICE,
                                                imapx_extd_store_network_service_init)
                         G_IMPLEMENT_INTERFACE (CAMEL_TYPE_SUBSCRIBABLE,
                                                imapx_extd_store_subscribable_init))

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

static void
camel_imapx_extd_store_init (CamelIMAPXExtdStore *self)
{
	CamelIMAPXStore *istore = NULL;
	CamelIMAPXExtdConnManager *cm = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (self));

	/* replace existing connection manager with
	 * our own extended version. in case we need
	 * to explicitly reference the *extended*
	 * 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_conn_manager_close_connections (istore->con_man);
		g_object_unref (istore->con_man);
	}
	cm = camel_imapx_extd_conn_manager_new (self);
	istore->con_man = CAMEL_IMAPX_CONN_MANAGER (cm);
}

static void
camel_imapx_extd_store_dispose (GObject *object)
{
	CamelIMAPXExtdStore *self = CAMEL_IMAPX_EXTD_STORE (object);
	CamelIMAPXStore *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
		 */
	}

	G_OBJECT_CLASS (camel_imapx_extd_store_parent_class)->dispose (object);
}

static void
camel_imapx_extd_store_finalize (GObject *object)
{
	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (object));

	G_OBJECT_CLASS (camel_imapx_extd_store_parent_class)->finalize (object);
}

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

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

	CamelIMAPXExtdStore *myself = CAMEL_IMAPX_EXTD_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);
	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);

		new_folder = CAMEL_FOLDER (camel_imapx_extd_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;
}

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

static CamelAuthenticationResult
imapx_extd_store_authenticate_sync (CamelService *service,
                                    const gchar *mechanism,
                                    GCancellable *cancellable,
                                    GError **err)
{
	/* modified dupe of imapx_authenticate_sync() */

	CamelIMAPXStore *istore = NULL;
	CamelIMAPXServer *server = NULL;

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

	istore = CAMEL_IMAPX_STORE (service);

	/* CamelIMAPXConnManager sets this before calling
	 * camel_imapx_server_connect(), and then clears it
	 * immediately after, all while holding the recursive
	 * connection lock (CAMEL_SERVICE_REC_CONNECT_LOCK).
	 * Otherwise we'd have no way of knowing which server
	 * is trying to authenticate. */
	server = istore->authenticating_server;

	g_return_val_if_fail (CAMEL_IS_IMAPX_EXTD_SERVER (server), CAMEL_AUTHENTICATION_REJECTED);

	/* modified */
	return camel_imapx_extd_server_authenticate (server,
	                                             mechanism,
	                                             cancellable,
	                                             err);
}

static gboolean
imapx_extd_store_connect_sync (CamelService *service,
                               GCancellable *cancellable,
                               GError **err)
{
	CamelIMAPXStore *istore = NULL;
	CamelIMAPXServer *server = NULL;

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

	istore = CAMEL_IMAPX_STORE (service);
	server = camel_imapx_extd_store_get_server (istore,
	                                            NULL,
	                                            cancellable,
	                                            err);
	if (server != NULL) {
		g_object_unref (server);
		return TRUE;
	}

	return FALSE;
}

static GList*
imapx_extd_store_query_auth_types_sync (CamelService *service,
                                        GCancellable *cancellable,
                                        GError **err)
{
	/* modified dupe of imapx_query_auth_types_sync() */

	CamelIMAPXExtdStore *estore = NULL;
	CamelIMAPXExtdServer *eserver = NULL;
	CamelServiceAuthType *authtype = NULL;
	GList *sasl_types = NULL;
	GList *t = NULL;
	GList *next = NULL;
	gboolean connected = FALSE;

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

	estore = CAMEL_IMAPX_EXTD_STORE (service);

	if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (estore))) {
		g_set_error (err,
		             CAMEL_SERVICE_ERROR,
		             CAMEL_SERVICE_ERROR_UNAVAILABLE,
		             _("You must be working online to complete this operation"));
		return NULL;
	}

	camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);

	eserver = camel_imapx_extd_server_new (estore);

	connected = CAMEL_IMAPX_SERVER (eserver)->stream != NULL;
	if (!connected)
		connected = camel_imapx_extd_server_connect_to_server (CAMEL_IMAPX_SERVER (eserver),
		                                                       cancellable,
		                                                       err);
	camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
	if (!connected)
		return NULL;

	sasl_types = camel_sasl_authtype_list (FALSE);
	for (t = sasl_types; t; t = next) {
		authtype = t->data;
		next = t->next;

		if (!g_hash_table_lookup (CAMEL_IMAPX_SERVER (eserver)->cinfo->auth_types,
		                          authtype->authproto)) {
			sasl_types = g_list_remove_link (sasl_types, t);
			g_list_free_1 (t);
		}
	}

	g_object_unref (eserver);

	return g_list_prepend (sasl_types, &camel_imapx_password_authtype);
}

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

	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (self));
	g_assert (info != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

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

static void
imapx_extd_store_free_folder_info (CamelStore *self,
                                   CamelFolderInfo *fi)
{
	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (self));
	parent_store_class->free_folder_info (self, fi);
}

static CamelFolder*
imapx_extd_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 = extd_store_get_folder_offline (self,
	                                        foldername,
	                                        flags,
	                                        err);
	if (folder != NULL)
		g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (folder));

	return folder;
}

static CamelFolderInfo*
imapx_extd_store_get_folder_info_sync (CamelStore *self,
                                       const gchar *top,
                                       CamelStoreGetFolderInfoFlags flags,
                                       GCancellable *cancellable,
                                       GError **err)
{
	CamelFolderInfo *fi = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (self));
	/* top may be NULL */ /* FIXME correct? */
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	fi = parent_store_class->get_folder_info_sync (self,
	                                               top,
	                                               flags,
	                                               cancellable,
	                                               err);
	return fi;
}

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

	g_assert (CAMEL_IS_IMAPX_EXTD_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 CamelIMAPXExtdFolder
	 * here, we need to clone the CamelFolder into
	 * a locally created instance
	 */
#if 0
	if (folder != NULL)
		g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (folder));
#endif
	return folder;
}

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

	g_assert (CAMEL_IS_IMAPX_EXTD_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 imapx_extd_store_get_junk_folder_sync()
	 * why we do not have a CamelIMAPXExtdFolder
	 * here
	 */
#if 0
	if (folder != NULL)
		g_assert (CAMEL_IS_IMAPX_EXTD_FOLDER (folder));
#endif
	return folder;
}

static CamelFolderInfo*
imapx_extd_store_create_folder_sync (CamelStore *self,
                                     const gchar *parentname,
                                     const gchar *foldername,
                                     GCancellable *cancellable,
                                     GError **err)
{
	CamelFolderInfo *fi = NULL;

	g_assert (CAMEL_IS_IMAPX_EXTD_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);

	fi = parent_store_class->create_folder_sync (self,
	                                             parentname,
	                                             foldername,
	                                             cancellable,
	                                             err);
	return fi;
}

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

	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (self));
	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,
	                                             err);

	/* FIXME delete metadata from CamelIMAPXExtdServer */

	return ok;
}

static gboolean
imapx_extd_store_rename_folder_sync (CamelStore *self,
                                     const gchar *oldname,
                                     const gchar *newname,
                                     GCancellable *cancellable,
                                     GError **err)
{
	gboolean ok = FALSE;

	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (self));
	g_assert (oldname != NULL);
	g_assert (newname != NULL);
	/* cancellable may be NULL */
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	ok = parent_store_class->rename_folder_sync (self,
	                                             oldname,
	                                             newname,
	                                             cancellable,
	                                             err);

	/* FIXME update metadata in CamelIMAPXExtdServer */

	return ok;
}

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

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

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

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

	g_assert (CAMEL_IS_IMAPX_EXTD_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 CamelIMAPXServer*
imapx_extd_store_get_server (CamelIMAPXStore *self,
                             const gchar *foldername,
                             GCancellable *cancellable,
                             GError **err)
{
	CamelIMAPXServer *server = NULL;
	GError *tmp_err = NULL;

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

	camel_service_lock (CAMEL_SERVICE (self), CAMEL_SERVICE_REC_CONNECT_LOCK);

	server = camel_imapx_conn_manager_get_connection (self->con_man,
	                                                  foldername,
	                                                  cancellable,
	                                                  &tmp_err);

	camel_service_unlock (CAMEL_SERVICE (self), CAMEL_SERVICE_REC_CONNECT_LOCK);

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

	g_assert (CAMEL_IS_IMAPX_EXTD_SERVER (server));

	return server;
}

static void
imapx_extd_store_op_done (CamelIMAPXStore *self,
                          CamelIMAPXServer *server,
                          const gchar *foldername)
{
	g_assert (CAMEL_IS_IMAPX_EXTD_STORE (self));
	g_assert (CAMEL_IS_IMAPX_EXTD_SERVER (server));
	g_assert (foldername != NULL);

	camel_imapx_store_op_done (self,
	                           server,
	                           foldername);
}

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

static gboolean
imapx_extd_store_initable_initialize (GInitable *initable,
                                      GCancellable *cancellable,
                                      GError **err)
{
	gboolean ok = FALSE;

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

	/* chain up to parent interface's init() method. */
	ok = parent_initable_iface->init (initable,
	                                  cancellable,
	                                  err);
	return ok;
}

static const gchar*
imapx_extd_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
imapx_extd_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
imapx_extd_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
imapx_extd_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
imapx_extd_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
imapx_extd_store_initable_init (GInitableIface *interface)
{
	parent_initable_iface = g_type_interface_peek_parent (interface);
	interface->init = imapx_extd_store_initable_initialize;
}

static void
imapx_extd_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 = imapx_extd_store_get_service_name;
	interface->get_default_port = imapx_extd_store_get_default_port;
}

static void
imapx_extd_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 = imapx_extd_store_folder_is_subscribed;
	interface->subscribe_folder_sync = imapx_extd_store_subscribe_folder_sync;
	interface->unsubscribe_folder_sync = imapx_extd_store_unsubscribe_folder_sync;
}

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

static void
camel_imapx_extd_store_class_init (CamelIMAPXExtdStoreClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	CamelServiceClass *service_class = CAMEL_SERVICE_CLASS (klass);
	CamelStoreClass *store_class = CAMEL_STORE_CLASS (klass);

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

	/* g_type_class_add_private (klass, sizeof (CamelIMAPXExtdStorePrivate)); */

	object_class->dispose = camel_imapx_extd_store_dispose;
	object_class->finalize = camel_imapx_extd_store_finalize;

	service_class->settings_type = CAMEL_TYPE_IMAPX_SETTINGS; /* maybe need to get our own here */
	service_class->get_name = parent_service_class->get_name;
	service_class->connect_sync = imapx_extd_store_connect_sync;
	service_class->disconnect_sync = parent_service_class->disconnect_sync;
	service_class->authenticate_sync = imapx_extd_store_authenticate_sync;
	service_class->query_auth_types_sync = imapx_extd_store_query_auth_types_sync;

	store_class->hash_folder_name = parent_store_class->hash_folder_name;
	store_class->compare_folder_name = parent_store_class->compare_folder_name;
	store_class->can_refresh_folder = imapx_extd_store_can_refresh_folder;
	store_class->free_folder_info = imapx_extd_store_free_folder_info;
	store_class->get_folder_sync = imapx_extd_store_get_folder_sync;
	store_class->get_folder_info_sync = imapx_extd_store_get_folder_info_sync;
	store_class->get_junk_folder_sync = imapx_extd_store_get_junk_folder_sync;
	store_class->get_trash_folder_sync = imapx_extd_store_get_trash_folder_sync;
	store_class->create_folder_sync = imapx_extd_store_create_folder_sync;
	store_class->delete_folder_sync = imapx_extd_store_delete_folder_sync;
	store_class->rename_folder_sync = imapx_extd_store_rename_folder_sync;
	store_class->synchronize_sync = imapx_extd_store_synchronize_sync;
	store_class->noop_sync = imapx_extd_store_noop_sync;

	klass->get_server = imapx_extd_store_get_server;
	klass->op_done = imapx_extd_store_op_done;
}

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

CamelIMAPXServer*
camel_imapx_extd_store_get_server (CamelIMAPXStore *self,
                                   const gchar *foldername,
                                   GCancellable *cancellable,
                                   GError **err)
{
	CamelIMAPXServer *server = NULL;
	CamelIMAPXExtdStoreClass *klass = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_EXTD_STORE (self), NULL);

	klass = CAMEL_IMAPX_EXTD_STORE_GET_CLASS (self);
	server = klass->get_server (self,
	                            foldername,
	                            cancellable,
	                            err);
	return server;
}

void
camel_imapx_extd_store_op_done (CamelIMAPXStore *self,
                                CamelIMAPXServer *server,
                                const gchar *foldername)
{
	CamelIMAPXExtdStoreClass *klass = NULL;

	g_return_if_fail (CAMEL_IS_IMAPX_EXTD_STORE (self));

	klass = CAMEL_IMAPX_EXTD_STORE_GET_CLASS (self);
	klass->op_done (self, server, foldername);
}

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