/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/***************************************************************************
 *            kolab-mail-info-db.c
 *
 *  Wed Jan 26 11:42:23 2011
 *  Copyright  2011  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 <string.h>
#include <sqlite3.h>

#include <glib/gi18n-lib.h>

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

#include "kolab-util-backend.h"
#include "kolab-mail-info-db.h"

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

#define KOLAB_MAIL_INFO_DB_SQLITE_DB_FILE "info.db"
#define KOLAB_MAIL_INFO_DB_SQLITE_DB_TBL_FOLDERS "folders"
#define KOLAB_MAIL_INFO_DB_SQLITE_DB_TBL_RECORDS "records_"

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

/* Folders table */
static const gchar *SCF[] = {
	"s_foldername",		/* KOLAB_FOLDER_SUMMARY_SQLCOL_CHAR_FIELD_FOLDERNAME		*/
	"s_cache_location",     /* KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_CACHE_LOCATION	*/
	"s_cache_status",	/* KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_CACHE_STATUS		*/
	"s_folder_type",	/* KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_TYPE		*/
	"s_folder_context",     /* KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_CONTEXT	*/
	"s_uidvalidity",	/* KOLAB_FOLDER_SUMMARY_SQLCOL_UINT64_FIELD_UIDVALIDITY		*/
	"s_uidvalidity_sync"    /* KOLAB_FOLDER_SUMMARY_SQLCOL_UINT64_FIELD_UIDVALIDITY_SYNC    */
};

/* Records table */
static const gchar *SCR[] = {
	"s_kolab_uid",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_KOLAB_UID			*/
	"s_imap_uid",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_IMAP_UID			*/
	"s_e_summary",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_SUMMARY			*/
	"s_e_organizer",	/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_ORGANIZER			*/
	"s_e_location",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_LOCATION			*/
	"s_e_category",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_CATEGORY			*/
	"s_e_dtstart",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_DTSTART			*/
	"s_e_dtstart_tzid",     /* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_DTSTART_TZID			*/
	"s_e_dtend",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_DTEND				*/
	"s_e_dtend_tzid",       /* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_DTEND_TZID			*/
	"s_e_fullname",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_FULLNAME			*/
	"s_e_email_1",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_1			*/
	"s_e_email_2",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_2			*/
	"s_e_email_3",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_3			*/
	"s_e_email_4",		/* KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_4			*/
	"s_folder_type",	/* KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_TYPE			*/
	"s_folder_context",     /* KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_CONTEXT			*/
	"s_cache_location",     /* KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_CACHE_LOCATION			*/
	"s_cache_status",	/* KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_CACHE_STATUS			*/
	"s_e_classification",   /* KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_E_CLASSIFICATION		*/
	"s_e_status",		/* KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_E_STATUS			*/
	"s_e_priority",		/* KOLAB_MAIL_SUMMARY_SQLCOL_INT_FIELD_E_PRIORITY			*/
	"s_e_percent",		/* KOLAB_MAIL_SUMMARY_SQLCOL_INT_FIELD_E_PERCENT			*/
	"s_complete",		/* KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_COMPLETE			*/
	"s_e_has_attendees",    /* KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_ATTENDEES			*/
	"s_e_has_attachments",  /* KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_ATTACHMENTS		*/
	"s_e_has_recurrence",   /* KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_RECURRENCE		*/
	"s_e_has_alarms",       /* KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_ALARMS			*/
	"r_imap_uid_sync",      /* KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_UID_SYNC		*/
	"r_folder_name",	/* KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_FOLDER_NAME		*/
	"r_imap_checksum",      /* KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_CHECKSUM		*/
	"r_imap_checksum_sync", /* KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_CHECKSUM_SYNC       */
	"r_side_checksum",      /* KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_SIDE_CHECKSUM		*/
	"r_imap_folder_type",   /* KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_UINT_FIELD_IMAP_FOLDER_TYPE		*/
	"r_imap_folder_context" /* KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_UINT_FIELD_IMAP_FOLDER_CONTEXT      */
};

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

typedef enum {
	KOLAB_MAIL_INFO_DB_TABLE_TYPE_FOLDER = 0,
	KOLAB_MAIL_INFO_DB_TABLE_TYPE_RECORD,
	/* LAST */
	KOLAB_MAIL_INFO_DB_TABLE_LAST_TYPE
} KolabMailInfoDbTableType;

typedef struct _KolabMailInfoDbPrivate KolabMailInfoDbPrivate;
struct _KolabMailInfoDbPrivate
{
	KolabSettingsHandler *ksettings;
	gboolean is_up;

	KolabUtilSqliteDb *kdb;		/* InfoDb SQLite DB descriptor */
	KolabFolderSummary *cache_fs;   /* cached folder summary */
	GHashTable *cache_r; /* cached InfoDb record */
};

#define KOLAB_MAIL_INFO_DB_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KOLAB_TYPE_MAIL_INFO_DB, KolabMailInfoDbPrivate))

G_DEFINE_TYPE (KolabMailInfoDb, kolab_mail_info_db, G_TYPE_OBJECT)

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

