]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added a notify plugin that can be used as a generic event catching framework by other...
authorTimo Sirainen <tss@iki.fi>
Mon, 24 Aug 2009 20:57:50 +0000 (16:57 -0400)
committerTimo Sirainen <tss@iki.fi>
Mon, 24 Aug 2009 20:57:50 +0000 (16:57 -0400)
Patch by Mark Washenberger / Rackspace.

--HG--
branch : HEAD

configure.in
src/plugins/Makefile.am
src/plugins/notify/Makefile.am [new file with mode: 0644]
src/plugins/notify/notify-noop.c [new file with mode: 0644]
src/plugins/notify/notify-plugin-private.h [new file with mode: 0644]
src/plugins/notify/notify-plugin.c [new file with mode: 0644]
src/plugins/notify/notify-plugin.h [new file with mode: 0644]
src/plugins/notify/notify-storage.c [new file with mode: 0644]

index 76b816af0178eedb6da61e74be58bbb231bd859f..2330800e04d8df967ea970087696aabadc6c251c 100644 (file)
@@ -2506,6 +2506,7 @@ src/plugins/lazy-expunge/Makefile
 src/plugins/listescape/Makefile
 src/plugins/mail-log/Makefile
 src/plugins/mbox-snarf/Makefile
+src/plugins/notify/Makefile
 src/plugins/quota/Makefile
 src/plugins/imap-quota/Makefile
 src/plugins/trash/Makefile
index a6dcc92bf0f452b6e9723e179b0a81e27749048e..e1d05d93282d6cbea644b99bde764986a1542f0d 100644 (file)
@@ -22,6 +22,7 @@ SUBDIRS = \
        listescape \
        mail-log \
        mbox-snarf \
+       notify \
        quota \
        imap-quota \
        trash \
