/* Copyright (C) 2007 Timo Sirainen, LGPLv2.1 */ /* export DOVECOT=~/src/dovecot-1.0.0 gcc -fPIC -shared -Wall -I$DOVECOT -I$DOVECOT/src/lib \ -I$DOVECOT/src/lib-storage -I$DOVECOT/src/lib-mail \ -I$DOVECOT/src/lib-imap -DHAVE_CONFIG_H \ mbox-snarf-plugin.c -o mbox_snarf_plugin.so */ #include "lib.h" #include "array.h" #include "home-expand.h" #include "mail-search.h" #include "mail-storage-private.h" #include #include #define MBOX_SNARF_CONTEXT(obj) \ *((void **)array_idx_modifyable(&(obj)->module_contexts, \ mbox_snarf_storage_module_id)) struct mbox_snarf_mail_storage { struct mail_storage_vfuncs super; bool internal_namespace; const char *snarf_inbox_path; bool open_spool_inbox; }; struct mbox_snarf_mailbox { struct mailbox_vfuncs super; struct mailbox *spool_mbox; }; /* defined by imap, pop3, lda */ extern void (*hook_mail_storage_created)(struct mail_storage *storage); const char *mbox_snarf_plugin_version = PACKAGE_VERSION; static void (*mbox_snarf_next_hook_mail_storage_created) (struct mail_storage *storage); static unsigned int mbox_snarf_storage_module_id = 0; static bool mbox_snarf_storage_module_id_set = FALSE; static int sync_mailbox(struct mailbox *box) { struct mailbox_sync_context *ctx; struct mailbox_sync_rec sync_rec; struct mailbox_status status; ctx = mailbox_sync_init(box, MAILBOX_SYNC_FLAG_FULL_READ); while (mailbox_sync_next(ctx, &sync_rec) > 0) ; return mailbox_sync_deinit(&ctx, &status); } static int mbox_snarf(struct mailbox *srcbox, struct mailbox *destbox) { struct mail_search_arg search_arg; struct mail_search_context *search_ctx; struct mailbox_transaction_context *src_trans, *dest_trans; struct mail *mail; int ret; if (sync_mailbox(srcbox) < 0) return -1; memset(&search_arg, 0, sizeof(search_arg)); search_arg.type = SEARCH_ALL; src_trans = mailbox_transaction_begin(srcbox, 0); dest_trans = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL); search_ctx = mailbox_search_init(src_trans, NULL, &search_arg, NULL); mail = mail_alloc(src_trans, MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, NULL); while ((ret = mailbox_search_next(search_ctx, mail)) > 0) { if (mail->expunged) continue; if (mailbox_copy(dest_trans, mail, 0, NULL, NULL) < 0) { if (!mail->expunged) break; } mail_expunge(mail); } mail_free(&mail); if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; /* commit the copied messages to the destination mailbox. if we crash between that and between expunging the messages from the source mailbox, we're left with duplicates. */ if (ret < 0) mailbox_transaction_rollback(&dest_trans); else if (mailbox_transaction_commit(&dest_trans, 0) < 0) ret = -1; if (ret < 0) mailbox_transaction_rollback(&src_trans); else { if (mailbox_transaction_commit(&src_trans, 0) < 0) ret = -1; } return ret; } static struct mailbox_sync_context * mbox_snarf_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct mbox_snarf_mail_storage *mstorage = MBOX_SNARF_CONTEXT(box->storage); struct mbox_snarf_mailbox *mbox = MBOX_SNARF_CONTEXT(box); if (mbox->spool_mbox == NULL) { /* try to open the spool mbox */ mstorage->open_spool_inbox = TRUE; mbox->spool_mbox = mailbox_open(box->storage, "INBOX", NULL, MAILBOX_OPEN_KEEP_RECENT | MAILBOX_OPEN_NO_INDEX_FILES); mstorage->open_spool_inbox = FALSE; } if (mbox->spool_mbox != NULL) mbox_snarf(mbox->spool_mbox, box); return mbox->super.sync_init(box, flags); } static int mbox_snarf_close(struct mailbox *box) { struct mbox_snarf_mailbox *mbox = MBOX_SNARF_CONTEXT(box); if (mbox->spool_mbox != NULL) mailbox_close(&mbox->spool_mbox); return mbox->super.close(box); } static struct mailbox * mbox_snarf_mailbox_open(struct mail_storage *storage, const char *name, struct istream *input, enum mailbox_open_flags flags) { struct mbox_snarf_mail_storage *mstorage = MBOX_SNARF_CONTEXT(storage); struct mailbox *box; struct mbox_snarf_mailbox *mbox; struct stat st; enum mail_storage_flags old_flags = storage->flags; bool use_snarfing = FALSE; if (strcasecmp(name, "INBOX") == 0 && !mstorage->open_spool_inbox) { if (stat(mstorage->snarf_inbox_path, &st) == 0) { /* use ~/mbox as the INBOX */ name = mstorage->snarf_inbox_path; use_snarfing = TRUE; storage->flags |= MAIL_STORAGE_FLAG_FULL_FS_ACCESS; } else if (errno != ENOENT) { mail_storage_set_critical(storage, "stat(%s) failed: %m", mstorage->snarf_inbox_path); } } box = mstorage->super.mailbox_open(storage, name, input, flags); storage->flags = old_flags; if (box == NULL || !use_snarfing) return box; mbox = p_new(box->pool, struct mbox_snarf_mailbox, 1); mbox->super = box->v; box->v.sync_init = mbox_snarf_sync_init; box->v.close = mbox_snarf_close; array_idx_set(&box->module_contexts, mbox_snarf_storage_module_id, &mbox); return box; } static void mbox_snarf_mail_storage_created(struct mail_storage *storage) { struct mbox_snarf_mail_storage *mstorage; if (mbox_snarf_next_hook_mail_storage_created != NULL) mbox_snarf_next_hook_mail_storage_created(storage); mstorage = p_new(storage->pool, struct mbox_snarf_mail_storage, 1); mstorage->snarf_inbox_path = p_strdup(storage->pool, home_expand(getenv("MBOX_SNARF"))); mstorage->super = storage->v; storage->v.mailbox_open = mbox_snarf_mailbox_open; if (!mbox_snarf_storage_module_id_set) { mbox_snarf_storage_module_id = mail_storage_module_id++; mbox_snarf_storage_module_id_set = TRUE; } array_idx_set(&storage->module_contexts, mbox_snarf_storage_module_id, &mstorage); } void mbox_snarf_plugin_init(void); void mbox_snarf_plugin_deinit(void); void mbox_snarf_plugin_init(void) { const char *path; path = getenv("MBOX_SNARF"); if (path != NULL) { mbox_snarf_next_hook_mail_storage_created = hook_mail_storage_created; hook_mail_storage_created = mbox_snarf_mail_storage_created; } } void mbox_snarf_plugin_deinit(void) { if (getenv("MBOX_SNARF") != NULL) { hook_mail_storage_created = mbox_snarf_next_hook_mail_storage_created; } }