static gboolean
kolab_mail_info_db_sql_table_create (KolabUtilSqliteDb *kdb,
                                     const gchar *tblname,
                                     KolabMailInfoDbTableType tbltype,
                                     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_assert (tbltype < KOLAB_MAIL_INFO_DB_TABLE_LAST_TYPE);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	switch (tbltype) {
	case KOLAB_MAIL_INFO_DB_TABLE_TYPE_FOLDER:
		/* row is KolabFolderSummary with folder name as primary key */
		sql_str = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS %Q ( %Q TEXT PRIMARY KEY, \
										    %Q INTEGER, %Q INTEGER, %Q INTEGER, %Q INTEGER, %Q INTEGER, \
										    %Q INTEGER );",
		                           tblname,
		                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_CHAR_FIELD_FOLDERNAME],
		                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_CACHE_LOCATION],
		                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_CACHE_STATUS],
		                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_TYPE],
		                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_CONTEXT],
		                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_UINT64_FIELD_UIDVALIDITY],
		                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_UINT64_FIELD_UIDVALIDITY_SYNC]);
		break;
	case KOLAB_MAIL_INFO_DB_TABLE_TYPE_RECORD:
		/* row is KolabMailInfoDbRecord with Kolab UID as primary key */
		sql_str = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS %Q ( %Q TEXT PRIMARY KEY, \
										    %Q TEXT, %Q TEXT, %Q TEXT, %Q TEXT, %Q TEXT, \
										    %Q TEXT, %Q TEXT, %Q TEXT, %Q TEXT, %Q TEXT, \
										    %Q TEXT, %Q TEXT, %Q TEXT, %Q TEXT, \
										    %Q INTEGER, %Q INTEGER, %Q INTEGER, %Q INTEGER, %Q INTEGER, \
										    %Q INTEGER, %Q INTEGER, %Q INTEGER, %Q INTEGER, %Q INTEGER, \
										    %Q INTEGER, %Q INTEGER, %Q INTEGER, \
										    %Q TEXT, %Q TEXT, %Q TEXT, %Q TEXT, %Q TEXT, \
										    %Q INTEGER, %Q INTEGER );",
		                           tblname,
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_KOLAB_UID],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_IMAP_UID],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_SUMMARY],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_ORGANIZER],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_LOCATION],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_CATEGORY],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_DTSTART],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_DTSTART_TZID],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_DTEND],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_DTEND_TZID],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_FULLNAME],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_1],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_2],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_3],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_4],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_TYPE],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_CONTEXT],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_CACHE_LOCATION],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_CACHE_STATUS],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_E_CLASSIFICATION],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_E_STATUS],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_INT_FIELD_E_PRIORITY],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_INT_FIELD_E_PERCENT],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_COMPLETE],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_ATTENDEES],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_ATTACHMENTS],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_RECURRENCE],
		                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_ALARMS],
		                           /* extra data */
		                           SCR[KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_UID_SYNC],
		                           SCR[KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_FOLDER_NAME],
		                           SCR[KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_CHECKSUM],
		                           SCR[KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_CHECKSUM_SYNC],
		                           SCR[KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_SIDE_CHECKSUM],
		                           SCR[KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_UINT_FIELD_IMAP_FOLDER_TYPE],
		                           SCR[KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_UINT_FIELD_IMAP_FOLDER_CONTEXT]);
		break;
	default:
		g_assert_not_reached ();
	}

	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 gboolean
kolab_mail_info_db_sql_update_folder (KolabUtilSqliteDb *kdb,
                                      const KolabFolderSummary *summary,
                                      GError **err)
{
	gchar *sql_str = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	sql_str = sqlite3_mprintf ("INSERT OR REPLACE INTO %Q VALUES ( %Q, %u, %u, %u, %u, %llu, %llu );",
	                           KOLAB_MAIL_INFO_DB_SQLITE_DB_TBL_FOLDERS,
	                           kolab_folder_summary_get_char_field (summary,
	                                                                KOLAB_FOLDER_SUMMARY_CHAR_FIELD_FOLDERNAME),
	                           kolab_folder_summary_get_uint_field (summary,
	                                                                KOLAB_FOLDER_SUMMARY_UINT_FIELD_CACHE_LOCATION),
	                           kolab_folder_summary_get_uint_field (summary,
	                                                                KOLAB_FOLDER_SUMMARY_UINT_FIELD_CACHE_STATUS),
	                           kolab_folder_summary_get_uint_field (summary,
	                                                                KOLAB_FOLDER_SUMMARY_UINT_FIELD_FOLDER_TYPE),
	                           kolab_folder_summary_get_uint_field (summary,
	                                                                KOLAB_FOLDER_SUMMARY_UINT_FIELD_FOLDER_CONTEXT),
	                           kolab_folder_summary_get_uint64_field (summary,
	                                                                  KOLAB_FOLDER_SUMMARY_UINT64_FIELD_UIDVALIDITY),
	                           kolab_folder_summary_get_uint64_field (summary,
	                                                                  KOLAB_FOLDER_SUMMARY_UINT64_FIELD_UIDVALIDITY_SYNC)
	                           );

	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_info_db_sql_record_new_tblname (const gchar *foldername)
{
	gchar *tblname = NULL;

	g_assert (foldername != NULL);

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

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

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

	tblname = kolab_mail_info_db_sql_record_new_tblname (foldername);
	ok = kolab_mail_info_db_sql_table_create (kdb,
	                                          tblname,
	                                          KOLAB_MAIL_INFO_DB_TABLE_TYPE_RECORD,
	                                          &tmp_err);
	if (! ok) {
		g_free (tblname);
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	sql_str = sqlite3_mprintf ("INSERT OR REPLACE INTO %Q VALUES ( %Q, %Q, %Q, %Q, %Q, \
								       %Q, %Q, %Q, %Q, %Q, \
								       %Q, %Q, %Q, %Q, %Q, \
								       %u, %u, %u, %u, %u, %u, \
								       %i, %i, \
								       %i, %i, %i, %i, %i, \
								       %Q, %Q, %Q, %Q, %Q, \
								       %u, %u );",
	                           tblname,
	                           /* KolabMailSummary data */
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_KOLAB_UID),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_IMAP_UID),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_SUMMARY),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_ORGANIZER),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_LOCATION),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_CATEGORY),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_DTSTART),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_DTSTART_TZID),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_DTEND),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_DTEND_TZID),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_FULLNAME),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_EMAIL_1),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_EMAIL_2),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_EMAIL_3),
	                           kolab_mail_summary_get_char_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_CHAR_FIELD_E_EMAIL_4),
	                           kolab_mail_summary_get_uint_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_UINT_FIELD_FOLDER_TYPE),
	                           kolab_mail_summary_get_uint_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_UINT_FIELD_FOLDER_CONTEXT),
	                           kolab_mail_summary_get_uint_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_UINT_FIELD_CACHE_LOCATION),
	                           kolab_mail_summary_get_uint_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_UINT_FIELD_CACHE_STATUS),
	                           kolab_mail_summary_get_uint_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_UINT_FIELD_E_CLASSIFICATION),
	                           kolab_mail_summary_get_uint_field (record->summary,
	                                                              KOLAB_MAIL_SUMMARY_UINT_FIELD_E_STATUS),
	                           kolab_mail_summary_get_int_field (record->summary,
	                                                             KOLAB_MAIL_SUMMARY_INT_FIELD_E_PRIORITY),
	                           kolab_mail_summary_get_int_field (record->summary,
	                                                             KOLAB_MAIL_SUMMARY_INT_FIELD_E_PERCENT),
	                           (gint)kolab_mail_summary_get_bool_field (record->summary,
	                                                                    KOLAB_MAIL_SUMMARY_BOOL_FIELD_COMPLETE),
	                           (gint)kolab_mail_summary_get_bool_field (record->summary,
	                                                                    KOLAB_MAIL_SUMMARY_BOOL_FIELD_E_HAS_ATTENDEES),
	                           (gint)kolab_mail_summary_get_bool_field (record->summary,
	                                                                    KOLAB_MAIL_SUMMARY_BOOL_FIELD_E_HAS_ATTACHMENTS),
	                           (gint)kolab_mail_summary_get_bool_field (record->summary,
	                                                                    KOLAB_MAIL_SUMMARY_BOOL_FIELD_E_HAS_RECURRENCE),
	                           (gint)kolab_mail_summary_get_bool_field (record->summary,
	                                                                    KOLAB_MAIL_SUMMARY_BOOL_FIELD_E_HAS_ALARMS),
	                           /* KolabMailInfoDbRecord extra data */
	                           kolab_mail_info_db_record_get_char_field (record,
	                                                                     KOLAB_MAIL_INFO_DB_RECORD_CHAR_FIELD_IMAP_UID_SYNC),
	                           kolab_mail_info_db_record_get_char_field (record,
	                                                                     KOLAB_MAIL_INFO_DB_RECORD_CHAR_FIELD_IMAP_FOLDER_NAME),
	                           kolab_mail_info_db_record_get_char_field (record,
	                                                                     KOLAB_MAIL_INFO_DB_RECORD_CHAR_FIELD_IMAP_CHECKSUM),
	                           kolab_mail_info_db_record_get_char_field (record,
	                                                                     KOLAB_MAIL_INFO_DB_RECORD_CHAR_FIELD_IMAP_CHECKSUM_SYNC),
	                           kolab_mail_info_db_record_get_char_field (record,
	                                                                     KOLAB_MAIL_INFO_DB_RECORD_CHAR_FIELD_SIDE_CHECKSUM),
	                           kolab_mail_info_db_record_get_uint_field (record,
	                                                                     KOLAB_MAIL_INFO_DB_RECORD_UINT_FIELD_IMAP_FOLDER_TYPE),
	                           kolab_mail_info_db_record_get_uint_field (record,
	                                                                     KOLAB_MAIL_INFO_DB_RECORD_UINT_FIELD_IMAP_FOLDER_CONTEXT)
	                           );
	g_free (tblname);
	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 KolabFolderSummary*
