view src/plugins/notify/notify-storage.c @ 19317:16ff395c156e

lib-storage/notify: Added MAILBOX_TRANSACTION_FLAG_NO_NOTIFY This flag could potentially apply for other things as well, not just notify plugin. In general anything that could do external notifications shouldn't do anything for transactions with this flag.
author Timo Sirainen <tss@iki.fi>
date Tue, 20 Oct 2015 15:27:42 +0300
parents 3009a1a6f6d5
children 3d576d682137
line wrap: on
line source

/* Copyright (c) 2013-2015 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "mail-storage-private.h"
#include "mailbox-list-private.h"
#include "notify-plugin-private.h"

#define NOTIFY_CONTEXT(obj) \
	MODULE_CONTEXT(obj, notify_storage_module)
#define NOTIFY_MAIL_CONTEXT(obj) \
	MODULE_CONTEXT(obj, notify_mail_module)

struct notify_transaction_context {
	union mailbox_transaction_module_context module_ctx;
	struct mail *tmp_mail;
};

static MODULE_CONTEXT_DEFINE_INIT(notify_storage_module,
				  &mail_storage_module_register);
static MODULE_CONTEXT_DEFINE_INIT(notify_mail_module,
				  &mail_module_register);

static void
notify_mail_expunge(struct mail *_mail)
{
	struct mail_private *mail = (struct mail_private *)_mail;
	union mail_module_context *lmail = NOTIFY_MAIL_CONTEXT(mail);
	
	notify_contexts_mail_expunge(_mail);
	lmail->super.expunge(_mail);
}

static void
notify_mail_update_flags(struct mail *_mail, enum modify_type modify_type,
			 enum mail_flags flags)
{
	struct mail_private *mail = (struct mail_private *)_mail;
	union mail_module_context *lmail = NOTIFY_MAIL_CONTEXT(mail);
	enum mail_flags old_flags, new_flags;

	old_flags = mail_get_flags(_mail);
	lmail->super.update_flags(_mail, modify_type, flags);
	new_flags = mail_get_flags(_mail);

	if ((old_flags ^ new_flags) == 0) 
		return;
	
	notify_contexts_mail_update_flags(_mail, old_flags);
}

static void
notify_mail_update_keywords(struct mail *_mail, enum modify_type modify_type,
			    struct mail_keywords *keywords)
{
	struct mail_private *mail = (struct mail_private *)_mail;
	union mail_module_context *lmail = NOTIFY_MAIL_CONTEXT(mail);
	const char *const *old_keywords, *const *new_keywords;
	unsigned int i;

	old_keywords = mail_get_keywords(_mail);
	lmail->super.update_keywords(_mail, modify_type, keywords);
	new_keywords = mail_get_keywords(_mail);

	for (i = 0; old_keywords[i] != NULL && new_keywords[i] != NULL; i++) {
		if (strcmp(old_keywords[i], new_keywords[i]) != 0)
			break;
	}

	if (old_keywords[i] == NULL && new_keywords[i] == NULL) 
		return;

	notify_contexts_mail_update_keywords(_mail, old_keywords);
}

static void notify_mail_allocated(struct mail *_mail)
{
	struct mail_private *mail = (struct mail_private *)_mail;
	struct mail_vfuncs *v = mail->vlast;
	union mail_module_context *lmail;

	if ((_mail->transaction->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) != 0)
		return;

	lmail = p_new(mail->pool, union mail_module_context, 1);
	lmail->super = *v;
	mail->vlast = &lmail->super;

	v->expunge = notify_mail_expunge;
	v->update_flags = notify_mail_update_flags;
	v->update_keywords = notify_mail_update_keywords;
	MODULE_CONTEXT_SET_SELF(mail, notify_mail_module, lmail);
}

static int
notify_copy(struct mail_save_context *ctx, struct mail *mail)
{
	struct notify_transaction_context *lt =
		NOTIFY_CONTEXT(ctx->transaction);
	union mailbox_module_context *lbox =
		NOTIFY_CONTEXT(ctx->transaction->box);
	int ret;

	if (ctx->dest_mail == NULL &&
	    (ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0) {
		if (lt->tmp_mail == NULL)
			lt->tmp_mail = mail_alloc(ctx->transaction, 0, NULL);
		ctx->dest_mail = lt->tmp_mail;
	}

	if ((ret = lbox->super.copy(ctx, mail)) < 0)
		return -1;

	if ((ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) != 0) {
		/* no notifications */
	} else if (ctx->saving) {
		/* we came from mailbox_save_using_mail() */
		notify_contexts_mail_save(ctx->dest_mail);
	} else {
		notify_contexts_mail_copy(mail, ctx->dest_mail);
	}
	return ret;
}

