/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/***************************************************************************
 *            kolab-mail-side-cache.c
 *
 *  Tue Dec 21 17:58:20 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
 */

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

/* TODO There is code duplication regarding SQLite in KolabImapxMetadataDb,
 *	KolabMailInfoDb and KolabMailSideCache.
 *
 *	A generalized approach could be to use some sort of persistent
 *	GHashTable, which would be mapped transparently onto a SQLite DB
 *	(or an object oriented DB right away), with some constraints
 *	applying to the hash table keys and values (as with marshalling
 *	of serializable objects). A GPersistentHashTable could be nested,
 *	binary data stored as GByteArray values and basic data types carry
 *	with them some type annotations (maybe through GType)
 */

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

#include <config.h>

#include <glib/gi18n-lib.h>

#include <libekolabconv/main/src/kolab-conv.h>

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

#include "kolab-util-backend.h"

#include "kolab-mail-side-cache.h"
#include "kolab-mail-handle-friend.h"

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

#define KOLAB_MAIL_SIDE_CACHE_SQLITE_DB_FILE "sidecache.db"

/*----------------------------------------------------------------------------*/
/* SQLite DB column names */

/* Kolab_conv_mail_part */
static const gchar *SCS[] = {
	"idx",			/* row index (autoincrement)      */
	"mp_name",		/* Kolab_conv_mail_part.name      */
	"mp_mime_type",		/* Kolab_conv_mail_part.mime_type */
	"mp_length",		/* Kolab_conv_mail_part.length    */
	"mp_data"		/* Kolab_conv_mail_part.data      */
};

enum {
	KOLAB_CONV_MAIL_SQLCOL_FIELD_INDEX = 0,
	KOLAB_CONV_MAIL_SQLCOL_FIELD_NAME,
	KOLAB_CONV_MAIL_SQLCOL_FIELD_MIME_TYPE,
	KOLAB_CONV_MAIL_SQLCOL_FIELD_LENGTH,
	KOLAB_CONV_MAIL_SQLCOL_FIELD_DATA,
	/* LAST */
	KOLAB_CONV_MAIL_SQLCOL_LAST_FIELD
};

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

typedef struct _KolabMailSideCachePrivate KolabMailSideCachePrivate;
struct _KolabMailSideCachePrivate
{
	KolabSettingsHandler *ksettings;
	KolabMailMimeBuilder *mimebuilder;
	gboolean is_up;
	KolabUtilSqliteDb *kdb; /* SideCache SQLite DB descriptor */
};

#define KOLAB_MAIL_SIDE_CACHE_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KOLAB_TYPE_MAIL_SIDE_CACHE, KolabMailSideCachePrivate))

G_DEFINE_TYPE (KolabMailSideCache, kolab_mail_side_cache, G_TYPE_OBJECT)

/*----------------------------------------------------------------------------*/
/* SQLite helpers */

static gboolean
kolab_mail_side_cache_sql_table_create (KolabUtilSqliteDb *kdb,
                                        const gchar *tblname,
                                        GError **err)
{
	gchar* sql_str = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (kdb != NULL);
	g_assert (kdb->db != NULL);
	g_assert (tblname != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	/* row is Kolab UID as primary key plus Kolab_conv_mail fields */
	sql_str = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS %Q ( %Q INTEGER PRIMARY KEY, \
								    %Q TEXT, %Q TEXT, %Q INTEGER, \
								    %Q BLOB );",
	                           tblname,
	                           SCS[KOLAB_CONV_MAIL_SQLCOL_FIELD_INDEX],
	                           SCS[KOLAB_CONV_MAIL_SQLCOL_FIELD_NAME],
	                           SCS[KOLAB_CONV_MAIL_SQLCOL_FIELD_MIME_TYPE],
	                           SCS[KOLAB_CONV_MAIL_SQLCOL_FIELD_LENGTH],
	                           SCS[KOLAB_CONV_MAIL_SQLCOL_FIELD_DATA]);

	ok = kolab_util_sqlite_exec_str (kdb, sql_str, &tmp_err);
	sqlite3_free (sql_str);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	return TRUE;
}

static gchar*
kolab_mail_side_cache_sql_new_tblname (const gchar *uid,
                                       const gchar *foldername)
{
	gchar *tblname = NULL;

	g_assert (uid != NULL);
	g_assert (foldername != NULL);

	tblname = g_strconcat (foldername, uid, NULL);
	return tblname;
}