kolab_mail_info_db_sql_folder_new_from_stmt (sqlite3_stmt *sql_stmt)
{
	KolabFolderSummary *summary = NULL;
	gchar *tmp_str = NULL;
	guint tmp_uint = 0;
	guint64 tmp_uint64 = 0;

	if (sql_stmt == NULL)
		return NULL;

	summary = kolab_folder_summary_new ();

	/* folder name */
	tmp_str = g_strdup ((gchar*) sqlite3_column_text (sql_stmt,
	                                                  KOLAB_FOLDER_SUMMARY_SQLCOL_CHAR_FIELD_FOLDERNAME));
	kolab_folder_summary_set_char_field (summary,
	                                     KOLAB_FOLDER_SUMMARY_CHAR_FIELD_FOLDERNAME,
	                                     tmp_str);
	/* cache location */
	tmp_uint = (guint) sqlite3_column_int (sql_stmt,
	                                       KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_CACHE_LOCATION);
	kolab_folder_summary_set_uint_field (summary,
	                                     KOLAB_FOLDER_SUMMARY_UINT_FIELD_CACHE_LOCATION,
	                                     tmp_uint);
	/* cache status */
	tmp_uint = (guint) sqlite3_column_int (sql_stmt,
	                                       KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_CACHE_STATUS);
	kolab_folder_summary_set_uint_field (summary,
	                                     KOLAB_FOLDER_SUMMARY_UINT_FIELD_CACHE_STATUS,
	                                     tmp_uint);
	/* folder type */
	tmp_uint = (guint) sqlite3_column_int (sql_stmt,
	                                       KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_TYPE);
	kolab_folder_summary_set_uint_field (summary,
	                                     KOLAB_FOLDER_SUMMARY_UINT_FIELD_FOLDER_TYPE,
	                                     tmp_uint);
	/* folder context */
	tmp_uint = (guint) sqlite3_column_int (sql_stmt,
	                                       KOLAB_FOLDER_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_CONTEXT);
	kolab_folder_summary_set_uint_field (summary,
	                                     KOLAB_FOLDER_SUMMARY_UINT_FIELD_FOLDER_CONTEXT,
	                                     tmp_uint);
	/* uidvalidity */
	tmp_uint64 = (guint64) sqlite3_column_int64 (sql_stmt,
	                                             KOLAB_FOLDER_SUMMARY_SQLCOL_UINT64_FIELD_UIDVALIDITY);
	kolab_folder_summary_set_uint64_field (summary,
	                                       KOLAB_FOLDER_SUMMARY_UINT64_FIELD_UIDVALIDITY,
	                                       tmp_uint64);
	/* uidvalidity (sync) */
	tmp_uint64 = (guint64) sqlite3_column_int64 (sql_stmt,
	                                             KOLAB_FOLDER_SUMMARY_SQLCOL_UINT64_FIELD_UIDVALIDITY_SYNC);
	kolab_folder_summary_set_uint64_field (summary,
	                                       KOLAB_FOLDER_SUMMARY_UINT64_FIELD_UIDVALIDITY_SYNC,
	                                       tmp_uint64);
	return summary;
}

static KolabFolderSummary*
kolab_mail_info_db_sql_query_folder (KolabUtilSqliteDb *kdb,
                                     const gchar *foldername,
                                     GError **err)
{
	KolabFolderSummary *summary = NULL;
	gchar *sql_str = NULL;
	gint sql_errno = SQLITE_OK;
	sqlite3_stmt *sql_stmt = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	/* prepare sql statement */
	sql_str = sqlite3_mprintf ("SELECT * FROM %Q WHERE %q=%Q;",
	                           KOLAB_MAIL_INFO_DB_SQLITE_DB_TBL_FOLDERS,
	                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_CHAR_FIELD_FOLDERNAME],
	                           foldername);
	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 folder summary row from db */
	sql_errno = sqlite3_step (sql_stmt);
	if (sql_errno != SQLITE_ROW) {
		if (sql_errno != SQLITE_DONE) {
			g_set_error (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);
		return NULL;
	}

	/* get new folder summary from prep'ed statement */
	summary = kolab_mail_info_db_sql_folder_new_from_stmt (sql_stmt);

	ok = kolab_util_sqlite_fnlz_stmt (kdb, sql_stmt, &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		kolab_folder_summary_free (summary);
		return NULL;
	}

	return summary;
}

