maildir-storage.c revision 181aa01111e2de2dae413b4c1ccfcfc4e801ac40
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi/* Copyright (C) 2002-2003 Timo Sirainen */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "lib.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "home-expand.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mkdir-parents.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "unlink-directory.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "subscription-file/subscription-file.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "maildir-index.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "maildir-storage.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <stdio.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <stdlib.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <unistd.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <sys/stat.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#define CREATE_MODE 0770 /* umask() should limit it more */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistruct rename_context {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi int found;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi size_t oldnamelen;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *newname;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi};
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiextern struct mail_storage maildir_storage;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiextern struct mailbox maildir_mailbox;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *maildirs[] = { "cur", "new", "tmp", NULL };
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic struct mail_storage *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimaildir_create(const char *data, const char *user,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *namespace, char hierarchy_sep)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage *storage;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *root_dir, *inbox_dir, *index_dir, *control_dir;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *home, *path, *p;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi size_t len;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi inbox_dir = root_dir = index_dir = control_dir = NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (data == NULL || *data == '\0') {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* we'll need to figure out the maildir location ourself.
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi it's either root dir if we've already chroot()ed, or
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi $HOME/Maildir otherwise */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (access("/cur", R_OK|W_OK|X_OK) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi root_dir = "/";
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi home = getenv("HOME");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (home != NULL) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi path = t_strconcat(home, "/Maildir", NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (access(path, R_OK|W_OK|X_OK) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi root_dir = path;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* <Maildir> [:INBOX=<dir>] [:INDEX=<dir>] [:CONTROL=<dir>] */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi p = strchr(data, ':');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (p == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi root_dir = data;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi root_dir = t_strdup_until(data, p);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi do {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi p++;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strncmp(p, "INBOX=", 6) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi inbox_dir = t_strcut(p+6, ':');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else if (strncmp(p, "INDEX=", 6) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index_dir = t_strcut(p+6, ':');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else if (strncmp(p, "CONTROL=", 8) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi control_dir = t_strcut(p+8, ':');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi p = strchr(p, ':');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } while (p != NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (root_dir == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* strip trailing '/' */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi len = strlen(root_dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (root_dir[len-1] == '/')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi root_dir = t_strndup(root_dir, len-1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (index_dir == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index_dir = root_dir;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else if (strcmp(index_dir, "MEMORY") == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index_dir = NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage = i_new(struct mail_storage, 1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi memcpy(storage, &maildir_storage, sizeof(struct mail_storage));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (hierarchy_sep != '\0')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->hierarchy_sep = hierarchy_sep;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->namespace = i_strdup(namespace);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->dir = i_strdup(home_expand(root_dir));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->inbox_file = i_strdup(home_expand(inbox_dir));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->index_dir = i_strdup(home_expand(index_dir));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->control_dir = i_strdup(home_expand(control_dir));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->user = i_strdup(user);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->callbacks = i_new(struct mail_storage_callbacks, 1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index_storage_init(storage);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return storage;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void maildir_free(struct mail_storage *storage)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index_storage_deinit(storage);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage->namespace);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage->dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage->inbox_file);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage->index_dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage->control_dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage->user);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage->error);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage->callbacks);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_free(storage);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int maildir_autodetect(const char *data)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct stat st;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi data = t_strcut(data, ':');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return stat(t_strconcat(data, "/cur", NULL), &st) == 0 &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi S_ISDIR(st.st_mode);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int maildir_is_valid_create_name(const char *name)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi size_t len;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi len = strlen(name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (len == 0 || name[0] == MAILDIR_FS_SEP ||
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi strchr(name, '*') != NULL || strchr(name, '%') != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (full_filesystem_access)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*name == '~' || strchr(name, '/') != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int maildir_is_valid_existing_name(const char *name)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (name[0] == '\0')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (full_filesystem_access)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*name == '~' || strchr(name, '/') != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *maildir_get_absolute_path(const char *name, int unlink)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *p;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi name = home_expand(name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi p = strrchr(name, '/');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (p == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return name;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return t_strconcat(t_strdup_until(name, p+1),
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unlink ? MAILDIR_FS_SEP_S MAILDIR_FS_SEP_S :
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi MAILDIR_FS_SEP_S, p+1, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiconst char *maildir_fix_mailbox_name(struct mail_storage *storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *name, int remove_namespace)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi char *dup, *p, sep;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi size_t len;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strncasecmp(name, "INBOX", 5) == 0 &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (name[5] == '\0' || name[5] == storage->hierarchy_sep)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* use same case with all INBOX folders or we'll get
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi into trouble */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi name = t_strconcat("INBOX", name+5, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (name[5] == '\0') {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* don't check namespace with INBOX */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return name;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (storage->namespace != NULL && remove_namespace) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi len = strlen(storage->namespace);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strncmp(storage->namespace, name, len) != 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_panic("maildir: expecting namespace '%s' in name "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "'%s'", storage->namespace, name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi name += len;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (full_filesystem_access && (*name == '/' || *name == '~'))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return name;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi sep = storage->hierarchy_sep;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (sep == MAILDIR_FS_SEP)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return name;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dup = t_strdup_noconst(name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi for (p = dup; *p != '\0'; p++) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*p == sep)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *p = MAILDIR_FS_SEP;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return dup;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiconst char *maildir_get_path(struct mail_storage *storage, const char *name)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (full_filesystem_access && (*name == '/' || *name == '~'))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return maildir_get_absolute_path(name, FALSE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strcmp(name, "INBOX") == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return storage->inbox_file != NULL ?
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi storage->inbox_file : storage->dir;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return t_strconcat(storage->dir, "/"MAILDIR_FS_SEP_S, name, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimaildir_get_unlink_path(struct mail_storage *storage, const char *name)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (full_filesystem_access && (*name == '/' || *name == '~'))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return maildir_get_absolute_path(name, TRUE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return maildir_get_path(storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi t_strconcat(MAILDIR_FS_SEP_S, name, NULL));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *maildir_get_index_path(struct mail_storage *storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *name)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (storage->index_dir == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strcmp(name, "INBOX") == 0 && storage->inbox_file != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return storage->inbox_file;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (full_filesystem_access && (*name == '/' || *name == '~'))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return maildir_get_absolute_path(name, FALSE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *maildir_get_control_path(struct mail_storage *storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *name)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (storage->control_dir == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return maildir_get_path(storage, name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (full_filesystem_access && (*name == '/' || *name == '~'))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return maildir_get_absolute_path(name, FALSE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi name, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int mkdir_verify(struct mail_storage *storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *dir, int verify)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct stat st;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (verify) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (lstat(dir, &st) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (errno != ENOENT) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_critical(storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "lstat(%s) failed: %m", dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mkdir(dir, CREATE_MODE) < 0 && (errno != EEXIST || !verify)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (errno != EEXIST && (!verify || errno != ENOENT)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_critical(storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "mkdir(%s) failed: %m", dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi/* create or fix maildir, ignore if it already exists */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int create_maildir(struct mail_storage *storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *dir, int verify)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **tmp, *path;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!verify && !mkdir_verify(storage, dir, verify))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi for (tmp = maildirs; *tmp != NULL; tmp++) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi path = t_strconcat(dir, "/", *tmp, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!mkdir_verify(storage, path, verify)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!verify || errno != ENOENT)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* small optimization. if we're verifying, we don't
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi check that the root dir actually exists unless we
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi fail here. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!mkdir_verify(storage, dir, verify))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!mkdir_verify(storage, path, verify))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int create_index_dir(struct mail_storage *storage, const char *name)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *dir;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (storage->index_dir == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strcmp(storage->index_dir, storage->dir) == 0 ||
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (strcmp(name, "INBOX") == 0 &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi strcmp(storage->index_dir, storage->inbox_file) == 0))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dir = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mkdir_parents(dir, CREATE_MODE) == -1 && errno != EEXIST) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_critical(storage, "mkdir(%s) failed: %m", dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int create_control_dir(struct mail_storage *storage, const char *name)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *dir;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (storage->control_dir == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dir = t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi name, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_critical(storage, "mkdir(%s) failed: %m", dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int verify_inbox(struct mail_storage *storage)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *inbox;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (storage->inbox_file == NULL) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* first make sure the cur/ new/ and tmp/ dirs exist
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi in root dir */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!create_maildir(storage, storage->dir, TRUE))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* create the .INBOX directory */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi inbox = t_strconcat(storage->dir,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "/"MAILDIR_FS_SEP_S"INBOX", NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!mkdir_verify(storage, inbox, TRUE))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!create_maildir(storage, storage->inbox_file, TRUE))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* make sure the index directories exist */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return create_index_dir(storage, "INBOX") &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi create_control_dir(storage, "INBOX");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void maildir_mail_init(struct index_mail *mail)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail->mail.copy = maildir_storage_copy;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail->mail.expunge = maildir_storage_expunge;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic struct mailbox *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimaildir_open(struct mail_storage *storage, const char *name,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum mailbox_open_flags flags)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct index_mailbox *ibox;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_index *index;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *path, *index_dir, *control_dir;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct stat st;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi path = maildir_get_path(storage, name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index_dir = maildir_get_index_path(storage, name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi control_dir = maildir_get_control_path(storage, name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index = index_storage_lookup_ref(index_dir, path);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (index == NULL) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index = maildir_index_alloc(path, index_dir, control_dir);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index_storage_add(index);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ibox = index_storage_mailbox_init(storage, &maildir_mailbox,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index, name, flags);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ibox != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ibox->mail_init = maildir_mail_init;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* for shared mailboxes get the create mode from the
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi permissions of dovecot-shared file */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (stat(t_strconcat(path, "/dovecot-shared", NULL), &st) < 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index->mail_create_mode = 0600;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index->mail_create_mode = st.st_mode & 0666;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index->private_flags_mask = MAIL_SEEN;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return (struct mailbox *) ibox;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic struct mailbox *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimaildir_open_mailbox(struct mail_storage *storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *name, enum mailbox_open_flags flags)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *path;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct stat st;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_clear_error(storage);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi name = maildir_fix_mailbox_name(storage, name, TRUE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strcmp(name, "INBOX") == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!verify_inbox(storage))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return maildir_open(storage, "INBOX", flags);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!maildir_is_valid_existing_name(name)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_error(storage, "Invalid mailbox name");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi path = maildir_get_path(storage, name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (stat(path, &st) == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* exists - make sure the required directories are also there */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!create_maildir(storage, path, TRUE) ||
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi !create_index_dir(storage, name) ||
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi !create_control_dir(storage, name))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return maildir_open(storage, name, flags);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else if (errno == ENOENT) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_error(storage, "Mailbox doesn't exist: %s",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_critical(storage, "stat(%s) failed: %m", path);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int maildir_create_mailbox(struct mail_storage *storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *name,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi int directory __attr_unused__)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *path;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_clear_error(storage);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi name = maildir_fix_mailbox_name(storage, name, TRUE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!maildir_is_valid_create_name(name)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_error(storage, "Invalid mailbox name");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi path = maildir_get_path(storage, name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!create_maildir(storage, path, FALSE)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (errno == EEXIST) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_error(storage,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "Mailbox already exists");
}
return FALSE;
}
return TRUE;
}
static int maildir_delete_mailbox(struct mail_storage *storage,
const char *name)
{
struct stat st;
const char *src, *dest, *index_dir;
int count;
mail_storage_clear_error(storage);
name = maildir_fix_mailbox_name(storage, name, TRUE);
if (strcmp(name, "INBOX") == 0) {
mail_storage_set_error(storage, "INBOX can't be deleted.");
return FALSE;
}
if (!maildir_is_valid_existing_name(name)) {
mail_storage_set_error(storage, "Invalid mailbox name");
return FALSE;
}
/* rename the .maildir into ..maildir which marks it as being
deleted. delete indexes before the actual maildir. this way we
never see partially deleted mailboxes. */
src = maildir_get_path(storage, name);
dest = maildir_get_unlink_path(storage, name);
if (stat(src, &st) != 0 && errno == ENOENT) {
mail_storage_set_error(storage, "Mailbox doesn't exist: %s",
name);
return FALSE;
}
if (storage->index_dir != NULL && *name != '/' && *name != '~' &&
strcmp(storage->index_dir, storage->dir) != 0) {
index_dir = t_strconcat(storage->index_dir,
"/"MAILDIR_FS_SEP_S, name, NULL);
index_storage_destroy_unrefed();
/* it can fail with some NFS implementations if indexes are
opened by another session.. can't really help it. */
if (unlink_directory(index_dir, TRUE) < 0 &&
errno != ENOTEMPTY) {
mail_storage_set_critical(storage,
"unlink_directory(%s) failed: %m", index_dir);
return FALSE;
}
}
count = 0;
while (rename(src, dest) < 0 && count < 2) {
if (errno != EEXIST && errno != ENOTEMPTY) {
mail_storage_set_critical(storage,
"rename(%s, %s) failed: %m", src, dest);
return FALSE;
}
/* ..dir already existed? delete it and try again */
if (unlink_directory(dest, TRUE) < 0) {
mail_storage_set_critical(storage,
"unlink_directory(%s) failed: %m", dest);
return FALSE;
}
count++;
}
if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) {
mail_storage_set_critical(storage,
"unlink_directory(%s) failed: %m", dest);
/* it's already renamed to ..dir, which means it's deleted
as far as client is concerned. Report success. */
}
return TRUE;
}
static int rename_indexes(struct mail_storage *storage,
const char *oldname, const char *newname)
{
const char *oldpath, *newpath;
if (storage->index_dir == NULL ||
strcmp(storage->index_dir, storage->dir) == 0)
return TRUE;
/* Rename it's index. */
oldpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
oldname, NULL);
newpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
newname, NULL);
if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
oldpath, newpath);
return FALSE;
}
return TRUE;
}
static int rename_subfolders(struct mail_storage *storage,
const char *oldname, const char *newname)
{
struct mailbox_list_context *ctx;
struct mailbox_list *list;
const char *oldpath, *newpath, *new_listname, *mask;
size_t oldnamelen;
int ret;
ret = 0;
oldnamelen = strlen(oldname);
mask = t_strdup_printf("%s%s%c*", storage->namespace != NULL ?
storage->namespace : "", oldname,
storage->hierarchy_sep);
ctx = storage->list_mailbox_init(storage, mask,
MAILBOX_LIST_FAST_FLAGS);
while ((list = maildir_list_mailbox_next(ctx)) != NULL) {
const char *list_name;
t_push();
list_name = maildir_fix_mailbox_name(storage, list->name, TRUE);
i_assert(oldnamelen <= strlen(list_name));
new_listname = t_strconcat(newname,
list_name + oldnamelen, NULL);
oldpath = maildir_get_path(storage, list_name);
newpath = maildir_get_path(storage, new_listname);
/* FIXME: it's possible to merge two folders if either one of
them doesn't have existing root folder. We could check this
but I'm not sure if it's worth it. It could be even
considered as a feature.
Anyway, the bug with merging is that if both folders have
identically named subfolder they conflict. Just ignore those
and leave them under the old folder. */
if (rename(oldpath, newpath) == 0 ||
errno == EEXIST || errno == ENOTEMPTY)
ret = 1;
else {
mail_storage_set_critical(storage,
"rename(%s, %s) failed: %m",
oldpath, newpath);
ret = -1;
t_pop();
break;
}
(void)rename_indexes(storage, list_name, new_listname);
t_pop();
}
if (!maildir_list_mailbox_deinit(ctx))
return -1;
return ret;
}
static int maildir_rename_mailbox(struct mail_storage *storage,
const char *oldname, const char *newname)
{
const char *oldpath, *newpath;
int ret, found;
mail_storage_clear_error(storage);
oldname = maildir_fix_mailbox_name(storage, oldname, TRUE);
newname = maildir_fix_mailbox_name(storage, newname, TRUE);
if (!maildir_is_valid_existing_name(oldname) ||
!maildir_is_valid_create_name(newname)) {
mail_storage_set_error(storage, "Invalid mailbox name");
return FALSE;
}
if (strcmp(oldname, "INBOX") == 0) {
mail_storage_set_error(storage,
"Renaming INBOX isn't supported.");
return FALSE;
}
/* NOTE: it's possible to rename a nonexisting folder which has
subfolders. In that case we should ignore the rename() error. */
oldpath = maildir_get_path(storage, oldname);
newpath = maildir_get_path(storage, newname);
ret = rename(oldpath, newpath);
if (ret == 0 || errno == ENOENT) {
(void)rename_indexes(storage, oldname, newname);
found = ret == 0;
ret = rename_subfolders(storage, oldname, newname);
if (ret < 0)
return FALSE;
if (!found && ret == 0) {
mail_storage_set_error(storage,
"Mailbox doesn't exist");
return FALSE;
}
return TRUE;
}
if (errno == EEXIST) {
mail_storage_set_error(storage,
"Target mailbox already exists");
return FALSE;
} else {
mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
oldpath, newpath);
return FALSE;
}
}
static int maildir_set_subscribed(struct mail_storage *storage,
const char *name, int set)
{
name = maildir_fix_mailbox_name(storage, name, FALSE);
return subsfile_set_subscribed(storage, name, set);
}
static int maildir_get_mailbox_name_status(struct mail_storage *storage,
const char *name,
enum mailbox_name_status *status)
{
struct stat st;
const char *path;
mail_storage_clear_error(storage);
name = maildir_fix_mailbox_name(storage, name, TRUE);
if (!maildir_is_valid_existing_name(name)) {
*status = MAILBOX_NAME_INVALID;
return TRUE;
}
path = maildir_get_path(storage, name);
if (stat(path, &st) == 0) {
*status = MAILBOX_NAME_EXISTS;
return TRUE;
}
if (!maildir_is_valid_create_name(name)) {
*status = MAILBOX_NAME_INVALID;
return TRUE;
}
if (errno == ENOENT) {
*status = MAILBOX_NAME_VALID;
return TRUE;
} else {
mail_storage_set_critical(storage, "stat(%s) failed: %m", path);
return FALSE;
}
}
static int maildir_storage_close(struct mailbox *box)
{
struct index_mailbox *ibox = (struct index_mailbox *) box;
int failed = FALSE;
index_storage_init_lock_notify(ibox);
if (!maildir_try_flush_dirty_flags(ibox->index, TRUE)) {
mail_storage_set_index_error(ibox);
failed = TRUE;
}
ibox->index->set_lock_notify_callback(ibox->index, NULL, NULL);
return index_storage_mailbox_free(box) && !failed;
}
static void maildir_storage_auto_sync(struct mailbox *box,
enum mailbox_sync_flags flags,
unsigned int min_newmail_notify_interval)
{
struct index_mailbox *ibox = (struct index_mailbox *) box;
ibox->min_newmail_notify_interval = min_newmail_notify_interval;
if ((ibox->autosync_flags == 0 && flags == 0) ||
(ibox->autosync_flags != 0 && flags != 0)) {
/* flags or interval just changed. or nothing. */
ibox->autosync_flags = flags;
}
ibox->autosync_flags = flags;
if (flags == 0) {
index_mailbox_check_remove_all(ibox);
return;
}
index_mailbox_check_add(ibox,
t_strconcat(ibox->index->mailbox_path, "/new", NULL), TRUE);
index_mailbox_check_add(ibox,
t_strconcat(ibox->index->mailbox_path, "/cur", NULL), TRUE);
}
static int maildir_storage_lock(struct mailbox *box,
enum mailbox_lock_type lock_type)
{
struct index_mailbox *ibox = (struct index_mailbox *) box;
if (lock_type == MAIL_LOCK_UNLOCK) {
ibox->lock_type = MAIL_LOCK_UNLOCK;
if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
return FALSE;
return TRUE;
}
i_assert(ibox->lock_type == MAIL_LOCK_UNLOCK);
if ((lock_type & (MAILBOX_LOCK_EXPUNGE | MAILBOX_LOCK_FLAGS)) != 0) {
if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE))
return FALSE;
} else if ((lock_type & MAILBOX_LOCK_READ) != 0) {
if (!index_storage_lock(ibox, MAIL_LOCK_SHARED))
return FALSE;
}
ibox->lock_type = lock_type;
return TRUE;
}
struct mail_storage maildir_storage = {
"maildir", /* name */
NULL, /* namespace */
'.', /* default hierarchy separator */
maildir_create,
maildir_free,
maildir_autodetect,
index_storage_set_callbacks,
maildir_open_mailbox,
maildir_create_mailbox,
maildir_delete_mailbox,
maildir_rename_mailbox,
maildir_list_mailbox_init,
maildir_list_mailbox_deinit,
maildir_list_mailbox_next,
maildir_set_subscribed,
maildir_get_mailbox_name_status,
mail_storage_get_last_error,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL, NULL, NULL,
0
};
struct mailbox maildir_mailbox = {
NULL, /* name */
NULL, /* storage */
index_storage_is_readonly,
index_storage_allow_new_custom_flags,
maildir_storage_close,
maildir_storage_lock,
index_storage_get_status,
index_storage_sync,
maildir_storage_auto_sync,
index_storage_fetch_init,
index_storage_fetch_deinit,
index_storage_fetch_next,
index_storage_fetch_uid,
index_storage_fetch_seq,
index_storage_search_get_sorting,
index_storage_search_init,
index_storage_search_deinit,
index_storage_search_next,
maildir_storage_save_init,
maildir_storage_save_deinit,
maildir_storage_save_next,
maildir_storage_copy_init,
maildir_storage_copy_deinit,
maildir_storage_expunge_init,
maildir_storage_expunge_deinit,
maildir_storage_expunge_fetch_next,
index_storage_is_inconsistency_error
};