mail-storage.c revision eaebd98a29299a4bde843323bc2edbde1d0d7d45
/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "llist.h"
#include "str.h"
#include "str-sanitize.h"
#include "sha1.h"
#include "unichar.h"
#include "hex-binary.h"
#include "file-create-locked.h"
#include "istream.h"
#include "eacces-error.h"
#include "mkdir-parents.h"
#include "time-util.h"
#include "var-expand.h"
#include "dsasl-client.h"
#include "imap-date.h"
#include "settings-parser.h"
#include "mail-index-private.h"
#include "mail-index-alloc-cache.h"
#include "mailbox-tree.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 "mail-search-mime-register.h"
#include "mailbox-search-result-private.h"
#include "mailbox-guid-cache.h"
#include "mail-cache.h"
#include <ctype.h>
#define MAILBOX_DELETE_RETRY_SECS 30
#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 255
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 };
static int mail_storage_init_refcount = 0;
void mail_storage_init(void)
{
if (mail_storage_init_refcount++ > 0)
return;
}
void mail_storage_deinit(void)
{
if (--mail_storage_init_refcount > 0)
return;
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 == '_') 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;
/* no mail_location, autodetect */
/* explicit autodetection with "auto" driver. */
/* handle the same as with driver=NULL */
}
} else {
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);
"Autodetection failed for %s (home=%s)",
} 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
bool autocreate, const char **error_r)
{
/* exists */
return 1;
"Root mail directory is a file: %s", root_dir);
return -1;
return -1;
return -1;
} else if (!autocreate) {
return -1;
} else {
/* doesn't exist */
return 0;
}
}
static int
{
enum mailbox_list_path_type type;
bool autocreate;
int ret;
type_name = "index";
} else {
type_name = "mail";
}
/* 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 the directories don't exist, we'll just autocreate them
later. FIXME: Make this the default in v2.3 even when
ITERINDEX isn't used. */
return 0;
}
if (ret == 0) {
error_r);
}
return ret < 0 ? -1 : 0;
}
static bool
const struct mail_storage *storage_class,
const struct mailbox_list_settings *set)
{
return FALSE;
return FALSE;
/* allow multiple independent shared namespaces */
return FALSE;
}
return TRUE;
}
static struct mail_storage *
const struct mail_storage *storage_class,
const struct mailbox_list_settings *set)
{
return storage;
}
return NULL;
}
struct mail_storage **storage_r,
const char **error_r)
{
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;
}
{
struct mail_storage *storage;
}
{
/* set *_storage=NULL only after calling destroy() callback.
for example mdbox wants to access ns->storage */
return;
}
i_panic("Trying to deinit storage without freeing mailbox %s",
}
if (storage->obj_refcount != 0)
i_panic("Trying to deinit storage before freeing its objects");
}
}
{
if (storage->obj_refcount++ == 0)
}
{
if (--storage->obj_refcount == 0) {
}
}
{
}
{
}
}
{
const char *str;
/* this function doesn't set last_internal_error, so
last_error_is_internal can't be TRUE. */
}
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. */
/* free the old_error and old_internal_error only after the new error
is generated, because they may be one of the parameters. */
}
enum mail_error *error_r)
{
if (storage->last_error_is_internal) {
return storage->last_internal_error;
}
}
enum mail_error *error_r)
{
error_r);
}
struct mail_storage *src)
{
const char *str;
enum mail_error error;
return;
}
struct mailbox_list *list)
{
const char *str;
enum mail_error error;
}
{
} else {
}
}
struct mail_index *index)
{
/* use the lib-index's error as our internal error string */
}
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;
}
{
struct mail_storage_error *err;
if (err->last_error_is_internal)
}
{
const struct mail_storage_error *err =
}
{
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;
}
const struct mailbox_settings *
{
struct mailbox_settings *const *box_set;
return NULL;
if (ns->prefix_len > 0 &&
/* namespace prefix itself */
vname = "";
}
}
return *box_set;
}
return NULL;
}
enum mailbox_flags flags)
{
struct mail_storage *storage;
enum mail_error open_error = 0;
/* make sure INBOX shows up in uppercase everywhere. do this
regardless of whether we're in inbox=yes namespace, because
clients expect INBOX to be case insensitive regardless of
server's internal configuration. */
vname = "INBOX";
/* not INBOX prefix */ ;
"Invalid server configuration: "
"Namespace prefix=%s must be uppercase INBOX",
} else {
}
}
T_BEGIN {
/* do a delayed failure at mailbox_open() */
}
if (open_error != 0)
} T_END;
return box;
}
const guid_128_t guid,
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;
}
{
struct mail_namespace *ns;
/* delivering to a namespace prefix means we actually want to
deliver to the INBOX instead */
name = "INBOX";
}
/* deliveries to INBOX must always succeed,
regardless of ACLs */
}
}
{
}
{
if (box->inbox_user)
return TRUE;
return TRUE;
}
{
return TRUE;
}
{
const char *errstr;
enum mail_error error;
if (error != MAIL_ERROR_EXISTS) {
"Failed to autocreate mailbox %s: %s",
return -1;
}
} else if (mailbox_is_autosubscribed(box)) {
"Failed to autosubscribe to mailbox %s: %s",
return -1;
}
}
return 0;
}
{
int ret;
if (mailbox_autocreate(box) < 0)
return -1;
"Opening INBOX failed: %s",
}
return ret;
}
static bool
const char **error_r)
{
unsigned int i;
/* Make sure the vname is correct: non-empty, doesn't begin or end
with separator and no adjacent separators */
for (i = 0; vname[i] != '\0'; i++) {
if (prev_sep) {
*error_r = "Has adjacent hierarchy separators";
return FALSE;
}
} else {
}
}
if (prev_sep && i > 0) {
*error_r = "Ends with hierarchy separator";
return FALSE;
}
return TRUE;
}
{
if (box->inbox_user) {
/* this is INBOX - don't bother with further checks */
return 0;
}
if (ns->prefix_len > 0) {
/* User input shouldn't normally be able to get us in
here. The main reason this isn't an assert is to
allow any input at all to mailbox_verify_*_name()
without crashing. */
t_strdup_printf("Invalid mailbox name '%s': "
"Missing namespace prefix '%s'",
return -1;
}
if (vname[0] != '\0') {
vname++;
if (vname[0] == '\0') {
/* "namespace/" isn't a valid mailbox name. */
"Invalid mailbox name");
return -1;
}
}
}
"Character not allowed in mailbox name: '%c'", list_sep));
return -1;
}
"Invalid mailbox name: Begins with hierarchy separator");
return -1;
}
return -1;
}
return 0;
}
{
const char *path;
return 0;
if (mailbox_verify_name(box) < 0)
return -1;
/* Make sure box->_path is set, so mailbox_get_path() works from
now on. Note that this may also fail with some backends if the
mailbox doesn't exist. */
return -1;
/* if this is an autocreated mailbox, create it now */
if (mailbox_autocreate(box) < 0)
return -1;
&path) < 0)
return -1;
}
return 0;
}
static bool mailbox_name_has_control_chars(const char *name)
{
const char *p;
for (p = name; *p != '\0'; p++) {
if ((unsigned char)*p < ' ')
return TRUE;
}
return FALSE;
}
{
}
{
/* mailbox_alloc() already checks that vname is valid UTF8,
so we don't need to verify that.
check vname instead of storage name, because vname is what is
visible to users, while storage name may be a fixed length GUID. */
if (mailbox_verify_name(box) < 0)
return -1;
return 0;
"Control characters not allowed in new mailbox names");
return -1;
}
"Mailbox name too long");
return -1;
}
/* check individual component names, too */
const char *name;
"Mailbox name too long");
return -1;
}
name++;
}
"Mailbox name too long");
return -1;
}
return 0;
}
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;
}
if (mailbox_verify_name(box) < 0) {
/* the mailbox name is invalid. we don't know if it currently
exists or not, but since it can never be accessed in any way
report it as if it didn't exist. */
return 0;
}
return 0;
}
return -1;
/* listable namespace prefix always exists. */
return 0;
}
/* if this is a shared namespace with only INBOX and
mail_shared_explicit_inbox=no, we'll need to mark the namespace as
usable here since nothing else will. */
return 0;
}
static int ATTR_NULL(2)
{
int ret;
return 0;
i_debug("%s: Mailbox opened because: %s",
}
switch (box->open_error) {
case 0:
break;
case MAIL_ERROR_NOTFOUND:
return -1;
default:
return -1;
}
if (mailbox_verify_existing_name(box) < 0)
return -1;
MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS) == 0) {
"Storage doesn't support streamed mailboxes");
return -1;
}
}
T_BEGIN {
} T_END;
} T_END;
if (ret < 0) {
return -1;
}
return 0;
}
{
/* most importantly we don't do this because we want to avoid
a loop: mdbox storage rebuild -> mailbox_open() ->
mailbox_mark_index_deleted() -> mailbox_sync() ->
mdbox storage rebuild. */
return FALSE;
}
return FALSE;
return FALSE;
if (ret < 0)
return FALSE;
return TRUE;
}
{
/* check that the storage supports stubs if require them */
"Mailbox does not support mail stubs");
return -1;
}
return -1;
/* mailbox has been marked as deleted. if this deletion
started (and crashed) a long time ago, it can be confusing
to user that the mailbox can't be opened. so we'll just
undelete it and reopen. */
if(!mailbox_try_undelete(box))
return -1;
/* make sure we close the mailbox in the middle. some backends
may not have fully opened the mailbox while it was being
undeleted. */
return -1;
}
return 0;
}
{
const char *index_dir;
int ret;
return 1;
&index_dir);
if (ret <= 0)
return ret; /* error / no private indexes */
return -1;
return 1;
}
{
int ret;
return 1;
if (mailbox_get_private_flags_mask(box) == 0)
return 0;
return ret;
return -1;
return 1;
}
{
}
{
if (mailbox_verify_name(box) < 0)
return -1;
}
{
return box->enabled_features;
}
{
return;
}
{
return;
if (box->transaction_count != 0) {
i_panic("Trying to close mailbox %s with open transactions",
}
box->recent_flags_prev_uid = 0;
box->recent_flags_count = 0;
}
{
if (box->attribute_iter_count != 0) {
i_panic("Trying to free mailbox %s with %u open attribute iterators",
}
}
{
const char *name1;
return FALSE;
return TRUE;
}
{
}
{
struct mail_namespace *ns =
enum mailbox_existence existence;
/* this should be NoSelect but since inbox can never be
NoSelect we use EXISTENCE_NONE to avoid creating inbox by accident */
mailbox_open(inbox) == 0 &&
mailbox_open(box) == 0) {
struct mail_index_transaction *dit =
/* we can't do much about errors here */
(void)mail_index_transaction_commit(&dit);
}
}
bool directory)
{
int ret;
if (mailbox_verify_create_name(box) < 0)
return -1;
if (ret == 0) {
/* Creation failed after (partially) opening the mailbox.
It may not be in a valid state, so close it. */
}
return ret;
}
{
int ret;
update->min_first_recent_uid == 0 ||
if (mailbox_verify_existing_name(box) < 0)
return -1;
return ret;
}
{
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;
}
if (del) {
/* 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. */
if (ret < 0)
return -1;
}
return 0;
}
{
}
{
int ret;
"Storage root can't be deleted");
return -1;
}
if (mailbox_open(box) < 0) {
return -1;
/* might be a \noselect mailbox, so continue deletion */
}
/* deletion failed. revert the mark so it can maybe be
tried again later. */
return -1;
}
/* if mailbox is reopened, its path may be different with
LAYOUT=index */
return ret;
}
{
int ret;
/* FIXME: should be a parameter to delete(), but since it changes API
don't do it for now */
return ret;
}
static bool
struct mail_storage *storage2,
const char **error_r)
{
return TRUE;
return FALSE;
}
/* e.g. mdbox where all mails are in storage/ directory and
they can't be easily moved from there. */
return FALSE;
}
return TRUE;
}
{
}
static bool
struct mailbox_list *list2,
const char **error_r)
{
return FALSE;
}
return FALSE;
}
return FALSE;
}
return FALSE;
}
return TRUE;
}
static
{
int ret = 0;
/* this can return folders with * in their name, that are not
actually our children */
const struct mailbox_info *child;
continue; /* not our child */
/* if total length of new name exceeds the limit, fail */
"Mailbox or child name too long");
ret = -1;
break;
}
}
/* something went bad */
if (mailbox_list_iter_deinit(&iter) < 0) {
ret = -1;
}
return ret;
}
{
/* Check only name validity, \Noselect don't necessarily exist. */
if (mailbox_verify_name(src) < 0)
return -1;
"Can't rename mailbox root");
return -1;
}
if (mailbox_verify_create_name(dest) < 0) {
return -1;
}
return -1;
}
i_debug("Can't rename '%s' to '%s': %s",
}
"Can't rename mailboxes across specified storages.");
return -1;
}
"Renaming not supported across non-private namespaces.");
return -1;
}
"Can't rename mailbox to itself.");
return -1;
}
return -1;
return 0;
}
{
if (mailbox_verify_name(box) < 0)
return -1;
return -1;
}
return 0;
}
{
struct mailbox_node *node;
}
{
}
struct mail_namespace *
{
}
{
}
{
}
{
}
{
}
{
return FALSE;
}
static void
struct mailbox_status *status_r)
{
}
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
if (mailbox_verify_existing_name(box) < 0)
return -1;
return -1;
return 0;
}
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
i_unreached();
}
struct mailbox_metadata *metadata_r)
{
if (mailbox_verify_existing_name(box) < 0)
return -1;
return -1;
return 0;
}
{
return MAIL_SEEN; /* FIXME */
else
return 0;
}
struct mailbox_sync_context *
{
struct mailbox_sync_context *ctx;
if (box->transaction_count != 0) {
i_panic("Trying to sync mailbox %s with open transactions",
}
if (mailbox_open(box) < 0) {
return ctx;
}
}
T_BEGIN {
} T_END;
return ctx;
}
struct mailbox_sync_rec *sync_rec_r)
{
if (ctx->open_failed)
return FALSE;
}
struct mailbox_sync_status *status_r)
{
const char *errormsg;
enum mail_error error;
int ret;
if (!ctx->open_failed)
else {
ret = -1;
}
if (error == MAIL_ERROR_NOTPOSSIBLE) {
}
}
if (ret == 0)
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;
}
{
*tryagain_r = FALSE;
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 *
const char *reason)
{
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;
unsigned int save_count = t->save_count;
int ret;
T_BEGIN {
} T_END;
/* either all the saved messages get UIDs or none, because a) we
failed, b) MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS not set,
c) backend doesn't support it (e.g. virtual plugin) */
/* decrease the transaction count only after transaction_commit().
that way if it creates and destroys transactions internally, we
don't see transaction_count=0 until the parent transaction is fully
finished */
box->transaction_count--;
return ret;
}
{
struct mailbox_transaction_context *t = *_t;
box->v.transaction_rollback(t);
box->transaction_count--;
}
{
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;
T_BEGIN {
} T_END;
/* Always have a dest_mail available. A lot of plugins make use
of this. */
else {
/* make sure the mail isn't used before mail_set_seq_saving() */
}
return ctx;
}
{
}
enum mail_flags flags,
struct mail_keywords *keywords)
{
}
{
const char *const *keywords_list;
struct mail_keywords *keywords;
}
{
}
{
}
{
}
const char *envelope)
{
}
{
}
}
{
}
{
}
unsigned int order)
{
}
{
}
{
int ret;
return -1;
}
/* if we're filling in a stub, we must have set UID already
(which in turn sets stub_seq) */
if (!(*ctx)->copying_or_moving) {
/* We're actually saving the mail. We're not being called by
mail_storage_copy() because backend didn't support fast
copying. */
} else {
}
"Saving messages not supported");
ret = -1;
} else T_BEGIN {
} T_END;
if (ret < 0) {
return -1;
}
return 0;
}
{
int ret;
T_BEGIN {
} T_END;
return ret;
}
static void
enum mail_flags pvt_flags)
{
struct mail_save_private_changes *save;
if (!array_is_created(&t->pvt_saves))
}
static void
{
if (!ctx->copying_or_moving) {
have come here also from mailbox_save_cancel(), in which
case ctx->saving may be FALSE. */
} else {
/* We came from mailbox_copy(). saving==TRUE is possible here
if we also came from mailbox_save_using_mail(). Don't set
saving=FALSE yet in that case, because copy() is still
running. */
}
}
{
/* we need to keep a copy of this because save_finish implementations
will likely zero the data structure during cleanup */
int ret;
/* Do one final continue. The caller may not have done it if the
input stream's offset already matched the number of bytes that
were wanted to be saved. But due to nested istreams some of the
underlying ones may not have seen the EOF yet, and haven't flushed
out the pending data. */
if (mailbox_save_continue(ctx) < 0) {
return -1;
}
T_BEGIN {
} T_END;
if (ret == 0 && !copying_via_save) {
if (pvt_flags != 0)
t->save_count++;
}
return ret;
}
{
T_BEGIN {
} T_END;
/* the dest_mail is no longer valid. if we're still saving
more mails, the mail sequence may get reused. make sure
the mail gets reset in between */
}
struct mailbox_transaction_context *
{
return ctx->transaction;
}
{
struct mail *backend_mail;
int ret;
mailbox_set_deleted(t->box);
return -1;
}
/* bypass virtual storage, so hard linking can be used whenever
possible */
return -1;
}
T_BEGIN {
} T_END;
if (ret == 0) {
if (pvt_flags != 0)
t->save_count++;
}
return ret;
}
{
}
{
int ret;
return ret;
}
{
}
{
}
{
"Mailbox was deleted under us");
}
const char **internal_path, const char **path_r)
{
int ret;
if ((*internal_path)[0] == '\0') {
return 0;
}
*path_r = *internal_path;
return 1;
}
if (ret < 0) {
return -1;
}
return ret;
}
const char **path_r)
{
if (type == MAILBOX_LIST_PATH_TYPE_MAILBOX)
if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
}
{
}
{
return box->_index_path;
}
{
return;
return;
}
struct mailbox_permissions perm;
}
{
}
}
{
(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;
}
enum mailbox_list_path_type type)
{
const char *root_dir;
if (!perm->gid_origin_is_mailbox_path) {
/* mailbox root directory doesn't exist, create it */
return -1;
}
}
perm->file_create_gid_origin) == 0)
return 1;
return 0;
"Mailbox doesn't allow inferior mailboxes");
return -1;
return -1;
} else {
"mkdir_parents(%s) failed: %m", path);
return -1;
}
}
enum mailbox_list_path_type type)
{
int ret;
return ret;
&mail_dir) < 0)
return -1;
/* Mailbox directory is different - create a missing dir */
/* This layout (e.g. imapc) wants to autocreate missing mailbox
directories as well. */
} else {
/* If the mailbox directory doesn't exist, the mailbox
shouldn't exist at all. So just assume that it's already
created and if there's a race condition just fail later. */
return 0;
}
/* we call this function even when the directory exists, so first do a
quick check to see if we need to mkdir anything */
return 0;
/* Race condition - mail root directory doesn't exist
anymore either. We shouldn't create this directory
anymore. */
return -1;
}
}
unsigned int secs)
{
}
{
enum mail_index_open_flags index_flags = 0;
#ifndef MMAP_CONFLICTS_WRITE
if (set->mmap_disable)
#endif
if (set->dotlock_use_excl)
if (set->mail_nfs_index)
return index_flags;
}
bool *utc_r)
{
unsigned int secs;
const char *error;
/* yyyy-mm-dd */
return 0;
/* imap date */
return 0;
/* unix timestamp */
return 0;
return 0;
} else {
return -1;
}
}
{
struct mail_cache_view *cache_view =
T_BEGIN {
t_strdup_printf("UID %u: %s",
} T_END;
/* update also the storage's internal error */
}
const char **error_r)
{
const struct mailbox_permissions *perm;
struct file_create_settings set;
const char *lock_path;
bool created;
else {
unsigned char box_name_sha1[SHA1_RESULTLEN];
/* Keep this simple: Use the lock_fname with a SHA1 of the
mailbox name as the suffix. The mailbox name itself could
be too large as a filename and creating the full directory
structure would be pretty troublesome. It would also make
it more difficult to perform the automated deletion of empty
lock directories. */
}
}
return 1;
}