static KolabMailInfoDbRecord*
kolab_mail_info_db_sql_record_new_from_stmt (sqlite3_stmt *sql_stmt)
{
	KolabMailInfoDbRecord* record = NULL;
	gchar *tmp_str = NULL;
	guint tmp_uint = 0;
	gint tmp_int = 0;
	gint ii = 0;
	gint msi = 0;

	if (sql_stmt == NULL)
		return NULL;

	record = kolab_mail_info_db_record_new ();
	record->summary = kolab_mail_summary_new ();

	for (ii = KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_KOLAB_UID;
	     ii <= KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_E_EMAIL_4;
	     ii++) {
		msi = ii - KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_KOLAB_UID;
		tmp_str = g_strdup ((gchar*) sqlite3_column_text (sql_stmt, ii));
		kolab_mail_summary_set_char_field (record->summary, msi, tmp_str);
	}

	for (ii = KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_TYPE;
	     ii <= KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_E_STATUS;
	     ii++) {
		msi = ii - KOLAB_MAIL_SUMMARY_SQLCOL_UINT_FIELD_FOLDER_TYPE;
		tmp_uint = (guint) sqlite3_column_int (sql_stmt, ii);
		kolab_mail_summary_set_uint_field (record->summary, msi, tmp_uint);
	}

	for (ii = KOLAB_MAIL_SUMMARY_SQLCOL_INT_FIELD_E_PRIORITY;
	     ii <= KOLAB_MAIL_SUMMARY_SQLCOL_INT_FIELD_E_PERCENT;
	     ii++) {
		msi = ii - KOLAB_MAIL_SUMMARY_SQLCOL_INT_FIELD_E_PRIORITY;
		tmp_int = sqlite3_column_int (sql_stmt, ii);
		kolab_mail_summary_set_int_field (record->summary, msi, tmp_int);
	}

	for (ii = KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_COMPLETE;
	     ii <= KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_E_HAS_ALARMS;
	     ii++) {
		msi = ii - KOLAB_MAIL_SUMMARY_SQLCOL_BOOL_FIELD_COMPLETE;
		tmp_int = sqlite3_column_int (sql_stmt, ii);
		kolab_mail_summary_set_bool_field (record->summary, msi, tmp_int);
	}

	for (ii = KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_UID_SYNC;
	     ii <= KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_SIDE_CHECKSUM;
	     ii++) {
		msi = ii - KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_CHAR_FIELD_IMAP_UID_SYNC;
		tmp_str = g_strdup ((gchar*) sqlite3_column_text (sql_stmt, ii));
		kolab_mail_info_db_record_set_char_field (record, msi, tmp_str);
	}

	for (ii = KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_UINT_FIELD_IMAP_FOLDER_TYPE;
	     ii <= KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_UINT_FIELD_IMAP_FOLDER_CONTEXT;
	     ii++) {
		msi = ii - KOLAB_MAIL_INFO_DB_RECORD_SQLCOL_UINT_FIELD_IMAP_FOLDER_TYPE;
		tmp_uint = (guint) sqlite3_column_int (sql_stmt, ii);
		kolab_mail_info_db_record_set_uint_field (record, msi, tmp_uint);
	}

	/* clear out the 'complete' bool field, wich is not really persistent */
	kolab_mail_summary_set_bool_field (record->summary,
	                                   KOLAB_MAIL_SUMMARY_BOOL_FIELD_COMPLETE,
	                                   FALSE);
	return record;
}

static KolabMailInfoDbRecord*
kolab_mail_info_db_sql_query_record (KolabUtilSqliteDb *kdb,
                                     const gchar *uid,
                                     const gchar *foldername,
                                     GError **err)
{
	KolabMailInfoDbRecord *record = NULL;
	gchar *tblname = NULL;
	gchar *sql_str = NULL;
	gint sql_errno = SQLITE_OK;
	sqlite3_stmt *sql_stmt = NULL;
	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);

	/* table name */
	tblname = kolab_mail_info_db_sql_record_new_tblname (foldername);

	/* prepare sql statement */
	sql_str = sqlite3_mprintf ("SELECT * FROM %Q WHERE %q=%Q;",
	                           tblname,
	                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_KOLAB_UID],
	                           uid);
	ok = kolab_util_sqlite_prep_stmt (kdb, &sql_stmt, sql_str, &tmp_err);
	sqlite3_free (sql_str);
	if (! ok) {
		g_free (tblname);
		g_propagate_error (err, tmp_err);
		return 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 (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);
		g_free (tblname);
		return NULL;
	}

	/* get new InfoDb record from prep'ed statement */
	record = kolab_mail_info_db_sql_record_new_from_stmt (sql_stmt);

	ok = kolab_util_sqlite_fnlz_stmt (kdb, sql_stmt, &tmp_err);
	g_free (tblname);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		kolab_mail_info_db_record_free (record);
		return NULL;
	}

	return record;
}

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

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

	sql_str = sqlite3_mprintf ("DELETE FROM %Q WHERE %q=%Q;",
	                           KOLAB_MAIL_INFO_DB_SQLITE_DB_TBL_FOLDERS,
	                           SCF[KOLAB_FOLDER_SUMMARY_SQLCOL_CHAR_FIELD_FOLDERNAME],
	                           foldername);

	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 gboolean
kolab_mail_info_db_sql_remove_record (KolabUtilSqliteDb *kdb,
                                      const gchar *uid,
                                      const gchar *foldername,
                                      GError **err)
{
	gchar *tblname = NULL;
	gchar *sql_str = NULL;
	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_info_db_sql_record_new_tblname (foldername);

	sql_str = sqlite3_mprintf ("DELETE FROM %Q WHERE %q=%Q;",
	                           tblname,
	                           SCR[KOLAB_MAIL_SUMMARY_SQLCOL_CHAR_FIELD_KOLAB_UID],
	                           uid);
	g_free (tblname);

	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;
	}

	g_debug ("%s: UID (%s) Folder (%s) deleted from SQLiteDB",
	         __func__, uid, foldername);

	return TRUE;
}

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

