doveadm-mail-expunge.c revision b66d803de86bfb411165b3465b0d9ef64ecfe2a1
148N/A/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
148N/A
148N/A#include "lib.h"
148N/A#include "array.h"
148N/A#include "mail-index.h"
148N/A#include "mail-storage.h"
148N/A#include "mail-search.h"
148N/A#include "doveadm-mailbox-list-iter.h"
148N/A#include "doveadm-mail-iter.h"
148N/A#include "doveadm-mail.h"
148N/A
148N/Astruct expunge_cmd_context {
148N/A struct doveadm_mail_cmd_context ctx;
148N/A bool delete_empty_mailbox;
148N/A};
148N/A
148N/Astatic int
148N/Acmd_expunge_box(struct doveadm_mail_cmd_context *_ctx,
148N/A const struct mailbox_info *info,
148N/A struct mail_search_args *search_args)
148N/A{
148N/A struct expunge_cmd_context *ctx = (struct expunge_cmd_context *)_ctx;
6463N/A struct doveadm_mail_iter *iter;
148N/A struct mailbox *box;
148N/A struct mailbox_transaction_context *trans;
148N/A struct mail *mail;
148N/A enum mail_error error;
148N/A int ret = 0;
6463N/A
618N/A if (doveadm_mail_iter_init(_ctx, info, search_args, 0, NULL,
148N/A &trans, &iter) < 0)
148N/A return -1;
844N/A
6463N/A while (doveadm_mail_iter_next(iter, &mail)) {
148N/A if (doveadm_debug) {
1273N/A i_debug("expunge: box=%s uid=%u",
148N/A info->name, mail->uid);
1002N/A }
1316N/A mail_expunge(mail);
1002N/A }
1316N/A
1002N/A if (doveadm_mail_iter_deinit_keep_box(&iter, &box) < 0)
1002N/A ret = -1;
6463N/A else if (mailbox_sync(box, 0) < 0) {
3510N/A doveadm_mail_failed_mailbox(_ctx, box);
3996N/A ret = -1;
3996N/A }
3996N/A
3996N/A if (ctx->delete_empty_mailbox && ret == 0) {
148N/A if (mailbox_delete_empty(box) < 0) {
5252N/A error = mailbox_get_last_mail_error(box);
5252N/A if (error != MAIL_ERROR_EXISTS) {
148N/A doveadm_mail_failed_mailbox(_ctx, box);
148N/A ret = -1;
148N/A }
3985N/A } else {
3985N/A if (mailbox_set_subscribed(box, FALSE) < 0) {
3985N/A doveadm_mail_failed_mailbox(_ctx, box);
148N/A ret = -1;
148N/A }
148N/A }
148N/A }
3510N/A mailbox_free(&box);
1316N/A return ret;
1316N/A}
148N/A
148N/Astatic bool
148N/Aexpunge_search_args_is_mailbox_ok(struct mail_search_arg *args);
690N/A
148N/Astatic bool
3781N/Aexpunge_search_args_is_mailbox_or_ok(struct mail_search_arg *args)
3781N/A{
3781N/A struct mail_search_arg *arg;
3781N/A
3781N/A for (arg = args; arg != NULL; arg = arg->next) {
3781N/A switch (arg->type) {
3781N/A case SEARCH_OR:
3781N/A if (!expunge_search_args_is_mailbox_or_ok(arg->value.subargs))
3781N/A return FALSE;
3781N/A break;
3781N/A case SEARCH_SUB:
3783N/A case SEARCH_INTHREAD:
3783N/A if (!expunge_search_args_is_mailbox_ok(arg->value.subargs))
3781N/A return FALSE;
3781N/A break;
3781N/A case SEARCH_MAILBOX:
3781N/A case SEARCH_MAILBOX_GUID:
3783N/A case SEARCH_MAILBOX_GLOB:
3783N/A break;
3783N/A default:
3783N/A return FALSE;
3781N/A }
3781N/A }
727N/A return TRUE;
727N/A}
3781N/A
727N/Astatic bool
3781N/Aexpunge_search_args_is_mailbox_ok(struct mail_search_arg *args)
3781N/A{
3781N/A struct mail_search_arg *arg;
727N/A bool have_or = FALSE;
3781N/A
1822N/A /* a) we find one mailbox here in the SUB block */
3781N/A for (arg = args; arg != NULL; arg = arg->next) {
3781N/A switch (arg->type) {
3781N/A case SEARCH_MAILBOX:
1822N/A case SEARCH_MAILBOX_GUID:
1822N/A case SEARCH_MAILBOX_GLOB:
1822N/A return TRUE;
1822N/A case SEARCH_OR:
1822N/A have_or = TRUE;
3781N/A break;
3781N/A case SEARCH_SUB:
3781N/A case SEARCH_INTHREAD:
1822N/A if (expunge_search_args_is_mailbox_ok(arg->value.subargs))
3781N/A return TRUE;
3781N/A break;
1822N/A default:
1822N/A break;
1822N/A }
727N/A }
148N/A
5252N/A /* b) there is at least one OR block, and all of the ORs must have
5252N/A mailbox */
148N/A if (!have_or)
148N/A return FALSE;
148N/A
560N/A for (arg = args; arg != NULL; arg = arg->next) {
727N/A if (arg->type == SEARCH_OR &&
3781N/A !expunge_search_args_is_mailbox_or_ok(arg->value.subargs))
148N/A return FALSE;
181N/A }
148N/A return TRUE;
148N/A}
3996N/A
3996N/Astatic bool
3996N/Aexpunge_search_args_is_msgset_ok(struct mail_search_arg *args);
3996N/A
3996N/Astatic bool
3996N/Aexpunge_search_args_is_msgset_or_ok(struct mail_search_arg *args)
3996N/A{
3996N/A struct mail_search_arg *arg;
3996N/A
/* we're done if all OR branches contain something else besides
MAILBOXes */
for (arg = args; arg != NULL; arg = arg->next) {
switch (arg->type) {
case SEARCH_MAILBOX:
case SEARCH_MAILBOX_GUID:
case SEARCH_MAILBOX_GLOB:
return FALSE;
case SEARCH_OR:
if (!expunge_search_args_is_msgset_or_ok(arg->value.subargs))
return FALSE;
break;
case SEARCH_SUB:
if (!expunge_search_args_is_msgset_ok(arg->value.subargs))
return FALSE;
break;
default:
break;
}
}
return TRUE;
}
static bool
expunge_search_args_is_msgset_ok(struct mail_search_arg *args)
{
struct mail_search_arg *arg;
/* all args can't be just MAILBOXes */
for (arg = args; arg != NULL; arg = arg->next) {
switch (arg->type) {
case SEARCH_MAILBOX:
case SEARCH_MAILBOX_GUID:
case SEARCH_MAILBOX_GLOB:
break;
case SEARCH_OR:
/* if each OR branch has something else than just
MAILBOXes, we're ok */
if (expunge_search_args_is_msgset_or_ok(arg->value.subargs))
return TRUE;
break;
case SEARCH_SUB:
if (expunge_search_args_is_msgset_ok(arg->value.subargs))
return TRUE;
break;
default:
return TRUE;
}
}
return FALSE;
}
static int
cmd_expunge_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user)
{
const enum mailbox_list_iter_flags iter_flags =
MAILBOX_LIST_ITER_RAW_LIST |
MAILBOX_LIST_ITER_NO_AUTO_BOXES |
MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
struct doveadm_mailbox_list_iter *iter;
const struct mailbox_info *info;
int ret = 0;
iter = doveadm_mailbox_list_iter_init(ctx, user, ctx->search_args,
iter_flags);
while ((info = doveadm_mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
if (cmd_expunge_box(ctx, info, ctx->search_args) < 0)
ret = -1;
} T_END;
if (doveadm_mailbox_list_iter_deinit(&iter) < 0)
ret = -1;
return ret;
}
void expunge_search_args_check(struct mail_search_args *args, const char *cmd)
{
mail_search_args_simplify(args);
if (!expunge_search_args_is_mailbox_ok(args->args)) {
i_fatal_status(EX_USAGE,
"%s: To avoid accidents, search query "
"must contain MAILBOX in all search branches", cmd);
}
if (!expunge_search_args_is_msgset_ok(args->args)) {
i_fatal_status(EX_USAGE,
"%s: To avoid accidents, each branch in search query "
"must contain something else besides MAILBOX", cmd);
}
}
static void cmd_expunge_init(struct doveadm_mail_cmd_context *ctx,
const char *const args[])
{
if (args[0] == NULL)
doveadm_mail_help_name("expunge");
ctx->search_args = doveadm_mail_build_search_args(args);
expunge_search_args_check(ctx->search_args, "expunge");
}
static bool cmd_expunge_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c)
{
struct expunge_cmd_context *ctx = (struct expunge_cmd_context *)_ctx;
switch (c) {
case 'd':
ctx->delete_empty_mailbox = TRUE;
break;
default:
return FALSE;
}
return TRUE;
}
static struct doveadm_mail_cmd_context *cmd_expunge_alloc(void)
{
struct expunge_cmd_context *ctx;
ctx = doveadm_mail_cmd_alloc(struct expunge_cmd_context);
ctx->ctx.getopt_args = "d";
ctx->ctx.v.parse_arg = cmd_expunge_parse_arg;
ctx->ctx.v.init = cmd_expunge_init;
ctx->ctx.v.run = cmd_expunge_run;
return &ctx->ctx;
}
struct doveadm_mail_cmd cmd_expunge = {
cmd_expunge_alloc, "expunge", "[-d] <search query>"
};