static int
notify_save_begin(struct mail_save_context *ctx, struct istream *input)
{
	struct notify_transaction_context *lt =
		NOTIFY_CONTEXT(ctx->transaction);
	union mailbox_module_context *lbox =
		NOTIFY_CONTEXT(ctx->transaction->box);

	if (ctx->dest_mail == NULL &&
	    (ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0) {
		if (lt->tmp_mail == NULL)
			lt->tmp_mail = mail_alloc(ctx->transaction, 0, NULL);
		ctx->dest_mail = lt->tmp_mail;
	}
	return lbox->super.save_begin(ctx, input);
}

static int
notify_save_finish(struct mail_save_context *ctx)
{
	union mailbox_module_context *lbox =
		NOTIFY_CONTEXT(ctx->transaction->box);
	struct mail *dest_mail = ctx->copying_via_save ? NULL : ctx->dest_mail;

	if (lbox->super.save_finish(ctx) < 0)
		return -1;
	if (dest_mail != NULL &&
	    (ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0)
		notify_contexts_mail_save(dest_mail);
	return 0;
}

static struct mailbox_transaction_context *
notify_transaction_begin(struct mailbox *box,
			 enum mailbox_transaction_flags flags)
{
	union mailbox_module_context *lbox = NOTIFY_CONTEXT(box);
	struct mailbox_transaction_context *t;
	struct notify_transaction_context *lt;
	
	t = lbox->super.transaction_begin(box, flags);

	lt = i_new(struct notify_transaction_context, 1);
	MODULE_CONTEXT_SET(t, notify_storage_module, lt);

	if ((t->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0)
		notify_contexts_mail_transaction_begin(t);
	return t;
}

static int
notify_transaction_commit(struct mailbox_transaction_context *t,
			  struct mail_transaction_commit_changes *changes_r)
{
	struct notify_transaction_context *lt = NOTIFY_CONTEXT(t);
	union mailbox_module_context *lbox = NOTIFY_CONTEXT(t->box);

	if (lt->tmp_mail != NULL)
		mail_free(&lt->tmp_mail);
	i_free(lt);

	if ((lbox->super.transaction_commit(t, changes_r)) < 0) {
		notify_contexts_mail_transaction_rollback(t);
		return -1;
	}

	/* FIXME: note that t is already freed at this stage. it's not actually
	   being dereferenced anymore though. still, a bit unsafe.. */
	if ((t->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0)
		notify_contexts_mail_transaction_commit(t, changes_r);
	return 0;
}

static void
notify_transaction_rollback(struct mailbox_transaction_context *t)
{
	struct notify_transaction_context *lt = NOTIFY_CONTEXT(t);
	union mailbox_module_context *lbox = NOTIFY_CONTEXT(t->box);

	if (lt->tmp_mail != NULL)
		mail_free(&lt->tmp_mail);
	i_free(lt);
	
	if ((t->flags & MAILBOX_TRANSACTION_FLAG_NO_NOTIFY) == 0)
		notify_contexts_mail_transaction_rollback(t);
	lbox->super.transaction_rollback(t);
}

static int
notify_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
		      bool directory)
{
	union mailbox_module_context *lbox = NOTIFY_CONTEXT(box);

	if (lbox->super.create_box(box, update, directory) < 0)
		return -1;

	notify_contexts_mailbox_create(box);
	return 0;
}

static int
notify_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
{
	union mailbox_module_context *lbox = NOTIFY_CONTEXT(box);

	if (lbox->super.update_box(box, update) < 0)
		return -1;

	notify_contexts_mailbox_update(box);
	return 0;
}

static int
notify_mailbox_delete(struct mailbox *box)
{
	union mailbox_module_context *lbox = NOTIFY_CONTEXT(box);

	notify_contexts_mailbox_delete_begin(box);
	if (lbox->super.delete_box(box) < 0) {
		notify_contexts_mailbox_delete_rollback();
		return -1;
	}
	notify_contexts_mailbox_delete_commit(box);
	return 0;
}

static int
notify_mailbox_rename(struct mailbox *src, struct mailbox *dest)
{
	union mailbox_module_context *lbox = NOTIFY_CONTEXT(src);

	if (lbox->super.rename_box(src, dest) < 0)
		return -1;

	notify_contexts_mailbox_rename(src, dest);
	return 0;
}

static int
notify_mailbox_set_subscribed(struct mailbox *box, bool set)
{
	union mailbox_module_context *lbox = NOTIFY_CONTEXT(box);

	if (lbox->super.set_subscribed(box, set) < 0)
		return -1;

	notify_contexts_mailbox_set_subscribed(box, set);
	return 0;
}

static void notify_mailbox_allocated(struct mailbox *box)
{
	struct mailbox_vfuncs *v = box->vlast;
	union mailbox_module_context *lbox;

	lbox = p_new(box->pool, union mailbox_module_context, 1);
	lbox->super = *v;
	box->vlast = &lbox->super;

	v->copy = notify_copy;
	v->save_begin = notify_save_begin;
	v->save_finish = notify_save_finish;
	v->transaction_begin = notify_transaction_begin;
	v->transaction_commit = notify_transaction_commit;
	v->transaction_rollback = notify_transaction_rollback;
	v->create_box = notify_mailbox_create;
	v->update_box = notify_mailbox_update;
	v->delete_box = notify_mailbox_delete;
	v->rename_box = notify_mailbox_rename;
	v->set_subscribed = notify_mailbox_set_subscribed;
	MODULE_CONTEXT_SET_SELF(box, notify_storage_module, lbox);
}

static struct mail_storage_hooks notify_mail_storage_hooks = {
	.mailbox_allocated = notify_mailbox_allocated,
	.mail_allocated = notify_mail_allocated
};

void notify_plugin_init_storage(struct module *module)
{
	mail_storage_hooks_add(module, &notify_mail_storage_hooks);
}

void notify_plugin_deinit_storage(void)
{
	mail_storage_hooks_remove(&notify_mail_storage_hooks);
}