static void
kolab_mail_info_db_init (KolabMailInfoDb *object)
{
	KolabMailInfoDb *self = NULL;
	KolabMailInfoDbPrivate *priv = NULL;

	g_assert (KOLAB_IS_MAIL_INFO_DB (object));

	self = object;
	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	priv->ksettings = NULL;
	priv->is_up = FALSE;

	priv->kdb = NULL;
	priv->cache_fs = NULL;
	priv->cache_r = NULL;
}

static void
kolab_mail_info_db_dispose (GObject *object)
{
	KolabMailInfoDb *self = NULL;
	KolabMailInfoDbPrivate *priv = NULL;

	self = KOLAB_MAIL_INFO_DB (object);
	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

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

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

static void
kolab_mail_info_db_finalize (GObject *object)
{
	KolabMailInfoDb *self = NULL;
	KolabMailInfoDbPrivate *priv = NULL;

	self = KOLAB_MAIL_INFO_DB (object);
	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	if (priv->cache_fs != NULL)
		kolab_folder_summary_free (priv->cache_fs);
	if (priv->cache_r != NULL)
		g_hash_table_destroy (priv->cache_r);

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

static void
kolab_mail_info_db_class_init (KolabMailInfoDbClass *klass)
{
	GObjectClass* object_class = G_OBJECT_CLASS (klass);
	/* GObjectClass* parent_class = G_OBJECT_CLASS (klass); */

	g_type_class_add_private (klass, sizeof (KolabMailInfoDbPrivate));

	object_class->dispose = kolab_mail_info_db_dispose;
	object_class->finalize = kolab_mail_info_db_finalize;
}

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

gboolean
kolab_mail_info_db_configure  (KolabMailInfoDb *self,
                               KolabSettingsHandler *ksettings,
                               GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;

	g_assert (KOLAB_IS_MAIL_INFO_DB (self));
	g_assert (KOLAB_IS_SETTINGS_HANDLER (ksettings));
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

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

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

	return TRUE;
}

gboolean
kolab_mail_info_db_bringup (KolabMailInfoDb *self,
                            GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_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_INFO_DB_SQLITE_DB_FILE,
	                                            &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}
	ok = kolab_mail_info_db_sql_table_create (priv->kdb,
	                                          KOLAB_MAIL_INFO_DB_SQLITE_DB_TBL_FOLDERS,
	                                          KOLAB_MAIL_INFO_DB_TABLE_TYPE_FOLDER,
	                                          &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	/* local cache infrastructure */
	/* nothing to be done for priv->cache_fs */
	priv->cache_r = g_hash_table_new_full (g_str_hash,
	                                       g_str_equal,
	                                       g_free,
	                                       kolab_mail_info_db_record_gdestroy);

	priv->is_up = TRUE;
	return TRUE;
}

gboolean
kolab_mail_info_db_shutdown (KolabMailInfoDb *self,
                             GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_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;

	/* kill local cache infrastructure */
	kolab_folder_summary_free (priv->cache_fs);
	g_hash_table_destroy (priv->cache_r);
	priv->cache_fs = NULL;
	priv->cache_r = NULL;

	priv->is_up = FALSE;
	return TRUE;
}

/*----------------------------------------------------------------------------*/
/* UIDs */

GList*
kolab_mail_info_db_query_uids (KolabMailInfoDb *self,
                               const gchar *foldername,
                               const gchar *sexp,
                               gboolean sidecache_only,
                               gboolean include_deleted,
                               GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	GList *uids_lst = NULL;
	gboolean exists = FALSE;
	gchar *tblname = NULL;
	gchar *sql_str = NULL;
	gint sql_errno = SQLITE_OK;
	sqlite3_stmt *sql_stmt = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	/* TODO implement search by expression */
	if (sexp != NULL)
		g_warning ("%s: searching by expression not yet implemented!",
		           __func__);

	/* check for records table existence */
	tblname = kolab_mail_info_db_sql_record_new_tblname (foldername);
	exists = kolab_util_sqlite_table_exists (priv->kdb, tblname, &tmp_err);
	if (tmp_err != NULL) {
		g_free (tblname);
		g_propagate_error (err, tmp_err);
		return NULL;
	}
	if (! exists) {
		g_free (tblname);
		return NULL;
	}

	/* prepare sql statement */
	sql_str = sqlite3_mprintf ("SELECT * FROM %Q;", tblname);
	g_free (tblname);
	ok = kolab_util_sqlite_prep_stmt (priv->kdb, &sql_stmt, sql_str, &tmp_err);
	sqlite3_free (sql_str);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	while (TRUE) {
		const gchar *uid = NULL;
		KolabMailInfoDbRecord *record = NULL;
		KolabObjectCacheLocation location = KOLAB_OBJECT_CACHE_LOCATION_INVAL;
		KolabObjectCacheStatus status = KOLAB_OBJECT_CACHE_STATUS_INVAL;
		/* 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 (priv->kdb->db));
			}
			break;
		}

		/* read needed values */
		record = kolab_mail_info_db_sql_record_new_from_stmt (sql_stmt);
		g_assert (record->summary != NULL);

		/* TODO check folder context */

		uid = kolab_mail_summary_get_char_field (record->summary,
		                                         KOLAB_MAIL_SUMMARY_CHAR_FIELD_KOLAB_UID);
		status = kolab_mail_summary_get_uint_field (record->summary,
		                                            KOLAB_MAIL_SUMMARY_UINT_FIELD_CACHE_STATUS);
		location = kolab_mail_summary_get_uint_field (record->summary,
		                                              KOLAB_MAIL_SUMMARY_UINT_FIELD_CACHE_LOCATION);
		if (include_deleted) {
			if (status & KOLAB_OBJECT_CACHE_STATUS_DELETED)
				goto include_uid;
		}
		if (location == KOLAB_OBJECT_CACHE_LOCATION_NONE)
			goto skip_uid;
		if (sidecache_only) {
			if (! (location & KOLAB_OBJECT_CACHE_LOCATION_SIDE))
				goto skip_uid;
		}

	include_uid:
		uids_lst = g_list_prepend (uids_lst, g_strdup (uid));
	skip_uid:
		kolab_mail_info_db_record_free (record);
	}

	if (tmp_err == NULL) {
		ok = kolab_util_sqlite_fnlz_stmt (priv->kdb, sql_stmt, &tmp_err);
	} else {
		(void)kolab_util_sqlite_fnlz_stmt (priv->kdb, sql_stmt, NULL);
		ok = FALSE;
	}
	if (! ok) {
		kolab_util_glib_glist_free (uids_lst);
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	return uids_lst;
}

GList*
kolab_mail_info_db_query_changed_uids (KolabMailInfoDb *self,
                                       const gchar *foldername,
                                       const gchar *sexp,
                                       gboolean full_update,
                                       GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	GHashTable *records_upd_tbl = NULL;
	GList *uids_lst = NULL;
	GList *uids_lst_ptr = NULL;
	GList *deleted_uids_lst = NULL;
	gboolean exists = FALSE;
	gchar *tblname = NULL;
	gchar *sql_str = NULL;
	gint sql_errno = SQLITE_OK;
	sqlite3_stmt *sql_stmt = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	/* TODO implement search by expression */
	if (sexp != NULL)
		g_warning ("%s: searching by expression not yet implemented!",
		           __func__);

	/* check for records table existence */
	tblname = kolab_mail_info_db_sql_record_new_tblname (foldername);
	exists = kolab_util_sqlite_table_exists (priv->kdb,
	                                         tblname,
	                                         &tmp_err);
	if (tmp_err != NULL) {
		g_free (tblname);
		g_propagate_error (err, tmp_err);
		return NULL;
	}
	if (! exists) {
		g_free (tblname);
		return NULL;
	}

	/* prepare sql statement */
	sql_str = sqlite3_mprintf ("SELECT * FROM %Q;", tblname);
	g_free (tblname);
	ok = kolab_util_sqlite_prep_stmt (priv->kdb, &sql_stmt, sql_str, &tmp_err);
	sqlite3_free (sql_str);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	records_upd_tbl = g_hash_table_new_full (g_str_hash,
	                                         g_str_equal,
	                                         g_free,
	                                         kolab_mail_info_db_record_gdestroy);
	while (TRUE) {
		const gchar *uid = NULL;
		KolabMailInfoDbRecord *record = NULL;
		KolabObjectCacheLocation location = KOLAB_OBJECT_CACHE_LOCATION_INVAL;
		KolabObjectCacheStatus status = KOLAB_OBJECT_CACHE_STATUS_INVAL;
		/* 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 (priv->kdb->db));
			}
			break;
		}

		/* read needed values */
		record = kolab_mail_info_db_sql_record_new_from_stmt (sql_stmt);
		g_assert (record->summary != NULL);

		/* TODO check folder context */
		uid = kolab_mail_summary_get_char_field (record->summary,
		                                         KOLAB_MAIL_SUMMARY_CHAR_FIELD_KOLAB_UID);
		location = kolab_mail_summary_get_uint_field (record->summary,
		                                              KOLAB_MAIL_SUMMARY_UINT_FIELD_CACHE_LOCATION);
		status = kolab_mail_summary_get_uint_field (record->summary,
		                                            KOLAB_MAIL_SUMMARY_UINT_FIELD_CACHE_STATUS);

		/* generate lists of changed uids */
		if ((location == KOLAB_OBJECT_CACHE_LOCATION_NONE) ||
		    (status & (guint)KOLAB_OBJECT_CACHE_STATUS_DELETED)) {
			if (full_update) {
				deleted_uids_lst = g_list_prepend (deleted_uids_lst,
				                                   g_strdup (uid));
			}
			kolab_mail_info_db_record_free (record);
			continue;
		}

		if (! (status & KOLAB_OBJECT_CACHE_STATUS_CHANGED)) {
			kolab_mail_info_db_record_free (record);
			continue;
		}

		uids_lst = g_list_prepend (uids_lst, g_strdup (uid));

		if (full_update) {
			status &= (~(guint)KOLAB_OBJECT_CACHE_STATUS_CHANGED);
			kolab_mail_summary_set_uint_field (record->summary,
			                                   KOLAB_MAIL_SUMMARY_UINT_FIELD_CACHE_STATUS,
			                                   status);
			g_hash_table_insert (records_upd_tbl,
			                     g_strdup (uid),
			                     record);
		} else {
			kolab_mail_info_db_record_free (record);
		}
	}

	if (tmp_err == NULL) {
		ok = kolab_util_sqlite_fnlz_stmt (priv->kdb, sql_stmt, &tmp_err);
	} else {
		(void)kolab_util_sqlite_fnlz_stmt (priv->kdb, sql_stmt, NULL);
		ok = FALSE;
	}
	if (! ok) {
		g_hash_table_destroy (records_upd_tbl);
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	if (deleted_uids_lst != NULL)
		uids_lst = g_list_concat (deleted_uids_lst, uids_lst);

	if (full_update) {
		uids_lst_ptr = uids_lst;
		while (uids_lst_ptr != NULL) {
			gchar *uid = (gchar *)(uids_lst_ptr->data);
			KolabMailInfoDbRecord *record = NULL;
			record = g_hash_table_lookup (records_upd_tbl, uid);
			if (record == NULL) {
				ok = kolab_mail_info_db_remove_record (self,
				                                       uid,
				                                       foldername,
				                                       &tmp_err);
			} else {
				ok = kolab_mail_info_db_update_record (self,
				                                       record,
				                                       foldername,
				                                       &tmp_err);
				g_hash_table_remove (records_upd_tbl, uid);
			}
			if (! ok) {
				g_warning ("%s: %s", __func__, tmp_err->message);
				g_free (tmp_err);
				tmp_err = NULL;
			}
			uids_lst_ptr = g_list_next (uids_lst_ptr);
		}
	}

	g_hash_table_destroy (records_upd_tbl);
	return uids_lst;
}

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

GList*
kolab_mail_info_db_query_foldernames (KolabMailInfoDb *self,
                                      GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	GList *foldernames_list = NULL;
	gchar *sql_str = NULL;
	sqlite3_stmt *sql_stmt = NULL;
	gint sql_errno = SQLITE_OK;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	/* prepare sql statement */
	sql_str = sqlite3_mprintf ("SELECT * FROM %Q;",
	                           KOLAB_MAIL_INFO_DB_SQLITE_DB_TBL_FOLDERS);
	ok = kolab_util_sqlite_prep_stmt (priv->kdb, &sql_stmt, sql_str, &tmp_err);
	sqlite3_free (sql_str);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	while (TRUE) {
		const gchar *foldername = NULL;
		KolabFolderSummary *summary = NULL;
		KolabObjectCacheLocation location = KOLAB_OBJECT_CACHE_LOCATION_INVAL;
		/* get folder 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 (priv->kdb->db));
			}
			break;
		}
		summary = kolab_mail_info_db_sql_folder_new_from_stmt (sql_stmt);

		foldername = kolab_folder_summary_get_char_field (summary,
		                                                  KOLAB_FOLDER_SUMMARY_CHAR_FIELD_FOLDERNAME);
		location = kolab_folder_summary_get_uint_field (summary,
		                                                KOLAB_FOLDER_SUMMARY_UINT_FIELD_CACHE_LOCATION);
		if (location == KOLAB_OBJECT_CACHE_LOCATION_NONE)
			goto folder_skip;

		/* TODO more folder checks needed? */

		foldernames_list = g_list_prepend (foldernames_list,
		                                   g_strdup (foldername));
	folder_skip:
		kolab_folder_summary_free (summary);
	}

	if (tmp_err == NULL) {
		ok = kolab_util_sqlite_fnlz_stmt (priv->kdb, sql_stmt, &tmp_err);
	} else {
		(void)kolab_util_sqlite_fnlz_stmt (priv->kdb, sql_stmt, NULL);
		ok = FALSE;
	}
	if (! ok) {
		g_propagate_error (err, tmp_err);
		kolab_util_glib_glist_free (foldernames_list);
		return NULL;
	}

	return foldernames_list;
}

GList*
kolab_mail_info_db_query_foldernames_anon (gpointer self,
                                           GError **err)
{
	GList *list = NULL;
	KolabMailInfoDb *myself = KOLAB_MAIL_INFO_DB (self);

	list = kolab_mail_info_db_query_foldernames (myself, err);
	return list;
}

gboolean
kolab_mail_info_db_exists_foldername (KolabMailInfoDb *self,
                                      const gchar *foldername,
                                      GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	KolabFolderSummary *summary = NULL;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	if (priv->cache_fs != NULL) {
		const gchar *cfs_fn;
		cfs_fn = kolab_folder_summary_get_char_field (priv->cache_fs,
		                                              KOLAB_FOLDER_SUMMARY_CHAR_FIELD_FOLDERNAME);
		if (g_strcmp0 (cfs_fn, foldername) == 0)
			return TRUE;
	}

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

	if (summary == NULL)
		return FALSE;

	kolab_folder_summary_free (priv->cache_fs);
	priv->cache_fs = summary;

	return TRUE;
}

KolabFolderSummary*
kolab_mail_info_db_query_folder_summary (KolabMailInfoDb *self,
                                         const gchar *foldername,
                                         GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	KolabFolderSummary *summary = NULL;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	if (priv->cache_fs != NULL) {
		const gchar *cfs_fn;
		cfs_fn = kolab_folder_summary_get_char_field (priv->cache_fs,
		                                              KOLAB_FOLDER_SUMMARY_CHAR_FIELD_FOLDERNAME);
		if (g_strcmp0 (cfs_fn, foldername) == 0)
			return kolab_folder_summary_clone (priv->cache_fs);
	}

	summary = kolab_mail_info_db_sql_query_folder (priv->kdb,
	                                               foldername,
	                                               &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}
	if (summary == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_NOTFOUND,
		             _("Folder name not known to internal database: '%s'"),
		             foldername);
		return NULL;
	}

	kolab_folder_summary_free (priv->cache_fs);
	priv->cache_fs = summary;

	return kolab_folder_summary_clone (priv->cache_fs);
}

gboolean
kolab_mail_info_db_update_folder_summary (KolabMailInfoDb *self,
                                          const KolabFolderSummary *summary,
                                          GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	/* const gchar *foldername = NULL; */
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

	g_assert (KOLAB_IS_MAIL_INFO_DB (self));
	g_assert (summary != NULL);
	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	/* TODO check what needs to be done here */

	/* TODO Take care of re-adding folders which have been marked
	 *      for deletion but have not yet been physically removed
	 *      from SideCache / ImapClient - how to keep the transaction
	 *      log small? If we re-add a folder, we cannot keep the
	 *      deleted folder's contents (which might be exactly what
	 *      we should be doing here). Re-adding will be problematic
	 *      in offline-mode only, so we'll be dealing with SideCache
	 *      here rather than ImapClient. If a folder has been 'deleted'
	 *      in offline mode and re-added, KolabMailSynchronizer should
	 *      have a way of determining which UIDs resided within this
	 *      folder and should be deleted on the server
	 */

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

	kolab_folder_summary_free (priv->cache_fs);
	priv->cache_fs = kolab_folder_summary_clone (summary);

	return TRUE;
}

gboolean
kolab_mail_info_db_remove_folder (KolabMailInfoDb *self,
                                  const gchar *foldername,
                                  GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	gboolean exists = FALSE;
	gchar *tblname = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	/* check for folder existence */
	exists = kolab_mail_info_db_exists_foldername (self,
	                                               foldername,
	                                               &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}
	if (! exists) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_NOTFOUND,
		             _("Folder to be removed does not exist in internal database: '%s'"),
		             foldername);
		return FALSE;
	}

	/* remove folder summary from folders table */
	ok = kolab_mail_info_db_sql_remove_folder (priv->kdb,
	                                           foldername,
	                                           &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	/* drop associated records table */
	tblname = kolab_mail_info_db_sql_record_new_tblname (foldername);
	ok = kolab_util_sqlite_table_drop (priv->kdb, tblname, &tmp_err);
	g_free (tblname);
	if (! ok) {
		g_warning ("%s: %s", __func__, tmp_err->message);
		g_error_free (tmp_err);
		tmp_err = NULL;
	}

	/* free folder summary cache
	 * (simply drop it - no further checks)
	 */
	kolab_folder_summary_free (priv->cache_fs);
	priv->cache_fs = NULL;

	return TRUE;
}

/*----------------------------------------------------------------------------*/
/* mail summaries */

KolabMailSummary*
kolab_mail_info_db_query_mail_summary (KolabMailInfoDb *self,
                                       const gchar *uid,
                                       const gchar *foldername,
                                       GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	KolabMailInfoDbRecord *record = NULL;
	KolabMailSummary *summary = NULL;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	record = kolab_mail_info_db_query_record (self, uid, foldername, &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}

	if (record == NULL)
		return NULL;

	summary = record->summary;
	record->summary = NULL;
	kolab_mail_info_db_record_free (record);

	return summary;
}

/*----------------------------------------------------------------------------*/
/* full records */

KolabMailInfoDbRecord*
kolab_mail_info_db_query_record (KolabMailInfoDb *self,
                                 const gchar *uid,
                                 const gchar *foldername,
                                 GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	/* GHashTable *records_tbl = NULL; */
	KolabMailInfoDbRecord *record = NULL;
	KolabFolderSummary *folder_summary = NULL;
	KolabObjectCacheLocation location = KOLAB_OBJECT_CACHE_LOCATION_INVAL;
	gchar *tblname = NULL;
	gboolean exists = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	/* use local cache object if available */
	record = g_hash_table_lookup (priv->cache_r, foldername);
	if (record != NULL) {
		const gchar *cr_uid;
		cr_uid = kolab_mail_summary_get_char_field (record->summary,
		                                            KOLAB_MAIL_SUMMARY_CHAR_FIELD_KOLAB_UID);
		if (g_strcmp0 (cr_uid, uid) == 0)
			return kolab_mail_info_db_record_clone (record);
	}

	/* check if record table exists */
	tblname = kolab_mail_info_db_sql_record_new_tblname (foldername);
	exists = kolab_util_sqlite_table_exists (priv->kdb,
	                                         tblname,
	                                         &tmp_err);
	g_free (tblname);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}
	if (! exists)
		return NULL;

	record = kolab_mail_info_db_sql_query_record (priv->kdb,
	                                              uid,
	                                              foldername,
	                                              &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return NULL;
	}
	if (record == NULL)
		return NULL;

	g_assert (kolab_mail_summary_check (record->summary));

	folder_summary = kolab_mail_info_db_query_folder_summary (self,
	                                                          foldername,
	                                                          &tmp_err);

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

	if (folder_summary == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_DATATYPE_INTERNAL,
		             _("Internal inconsistency detected: Folder without summary information, UID '%s', Folder '%s'"),
		             uid, foldername);
		kolab_mail_info_db_record_free (record);
		return NULL;
	}
	location = kolab_folder_summary_get_uint_field (folder_summary,
	                                                KOLAB_FOLDER_SUMMARY_UINT_FIELD_CACHE_LOCATION);
	kolab_folder_summary_free (folder_summary);

	if (location == KOLAB_OBJECT_CACHE_LOCATION_NONE) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_DATATYPE_INTERNAL,
		             _("Internal inconsistency detected: PIM Object has its folder deleted, UID '%s', Folder '%s'"),
		             uid, foldername);
		kolab_mail_info_db_record_free (record);
		return NULL;
	}

	/* update InfoDb record cache */
	g_hash_table_replace (priv->cache_r, g_strdup (foldername), record);

	return kolab_mail_info_db_record_clone (record);
}

