mbox-storage.c revision 690bafa70767e3f6e98bbfd62ad4a26be2387ea9
/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "istream.h"
#include "restrict-access.h"
#include "mkdir-parents.h"
#include "unlink-directory.h"
#include "mbox-storage.h"
#include "mbox-lock.h"
#include "mbox-file.h"
#include "mbox-sync-private.h"
#include "istream-raw-mbox.h"
#include "mail-copy.h"
#include "index-mail.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
/* How often to touch the dotlock file when using KEEP_LOCKED flag */
/* Assume that if atime < mtime, there are new mails. If it's good enough for
UW-IMAP, it's good enough for us. */
#define STAT_GET_MARKED(st) \
#define MBOX_LIST_CONTEXT(obj) \
/* NOTE: must be sorted for istream-header-filter. Note that it's not such
a good idea to change this list, as the messages will then change from
client's point of view. So if you do it, change all mailboxes' UIDVALIDITY
so all caches are reset. */
const char *mbox_hide_headers[] = {
"Content-Length",
"Status",
"X-IMAP",
"X-IMAPbase",
"X-Keywords",
"X-Status",
"X-UID"
};
/* A bit ugly duplification of the above list. It's safe to modify this list
without bad side effects, just keep the list sorted. */
const char *mbox_save_drop_headers[] = {
"Content-Length",
"Status",
"X-Delivery-ID"
"X-IMAP",
"X-IMAPbase",
"X-Keywords",
"X-Status",
"X-UID"
};
extern struct mail_storage mbox_storage;
extern struct mailbox mbox_mailbox;
const char *mailbox_name,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags);
const char *name);
{
} else {
"%s failed with mbox file %s: %m",
}
return -1;
}
{
if (debug) {
i_info("mbox autodetect: %s: stat(%s) failed: %m",
}
return FALSE;
}
if (debug) {
i_info("mbox autodetect: %s: is a directory (%s)",
}
return FALSE;
}
if (debug) {
i_info("mbox autodetect: %s: no R/W access (%s)",
}
return FALSE;
}
if (debug)
return TRUE;
}
{
if (debug) {
i_info("mbox autodetect: %s: stat(%s) failed: %m",
}
return FALSE;
}
if (debug) {
i_info("mbox autodetect: %s: is not a directory (%s)",
}
return FALSE;
}
if (debug) {
i_info("mbox autodetect: %s: no R/W/X access (%s)",
}
return FALSE;
}
if (debug)
return TRUE;
}
{
const char *path;
if (debug) {
i_info("mbox autodetect: data=%s, splitting ':' -> %s",
} else {
}
}
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return FALSE;
}
{
if (debug)
return path;
}
if (debug)
if (debug)
return path;
}
if (debug)
}
if (debug)
i_info("mbox: checking if we are chrooted:");
return "/";
if (debug)
i_info("mbox: root mail directory not found");
return NULL;
}
static const char *
{
const char *path;
if (!only_root) {
if (debug)
return path;
}
if (debug)
if (debug)
return path;
}
if (debug)
}
if (debug)
return path;
}
const char **error_r)
{
*error_r = "Root mail directory not set and "
"home directory is missing";
return NULL;
}
return NULL;
}
return path;
}
static int
{
const char *p;
bool autodetect;
*layout_r = "fs";
if (autodetect) {
if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) {
*error_r = "Root mail directory not given";
return -1;
}
/* we'll need to figure out the mail location ourself.
it's root dir if we've already chroot()ed, otherwise
either ~/mail or ~/Mail */
} else {
if (debug)
if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) == 0 &&
/* if the data points to a file, treat it as an INBOX */
else {
}
/* the most common misconfiguration */
*error_r = "Root mail directory not given";
return -1;
} else {
error_r) < 0)
return -1;
}
}
if ((flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) != 0) {
*error_r = "Root mail directory not given";
return -1;
}
return -1;
} else {
/* make sure the directory exists */
/* yep, go ahead */
return -1;
return -1;
/* can't create a new user, but we don't want to fail
the storage creation. */
} else if ((flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) != 0) {
"Root mail directory doesn't exist: %s",
return -1;
return -1;
}
}
}
return 0;
}
static const char *
enum mailbox_list_path_type type)
{
const char *path, *p;
if (type == MAILBOX_LIST_PATH_TYPE_CONTROL ||
if (p == NULL)
return "";
}
return path;
}
static struct mail_storage *mbox_alloc(void)
{
struct mbox_storage *storage;
}
static bool mbox_name_is_dotlock(const char *name)
{
}
static bool
{
}
static bool
{
}
const char **error_r)
{
struct mailbox_list_settings list_set;
const char *layout;
return -1;
return -1;
/* have to use .imap/ directories */
}
/* finish list init after we've overridden vfuncs */
return 0;
}
{
const char *inbox_path, *rootdir;
int fd;
/* make sure inbox file itself exists */
/* try again with increased privileges */
(void)restrict_access_use_priv_gid();
}
if (fd != -1)
"mbox root directory can't be a file: %s "
rootdir);
return -1;
return -1;
"open(%s, O_CREAT) failed: %m", inbox_path);
return -1;
}
return 0;
}
{
const char *env;
unsigned int min_size;
return FALSE;
if (min_size == 0)
return FALSE;
else {
"stat(%s) failed: %m", path);
return FALSE;
}
}
}
{
}
static struct mbox_mailbox *
enum mailbox_open_flags flags)
{
struct mbox_mailbox *mbox;
mbox->mbox_ext_idx =
if ((flags & MAILBOX_OPEN_KEEP_LOCKED) != 0) {
mailbox_close(&box);
return NULL;
}
mbox->keep_lock_to =
}
}
return mbox;
}
static struct mailbox *
enum mailbox_open_flags flags)
{
struct mbox_mailbox *mbox;
struct mail_index *index;
else {
}
}
/* if INBOX isn't under the root directory, it's probably in
}
}
static struct mailbox *
{
struct mail_index *index;
struct mbox_mailbox *mbox;
const char *path;
return NULL;
}
static struct mailbox *
{
const char *path;
/* make sure INBOX exists */
if (verify_inbox(_storage) < 0)
return NULL;
}
t_strdup_printf("Mailbox isn't selectable: %s",
name));
return NULL;
}
}
} else if (!mail_storage_set_error_from_errno(_storage)) {
path);
}
return NULL;
}
bool directory)
{
const char *path, *p;
int fd;
/* make sure it doesn't exist already */
"Mailbox already exists");
return -1;
}
"Mailbox doesn't allow inferior mailboxes");
} else if (!mail_storage_set_error_from_errno(_storage)) {
"stat() failed for mbox file %s: %m", path);
}
return -1;
}
/* create the hierarchy if needed */
if (p != NULL) {
p = t_strdup_until(path, p);
"mkdir_parents(%s) failed: %m", p);
}
return -1;
}
if (directory) {
/* wanted to create only the directory */
return 0;
}
}
/* create the mailbox file */
if (fd != -1) {
return 0;
}
/* mailbox was just created between stat() and open() call.. */
"Mailbox already exists");
} else if (!mail_storage_set_error_from_errno(_storage)) {
"Can't create mailbox %s: %m", name);
}
return -1;
}
{
const struct mail_index_header *hdr;
enum mbox_sync_flags sync_flags = 0;
int ret = 0;
/* clear the corruption by forcing a full resync */
}
!mbox->mbox_readonly) {
/* we've done changes to mbox which haven't been
written yet. do it now. */
}
}
ret = -1;
}
if (mbox->mbox_global_lock_id != 0)
}
{
else if (!mbox->no_mbox_file)
}
static bool
{
const char *inbox_path;
return FALSE;
}
const char *mailbox_name ATTR_UNUSED,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags)
{
*flags |= MAILBOX_NOSELECT;
return 0;
}
return 0;
}
}
/* skip all .lock files */
return 0;
}
/* try to avoid stat() with these checks */
if (type == MAILBOX_LIST_FILE_TYPE_DIR) {
return 1;
}
if (type != MAILBOX_LIST_FILE_TYPE_SYMLINK &&
*flags |= MAILBOX_NOINFERIORS;
return 1;
}
/* need to stat() then */
else {
/* it's possible for INBOX to have child
mailboxes as long as the inbox file itself
isn't in <mail root>/INBOX */
*flags &= ~MAILBOX_NOINFERIORS;
}
}
return 1;
/* doesn't exist - probably a non-existing subscribed mailbox */
*flags |= MAILBOX_NONEXISTENT;
return 1;
} else {
/* non-selectable. probably either access denied, or symlink
destination not found. don't bother logging errors. */
*flags |= MAILBOX_NOSELECT;
return 0;
}
}
const char *name)
{
} else if (!mailbox_list_set_error_from_errno(list)) {
"lstat() failed for %s: %m", path);
}
return -1;
}
/* deleting a directory. allow it only if it doesn't contain
anything. Delete the ".imap" directory first in case there
have been indexes. */
if (!mailbox_list_set_error_from_errno(list)) {
"rmdir() failed for %s: %m", index_dir);
}
return -1;
}
return 0;
t_strdup_printf("Directory %s isn't empty, "
"can't delete it.", name));
} else if (!mailbox_list_set_error_from_errno(list)) {
"rmdir() failed for %s: %m", path);
}
return -1;
}
/* delete index / control files first */
return -1;
} else if (!mailbox_list_set_error_from_errno(list)) {
"unlink() failed for %s: %m", path);
}
return -1;
}
return 0;
}
static void mbox_class_init(void)
{
}
static void mbox_class_deinit(void)
{
}
struct mail_storage mbox_storage = {
{
}
};
struct mailbox mbox_mailbox = {
{
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
}
};