mail-storage.c revision 015523cce59f1d1ca5bc16cf0d090b4a5c115f6d
/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "llist.h"
#include "unichar.h"
#include "istream.h"
#include "eacces-error.h"
#include "mkdir-parents.h"
#include "var-expand.h"
#include "mail-index-private.h"
#include "mail-index-alloc-cache.h"
#include "mailbox-list-private.h"
#include "mail-storage-private.h"
#include "mail-storage-settings.h"
#include "mail-namespace.h"
#include "mail-search.h"
#include "mail-search-register.h"
#include "mailbox-search-result-private.h"
#include "mailbox-guid-cache.h"
#include <stdlib.h>
#include <ctype.h>
extern struct mail_search_register *mail_search_register_imap;
extern struct mail_search_register *mail_search_register_human;
struct mail_storage_module_register mail_storage_module_register = { 0 };
struct mail_module_register mail_module_register = { 0 };
void mail_storage_init(void)
{
}
void mail_storage_deinit(void)
{
if (mail_search_register_human != NULL)
if (mail_search_register_imap != NULL)
}
{
/* append it after the list, so the autodetection order is correct */
}
{
struct mail_storage *const *classes;
unsigned int i, count;
for (i = 0; i < count; i++) {
if (classes[i] == storage_class) {
break;
}
}
}
{
struct mail_storage *const *classes;
unsigned int i, count;
for (i = 0; i < count; i++) {
return classes[i];
}
return NULL;
}
static struct mail_storage *
struct mailbox_list_settings *set)
{
struct mail_storage *const *classes;
unsigned int i, count;
for (i = 0; i < count; i++) {
return classes[i];
}
}
return NULL;
}
static void
{
const char *p;
/* check if data is in driver:data format (eg. mbox:~/mail) */
p = *data;
while (i_isalnum(*p)) p++;
if (*p == ':' && p != *data) {
/* no autodetection if the storage driver is given. */
*data = p + 1;
}
}
static struct mail_storage *
struct mailbox_list_settings *list_set,
{
const char *home;
if (storage_class == NULL) {
"Unknown mail storage driver %s", driver);
return NULL;
}
}
/* no root directory given. is this allowed? */
const struct mailbox_list *list;
if (storage_class == NULL &&
(flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) == 0) {
/* autodetection should take care of this */
} else if (storage_class != NULL &&
/* root not required for this storage */
/* root not required for this layout */
} else {
*error_r = "Root mail directory not given";
return NULL;
}
}
if (storage_class != NULL) {
return storage_class;
}
if (storage_class != NULL)
return storage_class;
"Mail storage autodetection failed with home=%s", home);
} else {
"Ambiguous mail location setting, "
"don't know what to do with it: %s "
"(try prefixing it with mbox: or maildir:)",
}
return NULL;
}
static int
const char **error_r)
{
/* exists */
return 1;
return -1;
return -1;
} else if (!autocreate) {
"Root mail directory doesn't exist: %s", root_dir);
return -1;
} else {
/* doesn't exist */
return 0;
}
}
static int
{
bool autocreate;
int ret;
/* storage doesn't use directories (e.g. shared root) */
return 0;
}
if ((flags & MAIL_STORAGE_FLAG_NO_AUTOVERIFY) != 0) {
return 0;
/* we don't need to verify, but since debugging is
enabled, check and log if the root doesn't exist */
i_debug("Namespace %s: Creating storage despite: %s",
}
return 0;
}
if (ret != 0)
return ret;
/* we need to create the root directory. */
return -1;
} else {
/* created */
return 0;
}
}
static struct mail_storage *
const struct mail_storage *storage_class,
const struct mailbox_list_settings *set)
{
((storage->class_flags &
MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) == 0 ||
return storage;
}
return NULL;
}
{
struct mailbox_list *list;
struct mailbox_list_settings list_set;
enum mailbox_list_flags list_flags = 0;
const char *p;
if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 &&
/* if pop3_uidl_format contains %m, we want to keep the
header MD5 sums stored even if we're not running POP3
right now. */
if (p[1] == '%')
p += 2;
else if (var_get_key(++p) == 'm') {
break;
}
}
}
/* autodetect */
/* internal shared namespace */
} else {
error_r) < 0)
return -1;
}
error_r);
if (storage_class == NULL)
return -1;
/* first storage for namespace */
return -1;
}
return -1;
}
/* using an existing storage */
return 0;
}
return -1;
}
T_BEGIN {
} T_END;
return 0;
}
{
/* set *_storage=NULL only after calling destroy() callback.
for example mdbox wants to access ns->storage */
return;
}
if (storage->obj_refcount != 0)
i_panic("Trying to deinit storage before freeing its objects");
}
{
storage->obj_refcount++;
}
{
storage->obj_refcount--;
}
{
}
{
}
{
char str[256];
MAIL_ERRSTR_CRITICAL_MSG_STAMP, tm) > 0 ?
}
const char *fmt, ...)
{
/* critical errors may contain sensitive data, so let user
see only "Internal error" with a timestamp to make it
easier to look from log files the actual error message. */
}
}
struct mailbox_list *list)
{
const char *str;
enum mail_error error;
}
{
else
}
const struct mail_storage_settings *
{
}
{
}
struct mail_storage_callbacks *callbacks,
void *context)
{
}
{
}
enum mail_error *error_r)
{
/* We get here only in error situations, so we have to return some
error. If storage->error is NONE, it means we forgot to set it at
some point.. */
"BUG: Unknown internal error";
}
/* This shouldn't happen.. */
i_strdup_printf("BUG: Unknown 0x%x error",
}
return storage->error_string;
}
enum mail_error *error_r)
{
}
{
enum mail_error error;
return error;
}
{
return (storage->class_flags &
}
{
const char *error_string;
enum mail_error error;
return FALSE;
/* debugging is enabled - admin may be debugging a
(permission) problem, so return FALSE to get the caller to
log the full error message. */
return FALSE;
}
return TRUE;
}
enum mailbox_flags flags)
{
struct mail_storage *storage;
/* make sure INBOX shows up in uppercase everywhere */
vname = "INBOX";
}
/* just use the first storage. FIXME: does this break? */
}
T_BEGIN {
} T_END;
return box;
}
enum mailbox_flags flags)
{
struct mailbox_metadata metadata;
const char *vname;
&metadata) < 0) {
/* GUID mismatch, refresh cache and try again */
mailbox_free(&box);
} else {
/* successfully opened the correct mailbox */
return box;
}
i_error("mailbox_alloc_guid(%s): "
"Couldn't verify mailbox GUID: %s",
mailbox_free(&box);
} else {
}
}
return box;
}
const char *name)
{
NAMESPACE_FLAG_LIST_CHILDREN)) == 0)
continue;
continue;
/* if prefix has multiple hierarchies, match
any of the hierarchies */
return TRUE;
}
return FALSE;
}
enum mailbox_existence *existence_r)
{
switch (box->open_error) {
case 0:
break;
case MAIL_ERROR_NOTFOUND:
return 0;
default:
/* unsure if this exists or not */
return -1;
}
/* report it as not selectable, since it exists but we won't
let it be opened. */
return 0;
}
if (!box->inbox_user &&
/* listable namespace prefix always exists */
return 0;
}
}
{
if (box->inbox_user) {
/* this is INBOX - don't bother with further checks */
return 0;
}
if (ns->prefix_len > 0) {
if (vname[0] != '\0') {
vname++;
if (vname[0] == '\0') {
/* "namespace/" isn't a valid mailbox name. */
"Invalid mailbox name");
return -1;
}
}
}
return 0;
for (p = vname; *p != '\0'; p++) {
if (*p == list_sep) {
t_strdup_printf("Character not allowed "
"in mailbox name: '%c'",
list_sep));
return -1;
}
}
return 0;
}
{
int ret;
return 0;
switch (box->open_error) {
case 0:
break;
case MAIL_ERROR_NOTFOUND:
return -1;
default:
return -1;
}
if (mailbox_check_mismatching_separators(box) < 0)
return -1;
"Invalid mailbox name");
return -1;
}
MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS) == 0) {
"Storage doesn't support streamed mailboxes");
return -1;
}
}
T_BEGIN {
} T_END;
/* INBOX should always exist. try to create it and retry. */
i_error("Opening INBOX failed: %s",
}
} T_END;
if (ret < 0) {
return -1;
}
return 0;
}
{
}
{
}
{
}
{
return box->enabled_features;
}
{
return;
if (box->transaction_count != 0) {
i_panic("Trying to close mailbox %s with open transactions",
}
}
{
}
bool directory)
{
enum mailbox_dir_create_type type;
int ret;
"Invalid mailbox name");
return -1;
}
if (directory ||
return -1;
/* the directory already exists, but the mailbox might not */
}
return ret;
}
{
update->min_first_recent_uid == 0 ||
}
{
struct mail_index_transaction *trans;
enum mail_index_transaction_flags trans_flags = 0;
enum mailbox_flags old_flag;
int ret;
/* we already marked it deleted. this allows plugins to
"lock" the deletion earlier. */
return 0;
}
if (ret < 0)
return -1;
if (del)
else
if (mail_index_transaction_commit(&trans) < 0) {
return -1;
}
/* sync the mailbox. this finishes the index deletion and it can
succeed only for a single session. we do it here, so the rest of
the deletion code doesn't have to worry about race conditions. */
return -1;
return 0;
}
{
return FALSE;
return FALSE;
return FALSE;
return TRUE;
}
{
int ret;
"Storage root can't be deleted");
return -1;
}
"INBOX can't be deleted.");
return -1;
}
if (mailbox_open(box) < 0) {
return -1;
if (!box->mailbox_deleted) {
/* \noselect mailbox */
} else {
/* if deletion happened a long time ago, it means it
crashed while doing it. undelete the mailbox in
that case. */
if (!mailbox_try_undelete(box))
return -1;
/* retry */
if (mailbox_open(box) < 0)
return -1;
}
}
/* deletion failed. revert the mark so it can maybe be
tried again later. */
return -1;
}
return ret;
}
static bool
struct mail_storage *storage2)
{
return TRUE;
return FALSE;
return FALSE;
return TRUE;
}
{
}
static bool
struct mailbox_list *list2)
{
}
bool rename_children)
{
"Invalid mailbox name");
return -1;
}
"Can't rename mailboxes across specified storages.");
return -1;
}
"Renaming not supported across non-private namespaces.");
return -1;
}
}
{
struct mail_namespace *ns;
const char *subs_name;
"Invalid mailbox name");
return -1;
}
else {
/* subscriptions=no namespace, find another one where we can
add the subscription to */
"This namespace has no subscriptions");
return -1;
}
/* use <orig ns prefix><orig storage name> as the
subscription name */
/* drop the common prefix (typically there isn't one) */
}
}
{
}
struct mail_namespace *
{
}
{
}
{
}
{
}
{
}
{
return FALSE;
}
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
}
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
i_unreached();
}
struct mailbox_metadata *metadata_r)
{
if (mailbox_open(box) < 0)
return -1;
}
return -1;
return 0;
}
{
return 0;
else
}
struct mailbox_sync_context *
{
struct mailbox_sync_context *ctx;
if (box->transaction_count != 0) {
i_panic("Trying to sync mailbox %s with open transactions",
}
T_BEGIN {
} T_END;
return ctx;
}
struct mailbox_sync_rec *sync_rec_r)
{
}
struct mailbox_sync_status *status_r)
{
const char *errormsg;
enum mail_error error;
int ret;
if (error == MAIL_ERROR_NOTPOSSIBLE) {
}
}
return ret;
}
{
struct mailbox_sync_context *ctx;
struct mailbox_sync_status status;
/* we don't care about mailbox's current state, so we might
as well fix inconsistency state */
}
}
{
}
{
}
struct mail_search_context *
mailbox_search_init(struct mailbox_transaction_context *t,
struct mail_search_args *args,
const enum mail_sort_type *sort_program,
struct mailbox_header_lookup_ctx *wanted_headers)
{
if (!args->simplified)
}
{
int ret;
return ret;
}
{
bool tryagain;
if (!tryagain)
return FALSE;
}
return TRUE;
}
{
return FALSE;
else {
return TRUE;
}
}
{
return ctx->seen_lost_data;
}
int mailbox_search_result_build(struct mailbox_transaction_context *t,
struct mail_search_args *args,
struct mail_search_result **result_r)
{
struct mail_search_context *ctx;
int ret;
if (ret < 0)
return ret;
}
struct mailbox_transaction_context *
{
struct mailbox_transaction_context *trans;
box->transaction_count++;
return trans;
}
int mailbox_transaction_commit(struct mailbox_transaction_context **t)
{
int ret;
/* Store changes temporarily so that plugins overriding
transaction_commit() can look at them. */
return ret;
}
struct mailbox_transaction_context **_t,
struct mail_transaction_commit_changes *changes_r)
{
struct mailbox_transaction_context *t = *_t;
int ret;
t->box->transaction_count--;
T_BEGIN {
} T_END;
return ret;
}
{
struct mailbox_transaction_context *t = *_t;
t->box->transaction_count--;
t->box->v.transaction_rollback(t);
}
{
return box->transaction_count;
}
void mailbox_transaction_set_max_modseq(struct mailbox_transaction_context *t,
{
}
struct mailbox *
mailbox_transaction_get_mailbox(const struct mailbox_transaction_context *t)
{
return t->box;
}
struct mail_save_context *
mailbox_save_alloc(struct mailbox_transaction_context *t)
{
struct mail_save_context *ctx;
return ctx;
}
enum mail_flags flags,
struct mail_keywords *keywords)
{
}
{
const char *const *keywords_list;
}
{
}
{
}
{
}
const char *envelope)
{
}
{
}
{
}
{
}
{
}
{
int ret;
return -1;
}
"Saving messages not supported");
ret = -1;
} else {
}
if (ret < 0) {
return -1;
}
return 0;
}
{
}
{
int ret;
return ret;
}
{
}
struct mailbox_transaction_context *
{
return ctx->transaction;
}
{
int ret;
return -1;
}
return ret;
}
{
}
{
"Mailbox was deleted under us");
}
{
const char *path;
}
}
{
return;
return;
}
}
{
}
}
{
(void)mailbox_get_permissions(box);
}
int *fd_r)
{
int fd;
*fd_r = -1;
if (fd != -1) {
/* ok */
/* O_EXCL used, caller will handle this error */
return 0;
return -1;
"Mailbox doesn't allow inferior mailboxes");
return -1;
return -1;
} else {
"open(%s, O_CREAT) failed: %m", path);
return -1;
}
/* ok */
} else {
"fchown(%s) failed: %m", path);
}
}
return 1;
}
unsigned int secs)
{
}