gboolean
kolab_mail_info_db_update_record (KolabMailInfoDb *self,
                                  const KolabMailInfoDbRecord *record,
                                  const gchar *foldername,
                                  GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	KolabFolderSummary *folder_summary = NULL;
	KolabObjectCacheLocation location = KOLAB_OBJECT_CACHE_LOCATION_INVAL;
	/* const gchar *uid = NULL; */
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	g_assert (kolab_mail_summary_check (record->summary));

	/* check folder existence */
	folder_summary = kolab_mail_info_db_query_folder_summary (self,
	                                                          foldername,
	                                                          &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}
	if (folder_summary == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_INFODB_NOFOLDER,
		             _("Folder name not known to internal database: '%s'"),
		             foldername);
		return FALSE;
	}

	/* if a folder has been marked as 'deleted', it needs to be re-created
	 * prior to adding new objects to it
	 */
	location = kolab_folder_summary_get_uint_field (folder_summary,
	                                                KOLAB_FOLDER_SUMMARY_UINT_FIELD_CACHE_LOCATION);
	kolab_folder_summary_free (folder_summary);
	if (location == KOLAB_OBJECT_CACHE_LOCATION_NONE) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_INFODB_NOFOLDER,
		             _("Destination folder has been deleted: '%s'"),
		             foldername);
		return FALSE;
	}

	/* The record data passed in is written into InfoDB tables
	 * as-is, which means that the caller of this function must
	 * make sure all record data has been properly set up for a
	 * new entry or has properly been merged with existing data
	 * if the entry did already exist
	 */
	ok = kolab_mail_info_db_sql_update_record (priv->kdb,
	                                           record,
	                                           foldername,
	                                           &tmp_err);
	if (! ok) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	/* update InfoDb record cache */
	g_hash_table_replace (priv->cache_r,
	                      g_strdup (foldername),
	                      kolab_mail_info_db_record_clone (record));
	return TRUE;
}

