mail-storage.c revision 868cbbd9f1bbc91a0005217bcd13e218d08bf24f
10139N/A/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
10139N/A
10139N/A#include "lib.h"
18537N/A#include "ioloop.h"
10139N/A#include "array.h"
10139N/A#include "str.h"
10139N/A#include "var-expand.h"
20307N/A#include "mail-index-private.h"
10139N/A#include "mailbox-list-private.h"
17180N/A#include "mail-storage-private.h"
18603N/A#include "mail-namespace.h"
17180N/A#include "mail-search.h"
15153N/A
10139N/A#include <stdlib.h>
15308N/A#include <time.h>
10139N/A#include <ctype.h>
20332N/A
20332N/A/* Message to show to users when critical error occurs */
10139N/A#define CRITICAL_MSG \
18616N/A "Internal error occurred. Refer to server log for more information."
10139N/A#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
20332N/A
12773N/A#define DEFAULT_MAX_KEYWORD_LENGTH 50
15153N/A
15153N/Astruct mail_storage_module_register mail_storage_module_register = { 0 };
12773N/Astruct mail_module_register mail_module_register = { 0 };
13633N/A
13992N/Astruct mail_storage_mail_index_module mail_storage_mail_index_module =
15555N/A MODULE_CONTEXT_INIT(&mail_index_module_register);
15428N/A
20307N/Avoid (*hook_mail_storage_created)(struct mail_storage *storage);
18719N/Avoid (*hook_mailbox_opened)(struct mailbox *box) = NULL;
20196N/A
20196N/Astatic ARRAY_DEFINE(storages, struct mail_storage *);
10139N/A
10139N/Avoid mail_storage_init(void)
10139N/A{
10139N/A mailbox_lists_init();
10139N/A i_array_init(&storages, 8);
10139N/A}
10139N/A
10139N/Avoid mail_storage_deinit(void)
10139N/A{
10139N/A if (array_is_created(&storages))
10139N/A array_free(&storages);
10139N/A mailbox_lists_deinit();
10139N/A}
10139N/A
10139N/Avoid mail_storage_class_register(struct mail_storage *storage_class)
10139N/A{
10139N/A if (storage_class->v.class_init != NULL)
10139N/A storage_class->v.class_init();
10139N/A
10139N/A /* append it after the list, so the autodetection order is correct */
10139N/A array_append(&storages, &storage_class, 1);
10139N/A}
10139N/A
10139N/Avoid mail_storage_class_unregister(struct mail_storage *storage_class)
10139N/A{
10139N/A struct mail_storage *const *classes;
10139N/A unsigned int i, count;
10139N/A
15153N/A classes = array_get(&storages, &count);
15153N/A for (i = 0; i < count; i++) {
15153N/A if (classes[i] == storage_class) {
15153N/A array_delete(&storages, i, 1);
15153N/A break;
13551N/A }
15428N/A }
20332N/A
20196N/A storage_class->v.class_deinit();
10139N/A}
10139N/A
10139N/Avoid mail_storage_parse_env(enum mail_storage_flags *flags_r,
10139N/A enum file_lock_method *lock_method_r)
10139N/A{
10139N/A const char *str;
10139N/A
10139N/A *flags_r = 0;
10139N/A if (getenv("FULL_FILESYSTEM_ACCESS") != NULL)
10139N/A *flags_r |= MAIL_STORAGE_FLAG_FULL_FS_ACCESS;
10139N/A if (getenv("DEBUG") != NULL)
10139N/A *flags_r |= MAIL_STORAGE_FLAG_DEBUG;
10139N/A if (getenv("MMAP_DISABLE") != NULL)
10139N/A *flags_r |= MAIL_STORAGE_FLAG_MMAP_DISABLE;
10139N/A if (getenv("MMAP_NO_WRITE") != NULL)
12773N/A *flags_r |= MAIL_STORAGE_FLAG_MMAP_NO_WRITE;
12773N/A if (getenv("DOTLOCK_USE_EXCL") != NULL)
12773N/A *flags_r |= MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL;
12773N/A if (getenv("MAIL_SAVE_CRLF") != NULL)
12773N/A *flags_r |= MAIL_STORAGE_FLAG_SAVE_CRLF;
15724N/A if (getenv("FSYNC_DISABLE") != NULL)
15724N/A *flags_r |= MAIL_STORAGE_FLAG_FSYNC_DISABLE;
15724N/A if (getenv("MAIL_NFS_STORAGE") != NULL)
15724N/A *flags_r |= MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE;
15724N/A if (getenv("MAIL_NFS_INDEX") != NULL) {
15724N/A *flags_r |= MAIL_STORAGE_FLAG_NFS_FLUSH_INDEX;
15724N/A if ((*flags_r & MAIL_STORAGE_FLAG_MMAP_DISABLE) == 0)
15724N/A i_fatal("mail_nfs_index=yes requires mmap_disable=yes");
10826N/A if ((*flags_r & MAIL_STORAGE_FLAG_FSYNC_DISABLE) != 0)
15724N/A i_fatal("mail_nfs_index=yes requires fsync_disable=no");
10826N/A }
15724N/A
10139N/A str = getenv("POP3_UIDL_FORMAT");
10139N/A if (str != NULL && (str = strchr(str, '%')) != NULL &&
10139N/A str != NULL && var_get_key(str + 1) == 'm')
10139N/A *flags_r |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;
10139N/A
10139N/A str = getenv("LOCK_METHOD");
10139N/A if (str == NULL || strcmp(str, "fcntl") == 0)
10139N/A *lock_method_r = FILE_LOCK_METHOD_FCNTL;
10139N/A else if (strcmp(str, "flock") == 0)
10139N/A *lock_method_r = FILE_LOCK_METHOD_FLOCK;
10139N/A else if (strcmp(str, "dotlock") == 0)
10139N/A *lock_method_r = FILE_LOCK_METHOD_DOTLOCK;
10139N/A else
10139N/A i_fatal("Unknown lock_method: %s", str);
10139N/A}
10139N/A
10139N/Astatic struct mail_storage *mail_storage_find(const char *name)
10139N/A{
10139N/A struct mail_storage *const *classes;
10139N/A unsigned int i, count;
10139N/A
10139N/A i_assert(name != NULL);
10139N/A
10139N/A classes = array_get(&storages, &count);
10139N/A for (i = 0; i < count; i++) {
10139N/A if (strcasecmp(classes[i]->name, name) == 0)
10139N/A return classes[i];
10139N/A }
10914N/A return NULL;
10139N/A}
10139N/A
10139N/Astatic struct mail_storage *
10139N/Amail_storage_autodetect(const char *data, enum mail_storage_flags flags)
10139N/A{
20332N/A struct mail_storage *const *classes;
20332N/A unsigned int i, count;
20196N/A
20196N/A classes = array_get(&storages, &count);
18719N/A for (i = 0; i < count; i++) {
18719N/A if (classes[i]->v.autodetect != NULL &&
18537N/A classes[i]->v.autodetect(data, flags))
18537N/A return classes[i];
17565N/A }
17565N/A return NULL;
17357N/A}
17357N/A
17241N/Astatic void
17241N/Amail_storage_set_autodetection(const char **data, const char **driver,
17107N/A enum mail_storage_flags *flags)
17107N/A{
16995N/A const char *p;
16995N/A
16838N/A /* check if data is in driver:data format (eg. mbox:~/mail) */
16838N/A p = *data;
16533N/A while (i_isalnum(*p)) p++;
16533N/A
16362N/A if (*p == ':' && p != *data) {
16362N/A /* no autodetection if the storage format is given. */
16160N/A *flags |= MAIL_STORAGE_FLAG_NO_AUTODETECTION;
16160N/A
16023N/A *driver = t_strdup_until(*data, p);
16023N/A *data = p + 1;
15857N/A }
15857N/A}
15793N/A
15793N/Aint mail_storage_create(struct mail_namespace *ns, const char *driver,
15793N/A const char *data, const char *user,
15724N/A enum mail_storage_flags flags,
15724N/A enum file_lock_method lock_method,
15710N/A const char **error_r)
15710N/A{
15710N/A struct mail_storage *storage_class, *storage;
15710N/A struct mail_storage *const *classes;
15596N/A const char *home, *value;
15596N/A unsigned int i, count;
15555N/A
15555N/A if (data == NULL)
15555N/A data = "";
15428N/A else if (driver == NULL)
15428N/A mail_storage_set_autodetection(&data, &driver, &flags);
15231N/A
15231N/A if (*data == '\0' && driver == NULL) {
15153N/A /* use the first driver that works */
15153N/A classes = array_get(&storages, &count);
14480N/A } else if (driver == NULL) {
14480N/A storage_class = mail_storage_autodetect(data, flags);
14189N/A if (storage_class == NULL) {
14189N/A *error_r = t_strdup_printf(
13992N/A "Ambiguous mail location setting, "
13992N/A "don't know what to do with it: %s "
13929N/A "(try prefixing it with mbox: or maildir:)",
13929N/A data);
13905N/A return -1;
13905N/A }
13905N/A classes = &storage_class;
13807N/A count = 1;
13807N/A } else {
13807N/A storage_class = mail_storage_find(driver);
13633N/A if (storage_class == NULL) {
13633N/A *error_r = t_strdup_printf(
13633N/A "Unknown mail storage driver %s", driver);
13633N/A return -1;
13551N/A }
13551N/A classes = &storage_class;
13551N/A count = 1;
13101N/A }
13101N/A
13101N/A for (i = 0; i < count; i++) {
12571N/A storage = classes[i]->v.alloc();
12571N/A storage->flags = flags;
12571N/A storage->lock_method = lock_method;
12466N/A storage->user = p_strdup(storage->pool, user);
12466N/A storage->ns = ns;
12466N/A
12363N/A storage->callbacks =
12363N/A p_new(storage->pool, struct mail_storage_callbacks, 1);
12363N/A p_array_init(&storage->module_contexts, storage->pool, 5);
12287N/A
12287N/A if (classes[i]->v.create(storage, data, error_r) == 0)
12287N/A break;
12022N/A
12022N/A if ((flags & MAIL_STORAGE_FLAG_DEBUG) != 0 && count > 1) {
12022N/A i_info("%s: Couldn't create mail storage %s: %s",
11915N/A classes[i]->name, data, *error_r);
11915N/A }
11915N/A
11840N/A /* try the next one */
11840N/A pool_unref(&storage->pool);
11840N/A }
11819N/A if (i == count) {
11819N/A if (count <= 1) {
11819N/A *error_r = t_strdup_printf("%s: %s", classes[0]->name,
11416N/A *error_r);
11416N/A return -1;
11416N/A }
11265N/A
11265N/A home = getenv("HOME");
11265N/A if (home == NULL || *home == '\0') home = "(not set)";
11162N/A
11162N/A *error_r = t_strdup_printf(
11162N/A "Mail storage autodetection failed with home=%s", home);
11070N/A return -1;
11070N/A }
11070N/A
10914N/A value = getenv("MAIL_MAX_KEYWORD_LENGTH");
10914N/A storage->keyword_max_len = value != NULL ?
10914N/A atoi(value) : DEFAULT_MAX_KEYWORD_LENGTH;
10914N/A
10909N/A if (hook_mail_storage_created != NULL) {
10909N/A T_BEGIN {
10909N/A hook_mail_storage_created(storage);
10824N/A } T_END;
10824N/A }
10824N/A
10506N/A ns->storage = storage;
10506N/A mail_namespace_init_storage(ns);
10507N/A return 0;
10506N/A}
10499N/A
10499N/Avoid mail_storage_destroy(struct mail_storage **_storage)
10499N/A{
10449N/A struct mail_storage *storage = *_storage;
10450N/A
10449N/A i_assert(storage != NULL);
10449N/A
10313N/A *_storage = NULL;
10313N/A
10313N/A storage->v.destroy(storage);
10313N/A mailbox_list_deinit(storage->list);
10276N/A i_free(storage->error_string);
10276N/A pool_unref(&storage->pool);
10276N/A}
10264N/A
10264N/Avoid mail_storage_clear_error(struct mail_storage *storage)
10264N/A{
10139N/A i_free_and_null(storage->error_string);
10139N/A
10139N/A storage->error = MAIL_ERROR_NONE;
10139N/A}
10139N/A
10139N/Avoid mail_storage_set_error(struct mail_storage *storage,
10139N/A enum mail_error error, const char *string)
10139N/A{
10139N/A i_free(storage->error_string);
10139N/A storage->error_string = i_strdup(string);
10139N/A storage->error = error;
10139N/A}
10139N/A
10139N/Avoid mail_storage_set_internal_error(struct mail_storage *storage)
10139N/A{
10139N/A struct tm *tm;
10139N/A char str[256];
10139N/A
10139N/A tm = localtime(&ioloop_time);
10139N/A
10139N/A i_free(storage->error_string);
10139N/A storage->error_string =
10139N/A strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
10139N/A i_strdup(str) : i_strdup(CRITICAL_MSG);
10139N/A storage->error = MAIL_ERROR_TEMP;
10139N/A}
10139N/A
10139N/Avoid mail_storage_set_critical(struct mail_storage *storage,
10139N/A const char *fmt, ...)
10139N/A{
10139N/A va_list va;
10139N/A
10139N/A mail_storage_clear_error(storage);
10139N/A if (fmt != NULL) {
10139N/A va_start(va, fmt);
10139N/A i_error("%s", t_strdup_vprintf(fmt, va));
10139N/A va_end(va);
10139N/A
10139N/A /* critical errors may contain sensitive data, so let user
10139N/A see only "Internal error" with a timestamp to make it
10139N/A easier to look from log files the actual error message. */
10139N/A mail_storage_set_internal_error(storage);
10139N/A }
10139N/A}
10139N/A
10139N/Achar mail_storage_get_hierarchy_sep(struct mail_storage *storage)
10139N/A{
10139N/A return mailbox_list_get_hierarchy_sep(storage->list);
10139N/A}
10139N/A
10139N/Astruct mailbox_list *mail_storage_get_list(struct mail_storage *storage)
10139N/A{
10139N/A return storage->list;
10139N/A}
10139N/A
10139N/Astruct mail_namespace *mail_storage_get_namespace(struct mail_storage *storage)
10139N/A{
10139N/A return storage->ns;
10139N/A}
10139N/A
10139N/Avoid mail_storage_set_callbacks(struct mail_storage *storage,
10139N/A struct mail_storage_callbacks *callbacks,
10139N/A void *context)
10139N/A{
10139N/A *storage->callbacks = *callbacks;
10139N/A storage->callback_context = context;
10139N/A}
10139N/A
10139N/Aint mail_storage_mailbox_create(struct mail_storage *storage, const char *name,
10139N/A bool directory)
10139N/A{
10139N/A mail_storage_clear_error(storage);
10139N/A
10139N/A if (!mailbox_list_is_valid_create_name(storage->list, name)) {
10139N/A mail_storage_set_error(storage, MAIL_ERROR_PARAMS,
10139N/A "Invalid mailbox name");
10139N/A return -1;
10139N/A }
10139N/A
10139N/A return storage->v.mailbox_create(storage, name, directory);
10139N/A}
10139N/A
10139N/Aconst char *mail_storage_get_last_error(struct mail_storage *storage,
10139N/A enum mail_error *error_r)
10139N/A{
10139N/A /* We get here only in error situations, so we have to return some
10139N/A error. If storage->error is NONE, it means we forgot to set it at
10139N/A some point.. */
10139N/A if (storage->error == MAIL_ERROR_NONE) {
10139N/A *error_r = MAIL_ERROR_TEMP;
10139N/A return storage->error_string != NULL ? storage->error_string :
10139N/A "BUG: Unknown internal error";
10139N/A }
10139N/A
10139N/A if (storage->error_string == NULL) {
10139N/A /* This shouldn't happen.. */
10139N/A storage->error_string =
10139N/A i_strdup_printf("BUG: Unknown 0x%x error",
10139N/A storage->error);
10139N/A }
10139N/A
10139N/A *error_r = storage->error;
10139N/A return storage->error_string;
10139N/A}
10139N/A
10139N/Aconst char *mail_storage_get_mailbox_path(struct mail_storage *storage,
10139N/A const char *name, bool *is_file_r)
10139N/A{
10139N/A *is_file_r = storage->mailbox_is_file;
10139N/A
10139N/A if (*name == '\0')
10139N/A name = NULL;
10139N/A
10139N/A return mailbox_list_get_path(storage->list, name,
10139N/A MAILBOX_LIST_PATH_TYPE_MAILBOX);
10139N/A}
10139N/A
10139N/Aconst char *mail_storage_get_mailbox_control_dir(struct mail_storage *storage,
10139N/A const char *name)
10139N/A{
10139N/A if (*name == '\0')
10139N/A name = NULL;
10139N/A
10139N/A return mailbox_list_get_path(storage->list, name,
10139N/A MAILBOX_LIST_PATH_TYPE_CONTROL);
10139N/A}
10139N/A
10139N/Aconst char *mail_storage_get_mailbox_index_dir(struct mail_storage *storage,
10139N/A const char *name)
10139N/A{
10139N/A if (*name == '\0')
10139N/A name = NULL;
10139N/A
10139N/A return mailbox_list_get_path(storage->list, name,
10139N/A MAILBOX_LIST_PATH_TYPE_INDEX);
10139N/A}
10139N/A
10139N/Aenum mailbox_list_flags
10139N/Amail_storage_get_list_flags(enum mail_storage_flags storage_flags)
10139N/A{
10139N/A enum mailbox_list_flags list_flags = 0;
if ((storage_flags & MAIL_STORAGE_FLAG_DEBUG) != 0)
list_flags |= MAILBOX_LIST_FLAG_DEBUG;
if ((storage_flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
list_flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS;
if ((storage_flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0)
list_flags |= MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL;
if ((storage_flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0)
list_flags |= MAILBOX_LIST_FLAG_NFS_FLUSH;
return list_flags;
}
bool mail_storage_set_error_from_errno(struct mail_storage *storage)
{
const char *error_string;
enum mail_error error;
if (!mail_error_from_errno(&error, &error_string))
return FALSE;
mail_storage_set_error(storage, error, error_string);
return TRUE;
}
struct mailbox *mailbox_open(struct mail_storage *storage, const char *name,
struct istream *input,
enum mailbox_open_flags flags)
{
struct mailbox *box;
mail_storage_clear_error(storage);
if (!mailbox_list_is_valid_existing_name(storage->list, name)) {
mail_storage_set_error(storage, MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return NULL;
}
T_BEGIN {
box = storage->v.mailbox_open(storage, name, input, flags);
if (hook_mailbox_opened != NULL && box != NULL)
hook_mailbox_opened(box);
} T_END;
return box;
}
int mailbox_close(struct mailbox **_box)
{
struct mailbox *box = *_box;
if (box->transaction_count != 0) {
i_panic("Trying to close mailbox %s with open transactions",
box->name);
}
*_box = NULL;
return box->v.close(box);
}
struct mail_storage *mailbox_get_storage(struct mailbox *box)
{
return box->storage;
}
const char *mailbox_get_name(struct mailbox *box)
{
return box->name;
}
bool mailbox_is_readonly(struct mailbox *box)
{
return box->v.is_readonly(box);
}
bool mailbox_allow_new_keywords(struct mailbox *box)
{
return box->v.allow_new_keywords(box);
}
void mailbox_get_status(struct mailbox *box,
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
box->v.get_status(box, items, status_r);
}
struct mailbox_sync_context *
mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
{
return box->v.sync_init(box, flags);
}
bool mailbox_sync_next(struct mailbox_sync_context *ctx,
struct mailbox_sync_rec *sync_rec_r)
{
return ctx->box->v.sync_next(ctx, sync_rec_r);
}
int mailbox_sync_deinit(struct mailbox_sync_context **_ctx,
enum mailbox_status_items status_items,
struct mailbox_status *status_r)
{
struct mailbox_sync_context *ctx = *_ctx;
*_ctx = NULL;
return ctx->box->v.sync_deinit(ctx, status_items, status_r);
}
int mailbox_sync(struct mailbox *box, enum mailbox_sync_flags flags,
enum mailbox_status_items status_items,
struct mailbox_status *status_r)
{
struct mailbox_sync_context *ctx;
/* we don't care about mailbox's current state, so we might as well
fix inconsistency state */
flags |= MAILBOX_SYNC_FLAG_FIX_INCONSISTENT;
ctx = mailbox_sync_init(box, flags);
return mailbox_sync_deinit(&ctx, status_items, status_r);
}
#undef mailbox_notify_changes
void mailbox_notify_changes(struct mailbox *box, unsigned int min_interval,
mailbox_notify_callback_t *callback, void *context)
{
box->notify_min_interval = min_interval;
box->notify_callback = callback;
box->notify_context = context;
box->v.notify_changes(box);
}
void mailbox_notify_changes_stop(struct mailbox *box)
{
mailbox_notify_changes(box, 0, NULL, NULL);
}
int mailbox_keywords_create(struct mailbox *box, const char *const keywords[],
struct mail_keywords **keywords_r)
{
const char *empty_keyword_list = NULL;
if (keywords == NULL)
keywords = &empty_keyword_list;
return box->v.keywords_create(box, keywords, keywords_r, FALSE);
}
struct mail_keywords *
mailbox_keywords_create_valid(struct mailbox *box,
const char *const keywords[])
{
const char *empty_keyword_list = NULL;
struct mail_keywords *kw;
if (keywords == NULL)
keywords = &empty_keyword_list;
if (box->v.keywords_create(box, keywords, &kw, TRUE) < 0)
i_unreached();
return kw;
}
void mailbox_keywords_free(struct mailbox *box,
struct mail_keywords **_keywords)
{
struct mail_keywords *keywords = *_keywords;
*_keywords = NULL;
box->v.keywords_free(keywords);
}
void mailbox_get_uids(struct mailbox *box, uint32_t uid1, uint32_t uid2,
uint32_t *seq1_r, uint32_t *seq2_r)
{
box->v.get_uids(box, uid1, uid2, seq1_r, seq2_r);
}
struct mailbox_header_lookup_ctx *
mailbox_header_lookup_init(struct mailbox *box, const char *const headers[])
{
return box->v.header_lookup_init(box, headers);
}
void mailbox_header_lookup_deinit(struct mailbox_header_lookup_ctx **_ctx)
{
struct mailbox_header_lookup_ctx *ctx = *_ctx;
*_ctx = NULL;
ctx->box->v.header_lookup_deinit(ctx);
}
struct mail_search_context *
mailbox_search_init(struct mailbox_transaction_context *t,
const char *charset, struct mail_search_arg *args,
const enum mail_sort_type *sort_program)
{
mail_search_args_simplify(args);
return t->box->v.search_init(t, charset, args, sort_program);
}
int mailbox_search_deinit(struct mail_search_context **_ctx)
{
struct mail_search_context *ctx = *_ctx;
*_ctx = NULL;
return ctx->transaction->box->v.search_deinit(ctx);
}
int mailbox_search_next(struct mail_search_context *ctx, struct mail *mail)
{
bool tryagain;
int ret;
while ((ret = mailbox_search_next_nonblock(ctx, mail,
&tryagain)) == 0) {
if (!tryagain)
break;
}
return ret;
}
int mailbox_search_next_nonblock(struct mail_search_context *ctx,
struct mail *mail, bool *tryagain_r)
{
return ctx->transaction->box->v.
search_next_nonblock(ctx, mail, tryagain_r);
}
struct mailbox_transaction_context *
mailbox_transaction_begin(struct mailbox *box,
enum mailbox_transaction_flags flags)
{
struct mailbox_transaction_context *trans;
box->transaction_count++;
trans = box->v.transaction_begin(box, flags);
trans->flags = flags;
return trans;
}
int mailbox_transaction_commit(struct mailbox_transaction_context **t)
{
uint32_t uidvalidity, uid1, uid2;
/* Store the return values to separate temporary variables so that
plugins overriding transaction_commit() can look at them. */
return mailbox_transaction_commit_get_uids(t, &uidvalidity,
&uid1, &uid2);
}
int mailbox_transaction_commit_get_uids(struct mailbox_transaction_context **_t,
uint32_t *uid_validity_r,
uint32_t *first_saved_uid_r,
uint32_t *last_saved_uid_r)
{
struct mailbox_transaction_context *t = *_t;
t->box->transaction_count--;
*_t = NULL;
return t->box->v.transaction_commit(t, uid_validity_r,
first_saved_uid_r,
last_saved_uid_r);
}
void mailbox_transaction_rollback(struct mailbox_transaction_context **_t)
{
struct mailbox_transaction_context *t = *_t;
t->box->transaction_count--;
*_t = NULL;
t->box->v.transaction_rollback(t);
}
unsigned int mailbox_transaction_get_count(struct mailbox *box)
{
return box->transaction_count;
}
struct mailbox *
mailbox_transaction_get_mailbox(struct mailbox_transaction_context *t)
{
return t->box;
}
int mailbox_save_init(struct mailbox_transaction_context *t,
enum mail_flags flags, struct mail_keywords *keywords,
time_t received_date, int timezone_offset,
const char *from_envelope, struct istream *input,
struct mail *dest_mail, struct mail_save_context **ctx_r)
{
if (t->box->v.save_init == NULL) {
mail_storage_set_error(t->box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Saving messages not supported");
return -1;
}
if (t->box->v.save_init(t, flags, keywords,
received_date, timezone_offset,
from_envelope, input, dest_mail, ctx_r) < 0)
return -1;
(*ctx_r)->dest_mail = dest_mail;
return 0;
}
int mailbox_save_continue(struct mail_save_context *ctx)
{
return ctx->transaction->box->v.save_continue(ctx);
}
int mailbox_save_finish(struct mail_save_context **_ctx)
{
struct mail_save_context *ctx = *_ctx;
*_ctx = NULL;
return ctx->transaction->box->v.save_finish(ctx);
}
void mailbox_save_cancel(struct mail_save_context **_ctx)
{
struct mail_save_context *ctx = *_ctx;
*_ctx = NULL;
ctx->transaction->box->v.save_cancel(ctx);
}
int mailbox_copy(struct mailbox_transaction_context *t, struct mail *mail,
enum mail_flags flags, struct mail_keywords *keywords,
struct mail *dest_mail)
{
return t->box->v.copy(t, mail, flags, keywords, dest_mail);
}
bool mailbox_is_inconsistent(struct mailbox *box)
{
return box->mailbox_deleted || box->v.is_inconsistent(box);
}
void mailbox_set_deleted(struct mailbox *box)
{
mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
"Mailbox was deleted under us");
box->mailbox_deleted = TRUE;
}
const char *mail_storage_eacces_msg(const char *func, const char *path)
{
const char *prev_path = path, *dir = "/", *p;
string_t *errmsg = t_str_new(256);
struct stat st;
int ret = -1;
str_printfa(errmsg, "%s(%s) failed: Permission denied (euid=%s egid=%s",
func, path, dec2str(geteuid()), dec2str(getegid()));
while ((p = strrchr(prev_path, '/')) != NULL) {
dir = t_strdup_until(prev_path, p);
ret = stat(dir, &st);
if (ret == 0 || errno != EACCES)
break;
prev_path = dir;
dir = "/";
}
if (ret == 0) {
if (access(dir, X_OK) < 0 && errno == EACCES)
str_printfa(errmsg, " missing +x perm: %s", dir);
else if (prev_path == path &&
access(path, R_OK) < 0 && errno == EACCES)
str_printfa(errmsg, " missing +r perm: %s", path);
}
str_append_c(errmsg, ')');
return str_c(errmsg);
}