snarf-plugin.c revision 0dab9cb35a976c49b28a11e28d5570f5191f1a7a
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen#include "array.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "unichar.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "mail-namespace.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "mail-search-build.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "mail-storage-private.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include "snarf-plugin.h"
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#define SNARF_CONTEXT(obj) \
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen MODULE_CONTEXT(obj, snarf_storage_module)
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainenstruct snarf_mail_storage {
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen union mail_storage_module_context module_ctx;
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen const char *snarf_path;
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen bool snarfing_disabled;
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen};
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainenstruct snarf_mailbox {
6cbe2facd40ea3461620571a1c168ce9884be3b3Timo Sirainen union mailbox_module_context module_ctx;
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen struct mailbox *snarf_box;
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen};
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainenconst char *snarf_plugin_version = DOVECOT_ABI_VERSION;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(snarf_storage_module,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen &mail_storage_module_register);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstatic int snarf(struct mailbox *srcbox, struct mailbox *destbox)
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen{
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen struct mail_search_args *search_args;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen struct mail_search_context *search_ctx;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen struct mailbox_transaction_context *src_trans, *dest_trans;
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainen struct mail_save_context *save_ctx;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen struct mail *mail;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen enum mail_error error;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen int ret;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen /* make sure the destination mailbox has been opened.
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen note that this locks the mailbox. */
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen if (mailbox_open(destbox) < 0)
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen return -1;
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (mailbox_sync(srcbox, MAILBOX_SYNC_FLAG_FULL_READ) < 0)
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen return -1;
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen src_trans = mailbox_transaction_begin(srcbox, 0, "snarf src_trans");
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen dest_trans = mailbox_transaction_begin(destbox,
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen MAILBOX_TRANSACTION_FLAG_EXTERNAL,
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen "snarf dest_trans");
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen search_args = mail_search_build_init();
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen mail_search_build_add_all(search_args);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen search_ctx = mailbox_search_init(src_trans, search_args, NULL,
3c296d819c54e21ce05c3d2eeeedc79be42ac593Timo Sirainen MAIL_FETCH_STREAM_HEADER |
3ab7783791bd46cdd46e9b9de3e98e8efcb6c6bfTimo Sirainen MAIL_FETCH_STREAM_BODY, NULL);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen mail_search_args_unref(&search_args);
3ab7783791bd46cdd46e9b9de3e98e8efcb6c6bfTimo Sirainen
3ab7783791bd46cdd46e9b9de3e98e8efcb6c6bfTimo Sirainen ret = 0;
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen while (mailbox_search_next(search_ctx, &mail)) {
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen if (mail->expunged)
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen continue;
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen save_ctx = mailbox_save_alloc(dest_trans);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen if (mailbox_copy(&save_ctx, mail) < 0 && !mail->expunged) {
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen error = mailbox_get_last_mail_error(destbox);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen /* if we failed because of out of disk space, just
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen move those messages we managed to move so far. */
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen if (error != MAIL_ERROR_NOQUOTA)
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen ret = -1;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen break;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen }
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen mail_expunge(mail);
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen }
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen if (mailbox_search_deinit(&search_ctx) < 0)
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen ret = -1;
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen /* commit the copied messages to the destination mailbox. if we crash
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen between that and between expunging the messages from the source
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen mailbox, we're left with duplicates. */
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen if (ret < 0)
553308791c097219e8eb31cbd03a29e9e1333848Timo Sirainen mailbox_transaction_rollback(&dest_trans);
24d7c5fc9fa1cb1f49402ec796654113199ba4e6Timo Sirainen else if (mailbox_transaction_commit(&dest_trans) < 0)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen ret = -1;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (ret < 0)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen mailbox_transaction_rollback(&src_trans);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen else {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if (mailbox_transaction_commit(&src_trans) < 0)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen ret = -1;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen }
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if (ret == 0) {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if (mailbox_sync(srcbox, 0) < 0)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen ret = -1;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
8759adc67109b5a12a7af3ed717c7040622a0a04Timo Sirainen return ret;
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
d31c77e63713a6cf3687a4b38ff8daf6d6c7a3ddTimo Sirainenstatic struct mailbox_sync_context *
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainensnarf_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen struct snarf_mailbox *sbox = SNARF_CONTEXT(box);
145d2eef238ed8bbff635e3b06951a83f0ee5a03Timo Sirainen
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen (void)snarf(sbox->snarf_box, box);
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen /* close the mailbox so that we don't have to keep it locked */
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen (void)mailbox_close(sbox->snarf_box);
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen return sbox->module_ctx.super.sync_init(box, flags);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen
7ace5117d5f2395bd66f20b09e77dac05492f7ceTimo Sirainenstatic void snarf_mailbox_free(struct mailbox *box)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen struct snarf_mailbox *sbox = SNARF_CONTEXT(box);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen mailbox_free(&sbox->snarf_box);
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen sbox->module_ctx.super.free(box);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstatic bool
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainensnarf_box_find(struct mail_user *user, struct mailbox_list **list_r,
3c296d819c54e21ce05c3d2eeeedc79be42ac593Timo Sirainen const char **name_r)
553308791c097219e8eb31cbd03a29e9e1333848Timo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen struct mail_namespace *snarf_ns;
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen const char *snarf_name;
7dcb5545370faa9d4ff83b3ede65a69fc3dd4b65Timo Sirainen
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen snarf_name = mail_user_plugin_getenv(user, "snarf");
cc0495b3bbe3c3e41c512274b302d6f0fa028187Timo Sirainen if (snarf_name == NULL)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return FALSE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (!uni_utf8_str_is_valid(snarf_name)) {
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen i_error("snarf: Mailbox name not UTF-8: %s", snarf_name);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return FALSE;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen snarf_ns = mail_namespace_find(user->namespaces, snarf_name);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen *list_r = snarf_ns->list;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen *name_r = snarf_name;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return TRUE;
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen}
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainenstatic void snarf_mailbox_allocated(struct mailbox *box)
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen{
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen struct snarf_mail_storage *sstorage = SNARF_CONTEXT(box->storage);
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen struct mailbox_vfuncs *v = box->vlast;
31a12066e4cd9310d64091c81b59fb8eb1986023Timo Sirainen struct snarf_mailbox *sbox;
61618d4c58080570f689614fec204ae14e90cef2Timo Sirainen struct mailbox_list *snarf_list;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *snarf_name;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (!box->inbox_user)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (sstorage != NULL && sstorage->snarfing_disabled)
8ef7c24178fd798c3e0301c5b8afa1a9bdedd27fTimo Sirainen return;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (!snarf_box_find(box->storage->user, &snarf_list, &snarf_name))
return;
sbox = p_new(box->pool, struct snarf_mailbox, 1);
sbox->module_ctx.super = *v;
box->vlast = &sbox->module_ctx.super;
sbox->snarf_box = mailbox_alloc(snarf_list, snarf_name,
MAILBOX_FLAG_KEEP_LOCKED);
v->sync_init = snarf_sync_init;
v->free = snarf_mailbox_free;
MODULE_CONTEXT_SET(box, snarf_storage_module, sbox);
}
static struct mailbox *
snarf_mailbox_alloc(struct mail_storage *storage,
struct mailbox_list *list,
const char *vname, enum mailbox_flags flags)
{
struct snarf_mail_storage *sstorage = SNARF_CONTEXT(storage);
struct mail_namespace *ns = mailbox_list_get_namespace(list);
struct mailbox *box;
struct mailbox_list *snarf_list;
const char *snarf_name;
struct stat st;
if (strcmp(vname, "INBOX") == 0 &&
(ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
if (stat(sstorage->snarf_path, &st) == 0)
sstorage->snarfing_disabled = FALSE;
else {
if (errno != ENOENT) {
mail_storage_set_critical(storage,
"stat(%s) failed: %m",
sstorage->snarf_path);
}
sstorage->snarfing_disabled = TRUE;
/* use the snarf box as our real INBOX */
if (snarf_box_find(storage->user, &snarf_list,
&snarf_name)) {
list = snarf_list;
vname = snarf_name;
}
}
}
box = sstorage->module_ctx.super.
mailbox_alloc(storage, list, vname, flags);
if (sstorage->snarfing_disabled) {
box->inbox_user = TRUE;
box->inbox_any = TRUE;
}
return box;
}
static void
snarf_mail_storage_create(struct mail_storage *storage, const char *path)
{
struct snarf_mail_storage *mstorage;
struct mail_storage_vfuncs *v = storage->vlast;
path = mail_user_home_expand(storage->user, path);
mstorage = p_new(storage->pool, struct snarf_mail_storage, 1);
mstorage->snarf_path = p_strdup(storage->pool, path);
mstorage->module_ctx.super = *v;
storage->vlast = &mstorage->module_ctx.super;
v->mailbox_alloc = snarf_mailbox_alloc;
MODULE_CONTEXT_SET(storage, snarf_storage_module, mstorage);
}
static void snarf_mail_storage_created(struct mail_storage *storage)
{
const char *path;
/* snarfing is optional: do it only if the path specified
by mbox_snarf exists */
path = mail_user_plugin_getenv(storage->user, "mbox_snarf");
if (path != NULL)
snarf_mail_storage_create(storage, path);
}
static struct mail_storage_hooks snarf_mail_storage_hooks = {
.mailbox_allocated = snarf_mailbox_allocated,
.mail_storage_created = snarf_mail_storage_created
};
void snarf_plugin_init(struct module *module)
{
mail_storage_hooks_add(module, &snarf_mail_storage_hooks);
}
void snarf_plugin_deinit(void)
{
mail_storage_hooks_remove(&snarf_mail_storage_hooks);
}