diff --git a/src/plugins/notify/Makefile.am b/src/plugins/notify/Makefile.am
new file mode 100644 (file)
index 0000000..a5ab874
--- /dev/null
@@ -0,0 +1,29 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/lib \
+       -I$(top_srcdir)/src/lib-mail \
+       -I$(top_srcdir)/src/lib-imap \
+       -I$(top_srcdir)/src/lib-index \
+       -I$(top_srcdir)/src/lib-storage \
+       -I$(top_srcdir)/src/lib-storage/index \
+       -I$(top_srcdir)/src/lib-storage/index/maildir
+
+lib15_notify_plugin_la_LDFLAGS = -module -avoid-version
+
+module_LTLIBRARIES = \
+       lib15_notify_plugin.la
+
+lib15_notify_plugin_la_SOURCES = \
+       notify-plugin.c \
+       notify-noop.c \
+       notify-storage.c
+
+noinst_HEADERS = \
+       notify-plugin.h
+       notify-plugin-private.h
+
+install-exec-local:
+       for d in imap pop3 lda; do \
+         $(mkdir_p) $(DESTDIR)$(moduledir)/$$d; \
+         rm -f $(DESTDIR)$(moduledir)/$$d/lib15_notify_plugin$(MODULE_SUFFIX); \
+         $(LN_S) ../lib15_notify_plugin$(MODULE_SUFFIX) $(DESTDIR)$(moduledir)/$$d; \
+       done
diff --git a/src/plugins/notify/notify-noop.c b/src/plugins/notify/notify-noop.c
new file mode 100644 (file)
index 0000000..c3b2fca
--- /dev/null
@@ -0,0 +1,32 @@
+#include "lib.h"
+#include "mail-types.h"
+#include "notify-plugin.h"
+
+void notify_noop_mail_transaction_begin(struct mailbox_transaction_context *t ATTR_UNUSED) {}
+void notify_noop_mail_save(void *txn ATTR_UNUSED,
+                          struct mail *mail ATTR_UNUSED) {}
+void notify_noop_mail_copy(void *txn ATTR_UNUSED,
+                          struct mail *src ATTR_UNUSED,
+                          struct mail *dst ATTR_UNUSED) {}
+void notify_noop_mail_expunge(void *txn ATTR_UNUSED,
+                             struct mail *mail ATTR_UNUSED) {}
+void notify_noop_mail_update_flags(void *txn ATTR_UNUSED,
+                                  struct mail *mail ATTR_UNUSED,
+                                  enum mail_flags old_flags ATTR_UNUSED) {}
+void notify_noop_mail_update_keywords(void *txn ATTR_UNUSED,
+                                     struct mail *mail ATTR_UNUSED,
+                                     const char *const *old_keywords ATTR_UNUSED) {}
+void notify_noop_mail_transaction_commit(void *txn ATTR_UNUSED,
+                                        struct mail_transaction_commit_changes *changes ATTR_UNUSED) {}
+void notify_noop_mail_transaction_rollback(void *txn ATTR_UNUSED) {}
+void *notify_noop_mailbox_delete_begin(struct mailbox_list *list ATTR_UNUSED,
+                                      const char *name ATTR_UNUSED) { return NULL; }
+void notify_noop_mailbox_delete_commit(void *txn ATTR_UNUSED,
+                                      struct mailbox_list *list ATTR_UNUSED,
+                                      const char *name ATTR_UNUSED) {}
+void notify_noop_mailbox_delete_rollback(void *txn ATTR_UNUSED) {}
+void notify_noop_mailbox_rename(struct mailbox_list *oldlist ATTR_UNUSED,
+                               const char *oldname ATTR_UNUSED,
+                               struct mailbox_list *newlist ATTR_UNUSED,
+                               const char *newname ATTR_UNUSED,
+                               bool rename_children ATTR_UNUSED) {}
diff --git a/src/plugins/notify/notify-plugin-private.h b/src/plugins/notify/notify-plugin-private.h
new file mode 100644 (file)
index 0000000..d464cfe
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef NOTIFY_PLUGIN_PRIVATE_H
+#define NOTIFY_PLUGIN_PRIVATE_H
+
+#include "notify-plugin.h"
+
+void notify_contexts_mail_transaction_begin(struct mailbox_transaction_context *t);
+void notify_contexts_mail_save(struct mail *mail);
+void notify_contexts_mail_copy(struct mail *src, struct mail *dst);
+void notify_contexts_mail_expunge(struct mail *mail);
+void notify_contexts_mail_update_flags(struct mail *mail,
+                                      enum mail_flags old_flags);
+void notify_contexts_mail_update_keywords(struct mail *mail,
+                                         const char *const *old_keywords);
+void notify_contexts_mail_transaction_commit(struct mailbox_transaction_context *t,
+                                            struct mail_transaction_commit_changes *changes);
+void notify_contexts_mail_transaction_rollback(struct mailbox_transaction_context *t);
+void notify_contexts_mailbox_delete_begin(struct mailbox_list *list,
+                                         const char *name);
+void notify_contexts_mailbox_delete_commit(struct mailbox_list *list,
+                                          const char *name);
+void notify_contexts_mailbox_delete_rollback(void);
+void notify_contexts_mailbox_rename(struct mailbox_list *oldlist,
+                                   const char *oldname,
+                                   struct mailbox_list *newlist,
+                                   const char *newname, bool rename_children);
+
+void notify_plugin_init_storage(void);
+void notify_plugin_deinit_storage(void);
+
+#endif
diff --git a/src/plugins/notify/notify-plugin.c b/src/plugins/notify/notify-plugin.c
new file mode 100644 (file)
index 0000000..ccd6f6f
--- /dev/null
@@ -0,0 +1,210 @@
+#include "lib.h"
+#include "llist.h"
+#include "mail-storage.h"
+#include "notify-plugin-private.h"
+
+#include <stdlib.h>
+
+struct notify_mail_txn {
+       struct notify_mail_txn *prev, *next;
+       struct mailbox_transaction_context *parent_mailbox_txn;
+       struct mail *tmp_mail;
+       void *txn;
+};
+
+struct notify_context {
+       struct notify_context *prev, *next;
+       struct notify_vfuncs v;
+       struct notify_mail_txn *mail_txn_list;
+       void *mailbox_delete_txn;
+};
+
+static struct notify_context *ctx_list = NULL;
+
+static struct notify_mail_txn *
+notify_context_find_mail_txn(struct notify_context *ctx,
+                            struct mailbox_transaction_context *t)
+{
+       struct notify_mail_txn *mail_txn = ctx->mail_txn_list;
+
+       for (; mail_txn != NULL; mail_txn = mail_txn->next) {
+               if (mail_txn->parent_mailbox_txn == t)
+                       return mail_txn;
+       }
+       i_panic("no notify_mail_txn found");
+}
+
+void notify_contexts_mail_transaction_begin(struct mailbox_transaction_context *t)
+{
+       struct notify_context *ctx;
+       struct notify_mail_txn *mail_txn;
+       
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               mail_txn = i_new(struct notify_mail_txn, 1);
+               mail_txn->parent_mailbox_txn = t;
+               mail_txn->txn = ctx->v.mail_transaction_begin(t);
+               DLLIST_PREPEND(&ctx->mail_txn_list, mail_txn);
+       }
+}
+
+void notify_contexts_mail_save(struct mail *mail)
+{
+       struct notify_context *ctx;
+       struct notify_mail_txn *mail_txn;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               mail_txn = notify_context_find_mail_txn(ctx, mail->transaction);
+               ctx->v.mail_save(mail_txn->txn, mail);
+       }
+}
+
+void notify_contexts_mail_copy(struct mail *src, struct mail *dst)
+{
+       struct notify_context *ctx;
+       struct notify_mail_txn *mail_txn;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               mail_txn = notify_context_find_mail_txn(ctx, dst->transaction);
+               ctx->v.mail_copy(mail_txn->txn, src, dst);
+       }
+}
+
+void notify_contexts_mail_expunge(struct mail *mail)
+{
+       struct notify_context *ctx;
+       struct notify_mail_txn *mail_txn;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               mail_txn = notify_context_find_mail_txn(ctx, mail->transaction);
+               ctx->v.mail_expunge(mail_txn->txn, mail);
+       }
+}
+
+void notify_contexts_mail_update_flags(struct mail *mail,
+                                      enum mail_flags old_flags)
+{
+       struct notify_context *ctx;
+       struct notify_mail_txn *mail_txn;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               mail_txn = notify_context_find_mail_txn(ctx, mail->transaction);
+               ctx->v.mail_update_flags(mail_txn->txn, mail, old_flags);
+       }
+}
+
+void notify_contexts_mail_update_keywords(struct mail *mail,
+                                         const char *const *old_keywords)
+{
+       struct notify_context *ctx;
+       struct notify_mail_txn *mail_txn;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               mail_txn = notify_context_find_mail_txn(ctx, mail->transaction);
+               ctx->v.mail_update_keywords(mail_txn->txn, mail, old_keywords);
+       }
+}
+
+void notify_contexts_mail_transaction_commit(struct mailbox_transaction_context *t,
+                                            struct mail_transaction_commit_changes *changes)
+{
+       struct notify_context *ctx;
+       struct notify_mail_txn *mail_txn;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               mail_txn = notify_context_find_mail_txn(ctx, t);
+               ctx->v.mail_transaction_commit(mail_txn->txn, changes);
+               DLLIST_REMOVE(&ctx->mail_txn_list, mail_txn);
+               i_free(mail_txn);
+       }
+}
+
+void notify_contexts_mail_transaction_rollback(struct mailbox_transaction_context *t)
+{
+       struct notify_context *ctx;
+       struct notify_mail_txn *mail_txn;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               mail_txn = notify_context_find_mail_txn(ctx, t);
+               ctx->v.mail_transaction_rollback(mail_txn->txn);
+               DLLIST_REMOVE(&ctx->mail_txn_list, mail_txn);
+               i_free(mail_txn);
+       }
+}
+
+void notify_contexts_mailbox_delete_begin(struct mailbox_list *list,
+                                         const char *name)
+{
+       struct notify_context *ctx;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               ctx->mailbox_delete_txn =
+                       ctx->v.mailbox_delete_begin(list, name);
+       }
+}
+
+void notify_contexts_mailbox_delete_commit(struct mailbox_list *list,
+                                          const char *name)
+{
+       struct notify_context *ctx;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               ctx->v.mailbox_delete_commit(ctx->mailbox_delete_txn,
+                                            list, name);
+               ctx->mailbox_delete_txn = NULL;
+       }
+}
+
+void notify_contexts_mailbox_delete_rollback(void)
+{
+       struct notify_context *ctx;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               ctx->v.mailbox_delete_rollback(ctx->mailbox_delete_txn);
+               ctx->mailbox_delete_txn = NULL;
+       }
+}
+
+void notify_contexts_mailbox_rename(struct mailbox_list *oldlist,
+                                   const char *oldname,
+                                   struct mailbox_list *newlist,
+                                   const char *newname, bool rename_children)
+{
+       struct notify_context *ctx;
+
+       for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
+               ctx->v.mailbox_rename(oldlist, oldname, newlist, newname,
+                                     rename_children);
+       }
+}
+
+struct notify_context *
+notify_register(const struct notify_vfuncs *v)
+{
+       struct notify_context *ctx;
+
+       ctx = i_new(struct notify_context, 1);
+       ctx->v = *v;
+       DLLIST_PREPEND(&ctx_list, ctx);
+       return ctx;
+}
+
+void notify_unregister(struct notify_context *ctx)
+{
+       struct notify_mail_txn *mail_txn = ctx->mail_txn_list;
+
+       for (; mail_txn != NULL; mail_txn = mail_txn->next)
+               ctx->v.mail_transaction_rollback(mail_txn->txn);
+       ctx->v.mailbox_delete_rollback(ctx->mailbox_delete_txn);
+       DLLIST_REMOVE(&ctx_list, ctx);
+       i_free(ctx);
+}
+
+void notify_plugin_init(void)
+{
+       notify_plugin_init_storage();
+}
+
+void notify_plugin_deinit(void)
+{
+       notify_plugin_deinit_storage();
+}
diff --git a/src/plugins/notify/notify-plugin.h b/src/plugins/notify/notify-plugin.h
new file mode 100644 (file)
index 0000000..f268897
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef NOTIFY_PLUGIN_H
+#define NOTIFY_PLUGIN_H
+
+enum mail_flags;
+struct mail;
+struct mail_transaction_commit_changes;
+struct mail_storage;
+struct mailbox_transaction_context;
+struct mailbox_list;
+struct notify_context;
+
+struct notify_vfuncs {
+       void *(*mail_transaction_begin)(struct mailbox_transaction_context *t);
+       void (*mail_save)(void *txn, struct mail *mail);
+       void (*mail_copy)(void *txn, struct mail *src, struct mail *dst);
+       void (*mail_expunge)(void *txn, struct mail *mail);
+       void (*mail_update_flags)(void *txn, struct mail *mail,
+                                 enum mail_flags old_flags);
+       void (*mail_update_keywords)(void *txn, struct mail *mail,
+                                    const char *const *old_keywords);
+       void (*mail_transaction_commit)(void *txn,
+                       struct mail_transaction_commit_changes *changes);
+       void (*mail_transaction_rollback)(void *txn);
+       void *(*mailbox_delete_begin)(struct mailbox_list *list, 
+                                     const char *name);
+       void (*mailbox_delete_commit)(void *txn, struct mailbox_list *list,
+                                     const char *name);
+       void (*mailbox_delete_rollback)(void *txn);
+       void (*mailbox_rename)(struct mailbox_list *oldlist,
+                              const char *oldname,
+                              struct mailbox_list *newlist,
+                              const char *newname, bool rename_children);
+};
+
+void notify_noop_mail_transaction_begin(struct mailbox_transaction_context *t);
+void notify_noop_mail_save(void *txn, struct mail *mail);
+void notify_noop_mail_copy(void *txn, struct mail *src, struct mail *dst);
+void notify_noop_mail_expunge(void *txn, struct mail *mail);
+void notify_noop_mail_update_flags(void *txn, struct mail *mail,
+                                  enum mail_flags old_flags);
+void notify_noop_mail_update_keywords(void *txn, struct mail *mail,
+                                     const char *const *old_keywords);
+void notify_noop_mail_transaction_commit(void *txn,
+                                        struct mail_transaction_commit_changes *changes);
+void notify_noop_mail_transaction_rollback(void *txn);
+void *notify_noop_mailbox_delete_begin(struct mailbox_list *list,
+                                      const char *name);
+void notify_noop_mailbox_delete_commit(void *txn, struct mailbox_list *list,
+                                      const char *name);
+void notify_noop_mailbox_delete_rollback(void *txn);
+void notify_noop_mailbox_rename(struct mailbox_list *oldlist,
+                               const char *oldname,
+                               struct mailbox_list *newlist,
+                               const char *newname, bool rename_children);
+
+struct notify_context *
+notify_register(const struct notify_vfuncs *vfuncs);
+void notify_unregister(struct notify_context *ctx);
+
+void notify_plugin_init(void);
+void notify_plugin_deinit(void);
+
+#endif
diff --git a/src/plugins/notify/notify-storage.c b/src/plugins/notify/notify-storage.c
new file mode 100644 (file)
index 0000000..8f35dd9
--- /dev/null
@@ -0,0 +1,311 @@
+#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)
+#define NOTIFY_LIST_CONTEXT(obj) \
+       MODULE_CONTEXT(obj, notify_mailbox_list_module)
+
+struct notify_transaction_context {
+       union mailbox_transaction_module_context module_ctx;
+       struct mail *tmp_mail;
+       bool save_hack;
+};
+
+static MODULE_CONTEXT_DEFINE_INIT(notify_storage_module,
+                                 &mail_storage_module_register);
+static MODULE_CONTEXT_DEFINE_INIT(notify_mail_module,
+                                 &mail_module_register);
+static MODULE_CONTEXT_DEFINE_INIT(notify_mailbox_list_module,
+                                 &mailbox_list_module_register);
+
+static void (*notify_next_hook_mail_storage_created)
+       (struct mail_storage *storage);
+static void (*notify_next_hook_mailbox_list_created)
+       (struct mailbox_list *list);
+
+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 struct mail *
+notify_mail_alloc(struct mailbox_transaction_context *t,
+                 enum mail_fetch_field wanted_fields,
+                 struct mailbox_header_lookup_ctx *wanted_headers)
+{
+       union mailbox_module_context *lbox = NOTIFY_CONTEXT(t->box);
+       union mail_module_context *lmail;
+       struct mail *_mail;
+       struct mail_private *mail;
+
+       _mail = lbox->super.mail_alloc(t, wanted_fields, wanted_headers);
+       mail = (struct mail_private *)_mail;
+
+       lmail = p_new(mail->pool, union mail_module_context, 1);
+       lmail->super = mail->v;
+
+       mail->v.expunge = notify_mail_expunge;
+       mail->v.update_flags = notify_mail_update_flags;
+       mail->v.update_keywords = notify_mail_update_keywords;
+       MODULE_CONTEXT_SET_SELF(mail, notify_mail_module, lmail);
+       return _mail;
+}
+
+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) {
+               if (lt->tmp_mail == NULL)
+                       lt->tmp_mail = mail_alloc(ctx->transaction, 0, NULL);
+               ctx->dest_mail = lt->tmp_mail;
+       }
+
+       lt->save_hack = TRUE;
+       ret = lbox->super.copy(ctx, mail);
+       lt->save_hack = FALSE;
+
+       if (ret == 0)
+               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) {
+               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)
+{
+       struct notify_transaction_context *lt =
+               NOTIFY_CONTEXT(ctx->transaction);
+       union mailbox_module_context *lbox =
+               NOTIFY_CONTEXT(ctx->transaction->box);
+
+       if (lbox->super.save_finish(ctx) < 0)
+               return -1;
+       if (!lt->save_hack)
+               notify_contexts_mail_save(ctx->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);
+
+       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;
+       }
+
+       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);
+       
+       notify_contexts_mail_transaction_rollback(t);
+       lbox->super.transaction_rollback(t);
+}
+
+static struct mailbox *
+notify_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
+                    const char *name, struct istream *input,
+                    enum mailbox_flags flags)
+{
+       union mail_storage_module_context *lstorage = NOTIFY_CONTEXT(storage);
+       struct mailbox *box;
+       union mailbox_module_context *lbox;
+
+       box = lstorage->super.mailbox_alloc(storage, list, name, input, flags);
+
+       lbox = p_new(box->pool, union mailbox_module_context, 1);
+       lbox->super = box->v;
+
+       box->v.mail_alloc = notify_mail_alloc;
+       box->v.copy = notify_copy;
+       box->v.save_begin = notify_save_begin;
+       box->v.save_finish = notify_save_finish;
+       box->v.transaction_begin = notify_transaction_begin;
+       box->v.transaction_commit = notify_transaction_commit;
+       box->v.transaction_rollback = notify_transaction_rollback;
+       MODULE_CONTEXT_SET_SELF(box, notify_storage_module, lbox);
+       return box;
+}
+
+static int
+notify_mailbox_list_delete(struct mailbox_list *list, const char *name)
+{
+       union mailbox_list_module_context *llist = NOTIFY_LIST_CONTEXT(list);
+
+       notify_contexts_mailbox_delete_begin(list, name);
+       if (llist->super.delete_mailbox(list, name) < 0) {
+               notify_contexts_mailbox_delete_rollback();
+               return -1;
+       }
+       notify_contexts_mailbox_delete_commit(list, name);
+       return 0;
+}
+
+static int
+notify_mailbox_list_rename(struct mailbox_list *oldlist, const char *oldname,
+                          struct mailbox_list *newlist, const char *newname,
+                          bool rename_children)
+{
+       union mailbox_list_module_context *oldllist =
+               NOTIFY_LIST_CONTEXT(oldlist);
+
+       if (oldllist->super.rename_mailbox(oldlist, oldname, newlist, newname,
+                                          rename_children) < 0)
+               return -1;
+
+       notify_contexts_mailbox_rename(oldlist, oldname, newlist, newname,
+                                      rename_children);
+       return 0;
+}
+
+static void notify_mail_storage_created(struct mail_storage *storage)
+{
+       union mail_storage_module_context *lstorage;
+
+       lstorage = p_new(storage->pool, union mail_storage_module_context, 1);
+       lstorage->super = storage->v;
+       storage->v.mailbox_alloc = notify_mailbox_alloc;
+
+       MODULE_CONTEXT_SET_SELF(storage, notify_storage_module, lstorage);
+
+       if (notify_next_hook_mail_storage_created != NULL)
+               notify_next_hook_mail_storage_created(storage);
+}
+
+static void notify_mailbox_list_created(struct mailbox_list *list)
+{
+       union mailbox_list_module_context *llist;
+
+       llist = p_new(list->pool, union mailbox_list_module_context, 1);
+       llist->super = list->v;
+       list->v.delete_mailbox = notify_mailbox_list_delete;
+       list->v.rename_mailbox = notify_mailbox_list_rename;
+
+       MODULE_CONTEXT_SET_SELF(list, notify_mailbox_list_module, llist);
+
+       if (notify_next_hook_mailbox_list_created != NULL)
+               notify_next_hook_mailbox_list_created(list);
+}
+
+void notify_plugin_init_storage(void)
+{
+       notify_next_hook_mail_storage_created = hook_mail_storage_created;
+       hook_mail_storage_created = notify_mail_storage_created;
+
+       notify_next_hook_mailbox_list_created = hook_mailbox_list_created;
+       hook_mailbox_list_created = notify_mailbox_list_created;
+}
+
+void notify_plugin_deinit_storage(void)
+{
+       hook_mail_storage_created = notify_next_hook_mail_storage_created;
+       hook_mailbox_list_created = notify_next_hook_mailbox_list_created;
+}