lazy-expunge-plugin.c revision 06c55920c133602d6347f574b4149a083935cca8
/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "str.h"
#include "hash.h"
#include "seq-range-array.h"
#include "mkdir-parents.h"
#include "mail-storage-private.h"
#include "mail-search-build.h"
#include "mailbox-list-private.h"
#include "mail-namespace.h"
#include "lazy-expunge-plugin.h"
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#define LAZY_EXPUNGE_CONTEXT(obj) \
#define LAZY_EXPUNGE_LIST_CONTEXT(obj) \
#define LAZY_EXPUNGE_USER_CONTEXT(obj) \
#define LAZY_EXPUNGE_MAIL_CONTEXT(obj) \
struct lazy_expunge_mail {
union mail_module_context module_ctx;
bool moving;
};
struct lazy_expunge_mail_user {
struct mail_namespace *lazy_ns;
const char *lazy_mailbox_vname;
const char *env;
bool copy_only_last_instance;
};
struct lazy_expunge_mailbox_list {
bool allow_rename:1;
bool internal_namespace:1;
};
struct lazy_expunge_transaction {
struct mailbox_transaction_context *dest_trans;
HASH_TABLE(const char *, void *) guids;
char *delayed_errstr;
enum mail_error delayed_error;
bool copy_only_last_instance;
};
const char *lazy_expunge_plugin_version = DOVECOT_ABI_VERSION;
static const char *
{
struct lazy_expunge_mail_user *luser =
const char *name;
return luser->lazy_mailbox_vname;
/* use the (canonical / unaliased) storage name */
/* replace hierarchy separators with destination virtual separator */
unsigned int i;
for (i = 0; name[i] != '\0'; i++) {
else
}
}
/* add expunge namespace prefix. the name is now a proper vname */
}
static struct mailbox *
const char **error_r)
{
enum mail_error error;
const char *name;
if (mailbox_open(box) == 0) {
return box;
}
if (error != MAIL_ERROR_NOTFOUND) {
mailbox_free(&box);
return NULL;
}
/* try creating and re-opening it. */
mailbox_free(&box);
return NULL;
}
if (mailbox_open(box) < 0) {
mailbox_free(&box);
return NULL;
}
return box;
}
static unsigned int
const char *guid)
{
void *refcountp;
unsigned int refcount;
1024);
}
if (refcount == 1) {
} else {
}
return refcount-1;
}
{
struct lazy_expunge_transaction *lt =
unsigned long refcount;
enum mail_error error;
if (error == MAIL_ERROR_EXPUNGED) {
/* already expunged - just ignore it */
return 0;
}
"lazy_expunge: Couldn't lookup message's refcount: %s",
errstr);
return -1;
}
if (*value == '\0') {
/* refcounts not supported by backend. assume all mails are
the last instance. */
return 1;
}
if (refcount > 1) {
/* this probably isn't the last instance of the mail, but
it's possible that the same mail was copied to this mailbox
multiple times and we're deleting more than one instance
within this transaction. in those cases each expunge will
see the same refcount, so we need to adjust the refcount
by tracking the expunged message GUIDs. */
if (error == MAIL_ERROR_EXPUNGED) {
/* already expunged - just ignore it */
return 0;
}
"lazy_expunge: Couldn't lookup message's GUID: %s", errstr);
return -1;
}
if (*value == '\0') {
/* GUIDs not supported by backend, but refcounts are?
not with our current backends. */
"lazy_expunge: Message unexpectedly has no GUID");
return -1;
}
}
}
{
struct lazy_expunge_mail_user *luser =
struct lazy_expunge_mailbox_list *llist =
/* lazy_expunge not enabled at all */
return FALSE;
}
if (llist->internal_namespace) {
/* lazy-expunge namespace */
return TRUE;
}
/* lazy-expunge mailbox */
return TRUE;
}
return FALSE;
}
struct mail_storage *storage)
{
const char *errstr;
enum mail_error error;
if (error == MAIL_ERROR_EXPUNGED) {
/* expunging failed because the mail was already expunged.
we don't want to fail because of that. */
return;
}
return;
}
{
struct lazy_expunge_mail_user *luser =
struct lazy_expunge_transaction *lt =
struct mail_save_context *save_ctx;
const char *error;
int ret;
return;
/* Clear this in case the mail is used for non-move later on. */
/* don't copy the mail if we're expunging from lazy_expunge
namespace (even if it's via a virtual mailbox) */
return;
}
return;
}
if (lt->copy_only_last_instance) {
/* we want to copy only the last instance of the mail to
lazy_expunge namespace. other instances will be expunged
immediately. */
if (moving)
ret = 0;
return;
}
if (ret == 0) {
return;
}
}
"lazy_expunge: Couldn't open expunge mailbox: "
"%s", error);
return;
}
"lazy_expunge: Couldn't sync expunge mailbox");
return;
}
}
}
{
union mailbox_module_context *mbox =
}
static struct mailbox_transaction_context *
{
struct lazy_expunge_mail_user *luser =
struct mailbox_transaction_context *t;
struct lazy_expunge_transaction *lt;
return t;
}
{
}
static int
struct mail_transaction_commit_changes *changes_r)
{
int ret;
}
}
lt->delayed_errstr);
ret = -1;
} else {
ret = -1;
}
return ret;
}
static void
{
}
{
struct lazy_expunge_transaction *lt =
struct lazy_expunge_mail *mmail;
return;
}
static int
{
struct lazy_expunge_mailbox_list *src_llist =
struct lazy_expunge_mailbox_list *dest_llist =
if (!src_llist->allow_rename &&
return -1;
}
}
{
struct lazy_expunge_mailbox_list *llist =
union mailbox_module_context *mbox;
return;
if (!lazy_expunge_is_internal_mailbox(box)) {
v->copy = lazy_expunge_copy;
} else if (llist->internal_namespace) {
} else {
/* internal mailbox in a non-internal namespace -
don't add any unnecessary restrictions to it. if it's not
wanted, just use the ACL plugin. */
}
}
{
struct lazy_expunge_mail_user *luser =
struct lazy_expunge_mailbox_list *llist;
return;
/* if this is one of our internal namespaces, mark it as such before
quota plugin sees it */
llist);
}
}
static void
{
struct lazy_expunge_mail_user *luser =
struct lazy_expunge_mailbox_list *llist;
return;
/* we don't want to override this namespace's expunge operation. */
} else {
/* store the the expunged mails to the specified mailbox. */
}
}
{
/* mail_namespaces_created hook isn't necessarily ever called */
}
{
struct lazy_expunge_mail_user *luser;
const char *env;
} else if (user->mail_debug) {
i_debug("lazy_expunge: No lazy_expunge setting - "
"plugin disabled");
}
}
static struct mail_storage_hooks lazy_expunge_mail_storage_hooks = {
};
{
}
void lazy_expunge_plugin_deinit(void)
{
}