/* Copyright (c) 2002-2018 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-dotlock.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>
extern struct mail_search_register *mail_search_register_imap;
extern struct mail_search_register *mail_search_register_human;
.name = "storage",
};
.name = "mailbox",
};
.name = "mail",
};
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 */
}
{
unsigned int i, count;
for (i = 0; i < count; i++) {
if (classes[i] == storage_class) {
break;
}
}
}
{
unsigned int i, count;
for (i = 0; i < count; i++) {
return classes[i];
}
return NULL;
}
static struct mail_storage *
struct mailbox_list_settings *set)
{
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? */
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
const char **error_r)
{
/* exists */
return 0;
"Root mail directory is a file: %s", root_dir);
return -1;
return -1;
return -1;
} else {
return -1;
}
}
static int
{
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 ((flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) == 0) {
/* If the directories don't exist, we'll just autocreate them
later. */
return 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)
{
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;
}
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. */
}
{
T_BEGIN {
} T_END;
}
{
T_BEGIN {
} else {
}
} T_END;
}
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;
return;
}
struct mailbox_list *list)
{
const char *str;
}
{
} else {
}
}
struct mail_index *index)
{
const char *index_error;
/* use the lib-index's error as our internal error string */
if (index_error == NULL)
index_error = "BUG: Unknown internal index error";
}
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)
{
}
{
return error;
}
{
if (err->last_error_is_internal)
}
{
}
{
return (storage->class_flags &
}
{
const char *error_string;
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 *
{
return NULL;
if (ns->prefix_len > 0 &&
/* namespace prefix itself */
vname = "";
}
}
return *box_set;
}
return NULL;
}
enum mailbox_flags flags)
{
/* 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)
{
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;
}
{
/* 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;
if (error != MAIL_ERROR_EXISTS) {
"Failed to autocreate mailbox: %s",
errstr);
return -1;
}
} else if (mailbox_is_autosubscribed(box)) {
"Failed to autosubscribe to mailbox: %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 doesn't have extra separators:
1) Must not have adjacent separators. If we allow these, these could
end up pointing to existing mailboxes due to kernel ignoring
duplicate '/' in paths. However, this might cause us to handle some
of our own checks wrong, such as skipping ACLs.
2) Must not end with separator. Similar reasoning as above.
*/
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;
}
static bool
{
if (ns->prefix_len == 0)
return TRUE;
/* 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. */
return FALSE;
}
if (vname[0] != '\0') {
vname++;
if (vname[0] == '\0') {
/* "namespace/" isn't a valid mailbox name. */
*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;
}
t_strdup_printf("Invalid mailbox name '%s': %s",
return -1;
}
/* If namespace { separator } differs from the mailbox_list separator,
the list separator can't actually be used in the mailbox name
unless it's escaped with escape_char. For example if namespace
separator is '/' and LAYOUT=Maildir++ has '.' as the separator,
there's no way to use '.' in the mailbox name (without escaping)
because it would end up becoming a hierarchy separator. */
"Character not allowed in mailbox name: '%c'", list_sep));
return -1;
}
/* vname must not begin with the hierarchy separator normally.
if mail_full_filesystem_access=yes, we do actually want to allow
that. */
"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;
}
{
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;
}
{
}
{
/* 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) {
/* 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;
/* Avoid race conditions by keeping mailbox list locked during changes.
This especially fixes a race during INBOX creation with LAYOUT=index
because it scans for missing mailboxes if INBOX doesn't exist. The
second process's scan can find a half-created INBOX and add it,
causing the first process to become confused. */
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;
}
{
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;
}
{
}
{
bool list_locked;
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 */
}
list_locked = FALSE;
ret = -1;
} else {
list_locked = TRUE;
}
/* deletion failed. revert the mark so it can maybe be
tried again later. */
ret = -1;
}
if (list_locked)
/* 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 */
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;
}
/* It would be safer to lock both source and destination, but that
could lead to deadlocks. So at least for now lets just lock only the
destination list. */
return -1;
}
if (ret < 0)
return -1;
return 0;
}
{
if (mailbox_verify_name(box) < 0)
return -1;
return -1;
}
return 0;
}
{
}
{
}
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 *
{
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;
int ret;
if (!ctx->open_failed)
else {
ret = -1;
}
if (error == MAIL_ERROR_NOTPOSSIBLE) {
}
}
if (ret == 0)
return ret;
}
{
/* we don't care about mailbox's current state, so we might
as well fix inconsistency state */
}
}
{
}
{
}
struct mail_search_context *
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;
}
struct mail_search_args *args,
struct mail_search_result **result_r)
{
int ret;
if (ret < 0)
return ret;
}
struct mailbox_transaction_context *
const char *reason)
{
box->transaction_count++;
return trans;
}
{
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_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;
}
{
}
struct mailbox *
{
return t->box;
}
{
}
struct mail_save_context *
{
mailbox_get_settings(t->box);
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() */
}
/* make sure parts get parsed early on */
return ctx;
}
{
}
enum mail_flags flags,
struct mail_keywords *keywords)
{
}
{
const char *const *keywords_list;
}
{
}
{
}
{
}
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)
{
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. */
}
}
{
mailbox_get_settings(t->box);
/* 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;
}
{
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;
}
}
{
}
}
{
(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 {
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 {
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)
{
}
{
#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;
}
}
{
T_BEGIN {
t_strdup_printf("UID %u: %s",
} T_END;
/* update also the storage's internal error */
}
static int
const struct file_create_settings *lock_set,
const struct mail_storage_settings *mail_set,
{
.lock_suffix = "",
.use_io_notify = TRUE,
};
if (ret <= 0) {
return ret;
}
return 1;
}
const struct file_create_settings *lock_set,
const struct mail_storage_settings *mail_set,
{
bool created;
}
return 1;
}
const char **error_r)
{
const char *lock_path;
else {
/* 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. */
}
}