gboolean
kolab_mail_info_db_remove_record (KolabMailInfoDb *self,
                                  const gchar *uid,
                                  const gchar *foldername,
                                  GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	KolabMailInfoDbRecord *record = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

	/* TODO need a more efficient implementation here */
	record = kolab_mail_info_db_sql_query_record (priv->kdb,
	                                              uid,
	                                              foldername,
	                                              &tmp_err);
	if (tmp_err != NULL) {
		g_propagate_error (err, tmp_err);
		return FALSE;
	}

	if (record == NULL) {
		g_set_error (err,
		             KOLAB_BACKEND_ERROR,
		             KOLAB_BACKEND_ERROR_NOTFOUND,
		             _("Record missing in internal database, UID '%s', Folder '%s'"),
		             uid, foldername);
		return FALSE;
	}

	kolab_mail_info_db_record_free (record);

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

	/* drop record from cache */
	g_hash_table_remove (priv->cache_r, foldername);

	return TRUE;
}

/*----------------------------------------------------------------------------*/
/* transaction handling */

gboolean
kolab_mail_info_db_transaction_start (KolabMailInfoDb *self, GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

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

	return TRUE;
}

gboolean
kolab_mail_info_db_transaction_commit (KolabMailInfoDb *self, GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

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

	return TRUE;
}

gboolean
kolab_mail_info_db_transaction_abort (KolabMailInfoDb *self, GError **err)
{
	KolabMailInfoDbPrivate *priv = NULL;
	gboolean ok = FALSE;
	GError *tmp_err = NULL;

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

	priv = KOLAB_MAIL_INFO_DB_PRIVATE (self);

	g_assert (priv->is_up == TRUE);

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

	return TRUE;
}

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