/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "mail-copy.h"
#include "index-mail.h"
#include "mailbox-list-private.h"
#include "cydir-sync.h"
#include "cydir-storage.h"

#include <sys/stat.h>

extern struct mail_storage cydir_storage;
extern struct mailbox cydir_mailbox;

static struct mail_storage *cydir_storage_alloc(void)
{
	struct cydir_storage *storage;
	pool_t pool;

	pool = pool_alloconly_create("cydir storage", 512+256);
	storage = p_new(pool, struct cydir_storage, 1);
	storage->storage = cydir_storage;
	storage->storage.pool = pool;
	return &storage->storage;
}

static void
cydir_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
				struct mailbox_list_settings *set)
{
	if (set->layout == NULL)
		set->layout = MAILBOX_LIST_NAME_FS;
	if (set->subscription_fname == NULL)
		set->subscription_fname = CYDIR_SUBSCRIPTION_FILE_NAME;
}

static struct mailbox *
cydir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
		    const char *vname, enum mailbox_flags flags)
{
	struct cydir_mailbox *mbox;
	pool_t pool;

	/* cydir can't work without index files */
	flags &= ~MAILBOX_FLAG_NO_INDEX_FILES;

	pool = pool_alloconly_create("cydir mailbox", 1024*3);
	mbox = p_new(pool, struct cydir_mailbox, 1);
	mbox->box = cydir_mailbox;
	mbox->box.pool = pool;
	mbox->box.storage = storage;
	mbox->box.list = list;
	mbox->box.mail_vfuncs = &cydir_mail_vfuncs;

	index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX);

	mbox->storage = (struct cydir_storage *)storage;
	return &mbox->box;
}

static int cydir_mailbox_open(struct mailbox *box)
{
	const char *box_path = mailbox_get_path(box);
	struct stat st;

	if (stat(box_path, &st) == 0) {
		/* exists, open it */
	} else if (errno == ENOENT) {
		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
			T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname));
		return -1;
	} else if (errno == EACCES) {
		mail_storage_set_critical(box->storage, "%s",
			mail_error_eacces_msg("stat", box_path));
		return -1;
	} else {
		mail_storage_set_critical(box->storage, "stat(%s) failed: %m",
					  box_path);
		return -1;
	}
	if (index_storage_mailbox_open(box, FALSE) < 0)
		return -1;
	mail_index_set_fsync_mode(box->index,
				  box->storage->set->parsed_fsync_mode,
				  MAIL_INDEX_FSYNC_MASK_APPENDS |
				  MAIL_INDEX_FSYNC_MASK_EXPUNGES);
	return 0;
}

static int
cydir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
		     bool directory)
{
	int ret;

	if ((ret = index_storage_mailbox_create(box, directory)) <= 0)
		return ret;

	return update == NULL ? 0 :
		index_storage_mailbox_update(box, update);
}

static void cydir_notify_changes(struct mailbox *box)
{
	if (box->notify_callback == NULL)
		mailbox_watch_remove_all(box);
	else
		mailbox_watch_add(box, mailbox_get_path(box));
}

struct mail_storage cydir_storage = {
	.name = CYDIR_STORAGE_NAME,
	.class_flags = MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG |
		MAIL_STORAGE_CLASS_FLAG_BINARY_DATA,

	.v = {
		NULL,
		cydir_storage_alloc,
		NULL,
		index_storage_destroy,
		NULL,
		cydir_storage_get_list_settings,
		NULL,
		cydir_mailbox_alloc,
		NULL
	}
};

struct mailbox cydir_mailbox = {
	.v = {
		index_storage_is_readonly,
		index_storage_mailbox_enable,
		index_storage_mailbox_exists,
		cydir_mailbox_open,
		index_storage_mailbox_close,
		index_storage_mailbox_free,
		cydir_mailbox_create,
		index_storage_mailbox_update,
		index_storage_mailbox_delete,
		index_storage_mailbox_rename,
		index_storage_get_status,
		NULL,
		index_storage_set_subscribed,
		index_storage_attribute_set,
		index_storage_attribute_get,
		index_storage_attribute_iter_init,
		index_storage_attribute_iter_next,
		index_storage_attribute_iter_deinit,
		index_storage_list_index_has_changed,
		index_storage_list_index_update_sync,
		cydir_storage_sync_init,
		index_mailbox_sync_next,
		index_mailbox_sync_deinit,
		NULL,
		cydir_notify_changes,
		index_transaction_begin,
		index_transaction_commit,
		index_transaction_rollback,
		NULL,
		index_mail_alloc,
		index_storage_search_init,
		index_storage_search_deinit,
		index_storage_search_next_nonblock,
		index_storage_search_next_update_seq,
		cydir_save_alloc,
		cydir_save_begin,
		cydir_save_continue,
		cydir_save_finish,
		cydir_save_cancel,
		mail_storage_copy,
		cydir_transaction_save_commit_pre,
		cydir_transaction_save_commit_post,
		cydir_transaction_save_rollback,
		index_storage_is_inconsistent
	}
};