static gboolean
kolab_mail_side_cache_sql_delete_by_uid (KolabUtilSqliteDb *kdb,
                                         const gchar *uid,
                                         const gchar *foldername,
                                         GError **err)
{
	gchar *tblname = NULL;
	gboolean exists = FALSE;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (kdb != NULL);
	g_assert (kdb->db != NULL);
	g_assert (uid != NULL);
	g_assert (foldername != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	tblname = kolab_mail_side_cache_sql_new_tblname (uid, foldername);

	/* check if object exists */
	exists = kolab_util_sqlite_table_exists (kdb, tblname, &tmp_err);
	if (tmp_err != NULL) {
		g_free (tblname);
		g_propagate_error (err, tmp_err);
		return FALSE;
	}
	if (! exists) {
		g_free (tblname);
		g_debug ("%s: UID (%s) Folder (%s) does not exist in SideCache",
		         __func__, uid, foldername);
		return TRUE;
	}

	ok = kolab_util_sqlite_table_drop (kdb, tblname, &tmp_err);
	g_free (tblname);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	return TRUE;
}

static gboolean
kolab_mail_side_cache_sql_delete_folder (KolabUtilSqliteDb *kdb,
                                         const gchar *foldername,
                                         GError **err)
{
	(void)kdb;
	(void)foldername;
	(void)err;

	/* TODO implement me
	 *      - find all tables the names of which start
	 *        with 'foldername'
	 *      - drop these tables
	 */
	g_warning ("%s: not yet implemented", __func__);

	return TRUE;
}

static Kolab_conv_mail*
kolab_mail_side_cache_sql_query_object (KolabUtilSqliteDb *kdb,
                                        const gchar *uid,
                                        const gchar *foldername,
                                        GError **err)
{
	Kolab_conv_mail *kconvmail = NULL;
	gchar *tblname = NULL;
	gint nparts = 0;
	gchar *sql_str = NULL;
	gint sql_errno = SQLITE_OK;
	sqlite3_stmt *sql_stmt = NULL;
	gboolean exists = FALSE;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (kdb != NULL);
	g_assert (kdb->db != NULL);
	g_assert (uid != NULL);
	g_assert (foldername != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, NULL);

	tblname = kolab_mail_side_cache_sql_new_tblname (uid, foldername);

	/* check if object exists */
	exists = kolab_util_sqlite_table_exists (kdb, tblname, &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		g_free (tblname);
		return NULL;
	}
	if (! exists) {
		g_warning ("%s: UID (%s) Folder (%s) does not exist in SideCache",
		           __func__, uid, foldername);
		g_free (tblname);
		return NULL;
	}

	/* get number of mail parts */
	nparts = kolab_util_sqlite_table_get_rowcount (kdb, tblname, &tmp_err);
	if (nparts < 0) {
		g_propagate_error (err, tmp_err);
		g_free (tblname);
		return NULL;
	}
	if (nparts == 0) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_DATATYPE_KOLAB,
		             _("Internal inconsistency detected: Invalid cache object with zero mail message parts, UID '%s', Folder '%s'"),
		             uid, foldername);
		g_free (tblname);
		return NULL;
	}

	/* prep statement for reading table */
	sql_str = sqlite3_mprintf ("SELECT * FROM %Q;", tblname);
	g_free (tblname);
	ok = kolab_util_sqlite_prep_stmt (kdb, &sql_stmt, sql_str, &tmp_err);
	sqlite3_free (sql_str);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	/* get data */
	kconvmail = g_new0 (Kolab_conv_mail, 1);
	kconvmail->length = (guint)nparts;
	kconvmail->mail_parts = g_new0 (Kolab_conv_mail_part, (guint) nparts);

	while (TRUE) {
		gint  rowidx = 0;
		guint nbytes = 0;
		Kolab_conv_mail_part *mpart = NULL;

		/* get record row from db */
		sql_errno = sqlite3_step (sql_stmt);
		if (sql_errno != SQLITE_ROW) {
			if (sql_errno != SQLITE_DONE) {
				g_set_error (&tmp_err,
				             KOLAB_UTIL_ERROR,
				             KOLAB_UTIL_ERROR_SQLITE_DB,
				             _("SQLite Error: %s"),
				             sqlite3_errmsg (kdb->db));
			}
			break;
		}

		/* read mail part data into Kolab_conv_mail */
		rowidx = sqlite3_column_int (sql_stmt,
		                             KOLAB_CONV_MAIL_SQLCOL_FIELD_INDEX);
		rowidx -= 1; /* SQLite starts with row 1 (instead of row 0) */
		mpart = &((kconvmail->mail_parts)[rowidx]);
		/* check number of expected payload bytes */
		mpart->length = (guint) sqlite3_column_int (sql_stmt,
		                                            KOLAB_CONV_MAIL_SQLCOL_FIELD_LENGTH);
		nbytes = (guint) sqlite3_column_bytes(sql_stmt,
		                                      KOLAB_CONV_MAIL_SQLCOL_FIELD_DATA);
		if (mpart->length != nbytes) {
			g_set_error (&tmp_err,
			             KOLAB_UTIL_ERROR,
			             KOLAB_UTIL_ERROR_SQLITE_DB,
			             _("Internal inconsistency detected: Invalid cache object has %i payload bytes, expected %i bytes, UID '%s', Folder '%s'"),
			             mpart->length, nbytes, uid, foldername);
			break;
		}
		mpart->name = g_strdup ((gchar *) sqlite3_column_text (sql_stmt,
		                                                       KOLAB_CONV_MAIL_SQLCOL_FIELD_NAME));
		mpart->mime_type = g_strdup ((gchar *) sqlite3_column_text (sql_stmt,
		                                                            KOLAB_CONV_MAIL_SQLCOL_FIELD_MIME_TYPE));
		if (nbytes > 0) {
			const void* blobdata = sqlite3_column_blob (sql_stmt,
			                                            KOLAB_CONV_MAIL_SQLCOL_FIELD_DATA);
			mpart->data = g_memdup (blobdata, nbytes);
		} else {
			mpart->data = NULL;
		}
	}

	if (tmp_err == NULL) {
		ok = kolab_util_sqlite_fnlz_stmt (kdb, sql_stmt, &tmp_err);
	} else {
		(void)kolab_util_sqlite_fnlz_stmt (kdb, sql_stmt, NULL);
		ok = FALSE;
	}
	if (! ok) {
		kolabconv_free_kmail (kconvmail);
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	return kconvmail;
}

static gboolean
kolab_mail_side_cache_sql_store_object (KolabUtilSqliteDb *kdb,
                                        const Kolab_conv_mail *kconvmail,
                                        const gchar *uid,
                                        const gchar *foldername,
                                        GError **err)
{
	guint ii = 0;
	gchar *tblname = NULL;
	gboolean exists = FALSE;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (kdb != NULL);
	g_assert (kdb->db != NULL);
	g_assert (kconvmail != NULL);
	g_assert (uid != NULL);
	g_assert (foldername != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	tblname = kolab_mail_side_cache_sql_new_tblname (uid, foldername);

	/* check if object exists */
	exists = kolab_util_sqlite_table_exists (kdb, tblname, &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		g_free (tblname);
		return FALSE;
	}

	/* TODO change this to transaction based - insert into interim
	 *      table first, drop original, rename interim
	 */
	if (exists) {
		ok = kolab_util_sqlite_table_drop (kdb, tblname, &tmp_err);
		if (! ok) {
			g_propagate_error (err, tmp_err);
			g_free (tblname);
			return FALSE;
		}
	}

	ok = kolab_mail_side_cache_sql_table_create (kdb, tblname, &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		g_free (tblname);
		return FALSE;
	}

	for (ii = 0; ii < kconvmail->length; ii++) {
		Kolab_conv_mail_part *mpart = &((kconvmail->mail_parts)[ii]);
		gchar *sql_str = NULL;
		sqlite3_stmt *sql_stmt = NULL;
		gint sql_errno = SQLITE_OK;
		sql_str = sqlite3_mprintf ("INSERT INTO %Q VALUES ( NULL, %Q, %Q, '%u', ? );",
		                           tblname,
		                           mpart->name,
		                           mpart->mime_type,
		                           mpart->length);
		ok = kolab_util_sqlite_prep_stmt (kdb, &sql_stmt, sql_str, &tmp_err);
		sqlite3_free (sql_str);
		if (! ok)
			break;
		sql_errno = sqlite3_bind_blob (sql_stmt, 1, mpart->data, (int)(mpart->length), NULL);
		if (sql_errno != SQLITE_OK)
			goto sql_err;
		sql_errno = sqlite3_step (sql_stmt);
		if (sql_errno != SQLITE_DONE)
			goto sql_err;
		ok = kolab_util_sqlite_fnlz_stmt (kdb, sql_stmt, &tmp_err);
		if (!ok) {
			(void)kolab_util_sqlite_fnlz_stmt (kdb, sql_stmt, NULL);
			break;
		}
		continue;
	sql_err:
		g_set_error (&tmp_err,
		             KOLAB_UTIL_ERROR,
		             KOLAB_UTIL_ERROR_SQLITE_DB,
		             _("SQLite Error: %s"),
		             sqlite3_errmsg (kdb->db));
		(void)kolab_util_sqlite_fnlz_stmt (kdb, sql_stmt, NULL);
		break;
	}

	if (tmp_err != NULL) {
		GError *tmp_err_2 = NULL;
		g_propagate_error (err, tmp_err);
		ok = kolab_util_sqlite_table_drop (kdb, tblname, &tmp_err_2);
		if (! ok) {
			g_warning ("%s: %s", __func__, tmp_err_2->message);
			g_error_free (tmp_err_2);
		}
		g_free (tblname);
		return FALSE;
	}

	g_free (tblname);

	return TRUE;
}

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

static void
kolab_mail_side_cache_init (KolabMailSideCache *object)
{
	KolabMailSideCache *self = NULL;
	KolabMailSideCachePrivate *priv = NULL;

	g_assert (KOLAB_IS_MAIL_SIDE_CACHE (object));

	self = KOLAB_MAIL_SIDE_CACHE (object);
	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	priv->ksettings = NULL;
	priv->mimebuilder = NULL;
	priv->is_up = FALSE;
	priv->kdb = NULL;
}

static void
kolab_mail_side_cache_dispose (GObject *object)
{
	KolabMailSideCache *self = NULL;
	KolabMailSideCachePrivate *priv = NULL;

	self = KOLAB_MAIL_SIDE_CACHE (object);
	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	if (priv->ksettings != NULL) { /* ref'd in configure() */
		g_object_unref (priv->ksettings);
		priv->ksettings = NULL;
	}
	if (priv->mimebuilder != NULL) {
		g_object_unref (priv->mimebuilder);
		priv->mimebuilder = NULL;
	}

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

static void
kolab_mail_side_cache_finalize (GObject *object)
{
	/* KolabMailSideCache *self = NULL; */
	/* KolabMailSideCachePrivate *priv = NULL; */

	/* self = KOLAB_MAIL_SIDE_CACHE (object); */
	/* priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self); */

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

static void
kolab_mail_side_cache_class_init (KolabMailSideCacheClass *klass)
{
	GObjectClass* object_class = G_OBJECT_CLASS (klass);
	/* GObjectClass* parent_class = G_OBJECT_CLASS (klass); */

	g_type_class_add_private (klass, sizeof (KolabMailSideCachePrivate));

	object_class->dispose = kolab_mail_side_cache_dispose;
	object_class->finalize = kolab_mail_side_cache_finalize;
}

/*----------------------------------------------------------------------------*/
/* object config/status */

gboolean
kolab_mail_side_cache_configure  (KolabMailSideCache *self,
                                  KolabSettingsHandler *ksettings,
                                  KolabMailMimeBuilder *mimebuilder,
                                  GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;

	g_assert (KOLAB_IS_MAIL_SIDE_CACHE (self));
	g_assert (KOLAB_IS_SETTINGS_HANDLER (ksettings));
	g_assert (KOLAB_IS_MAIL_MIME_BUILDER (mimebuilder));
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	if (priv->ksettings != NULL)
		return TRUE;

	g_object_ref (ksettings); /* unref'd in dispose() */
	g_object_ref (mimebuilder);
	priv->ksettings = ksettings;
	priv->mimebuilder = mimebuilder;

	return TRUE;
}

gboolean
kolab_mail_side_cache_bringup (KolabMailSideCache *self,
                               GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	if (priv->is_up == TRUE)
		return TRUE;

	/* init sql db */
	ok = kolab_util_backend_sqlite_db_new_open (&(priv->kdb),
	                                            priv->ksettings,
	                                            KOLAB_MAIL_SIDE_CACHE_SQLITE_DB_FILE,
	                                            &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	priv->is_up = TRUE;
	return TRUE;
}

gboolean
kolab_mail_side_cache_shutdown (KolabMailSideCache *self,
                                GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	if (priv->is_up == FALSE)
		return TRUE;

	ok = kolab_util_sqlite_db_free (priv->kdb, &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}
	priv->kdb = NULL;

	priv->is_up = FALSE;
	return TRUE;
}

/*----------------------------------------------------------------------------*/
/* folders */

GList*
kolab_mail_side_cache_query_foldernames (KolabMailSideCache *self,
                                         GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;

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

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	/* TODO implement me by SQLiteDB
	 *
	 * - need to list all folders known to SideCache
	 * - this might be needed if we store new folders
	 *   in the SideCache
	 * - as long as we create new folders only in
	 *   online mode, this should never be needed
	 */

	return NULL;
}

gboolean
kolab_mail_side_cache_delete_folder (KolabMailSideCache *self,
                                     const gchar *foldername,
                                     GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (KOLAB_IS_MAIL_SIDE_CACHE (self));
	g_assert (foldername != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	ok = kolab_mail_side_cache_sql_delete_folder (priv->kdb,
	                                              foldername,
	                                              &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	return TRUE;
}

/*----------------------------------------------------------------------------*/
/* mailobject retrieve/store/delete */

gboolean
kolab_mail_side_cache_store (KolabMailSideCache *self,
                             KolabMailHandle *kmailhandle,
                             const gchar *foldername,
                             GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;
	const Kolab_conv_mail *kconvmail = NULL;
	const gchar *kolab_uid = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (KOLAB_IS_MAIL_SIDE_CACHE (self));
	if (kmailhandle != NULL)
		g_assert (KOLAB_IS_MAIL_HANDLE (kmailhandle));
	g_assert (foldername != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	kolab_uid = kolab_mail_handle_get_uid (kmailhandle);
	if (kolab_uid == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_INTERNAL,
		             _("Internal inconsistency detected: PIM Object handle has no Kolab UID set"));
		return FALSE;
	}

	if (kmailhandle == NULL)
		return TRUE;

	kconvmail = kolab_mail_handle_get_kconvmail (kmailhandle);

	ok = kolab_mail_side_cache_sql_store_object (priv->kdb,
	                                             kconvmail,
	                                             kolab_uid,
	                                             foldername,
	                                             &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	return TRUE;
}

gboolean
kolab_mail_side_cache_retrieve (KolabMailSideCache *self,
                                KolabMailHandle *kmailhandle,
                                GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;
	Kolab_conv_mail *kconvmail = NULL;
	const gchar *foldername = NULL;
	const gchar *kolab_uid = NULL;
	GError *tmp_err = NULL;

	g_assert (KOLAB_IS_MAIL_SIDE_CACHE (self));
	g_assert (KOLAB_IS_MAIL_HANDLE (kmailhandle));
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	kolab_uid = kolab_mail_handle_get_uid (kmailhandle);
	if (kolab_uid == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_INTERNAL,
		             _("Internal inconsistency detected: PIM Object handle has no Kolab UID set"));
		return FALSE;
	}

	foldername = kolab_mail_handle_get_foldername (kmailhandle);
	if (foldername == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_INTERNAL,
		             _("Internal inconsistency detected: Folder name not set on PIM Object handle, UID '%s'"),
		             kolab_uid);
		return FALSE;
	}

	kconvmail = kolab_mail_side_cache_sql_query_object (priv->kdb,
	                                                    kolab_uid,
	                                                    foldername,
	                                                    &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}
	if (kconvmail == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_NOTFOUND,
		             _("Could not find cache object, UID '%s', Folder '%s'"),
		             kolab_uid, foldername);
		return FALSE;
	}

	kolab_mail_handle_set_kconvmail (kmailhandle, kconvmail);

	/* folder_type / folder_context must already be set on handle */

	return TRUE;
}

gboolean
kolab_mail_side_cache_delete_by_uid (KolabMailSideCache *self,
                                     const gchar *uid,
                                     const gchar *foldername,
                                     GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (KOLAB_IS_MAIL_SIDE_CACHE (self));
	g_assert (uid != NULL);
	g_assert (foldername != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	ok = kolab_mail_side_cache_sql_delete_by_uid (priv->kdb,
	                                              uid,
	                                              foldername,
	                                              &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	return TRUE;
}

gboolean
kolab_mail_side_cache_delete (KolabMailSideCache *self,
                              KolabMailHandle *kmailhandle,
                              GError **err)
{
	KolabMailSideCachePrivate *priv = NULL;
	const gchar *kolab_uid = NULL;
	const gchar *foldername = NULL;
	gboolean ok = TRUE;
	GError *tmp_err = NULL;

	g_assert (KOLAB_IS_MAIL_SIDE_CACHE (self));
	g_assert (KOLAB_IS_MAIL_HANDLE (kmailhandle));
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = KOLAB_MAIL_SIDE_CACHE_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	kolab_uid = kolab_mail_handle_get_uid (kmailhandle);
	if (kolab_uid == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_INTERNAL,
		             _("Internal inconsistency detected: PIM Object handle has no Kolab UID set"));
		return FALSE;
	}

	foldername = kolab_mail_handle_get_foldername (kmailhandle);
	if (foldername == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_INTERNAL,
		             _("Internal inconsistency detected: Folder name not set on PIM Object handle, UID '%s'"),
		             kolab_uid);
		return FALSE;
	}

	ok = kolab_mail_side_cache_delete_by_uid (self,
	                                          kolab_uid,
	                                          foldername,
	                                          &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	return TRUE;
}

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