dbox-storage.c revision f1743785713e7632459d623d5df2108f4b93accb
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2007-2008 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "lib.h"
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen#include "array.h"
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen#include "ioloop.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "str.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "mkdir-parents.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "unlink-old-files.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "index-mail.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "mail-copy.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "dbox-sync.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "dbox-index.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "dbox-file.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include "dbox-storage.h"
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include <stdio.h>
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include <stdlib.h>
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include <unistd.h>
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen#include <dirent.h>
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include <sys/stat.h>
bf87a5247ca33660ab8f20c12556ad16d7159395Timo Sirainen
bf87a5247ca33660ab8f20c12556ad16d7159395Timo Sirainen#define CREATE_MODE 0770 /* umask() should limit it more */
bf87a5247ca33660ab8f20c12556ad16d7159395Timo Sirainen
bf87a5247ca33660ab8f20c12556ad16d7159395Timo Sirainen#define DBOX_LIST_CONTEXT(obj) \
bf87a5247ca33660ab8f20c12556ad16d7159395Timo Sirainen MODULE_CONTEXT(obj, dbox_mailbox_list_module)
bf87a5247ca33660ab8f20c12556ad16d7159395Timo Sirainen
bf87a5247ca33660ab8f20c12556ad16d7159395Timo Sirainenextern struct mail_storage dbox_storage;
bf87a5247ca33660ab8f20c12556ad16d7159395Timo Sirainenextern struct mailbox dbox_mailbox;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(dbox_mailbox_list_module,
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen &mailbox_list_module_register);
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainendbox_list_delete_mailbox(struct mailbox_list *list, const char *name);
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainenstatic int
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainendbox_list_rename_mailbox(struct mailbox_list *list,
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen const char *oldname, const char *newname);
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainenstatic int
8b2cf1c1bd8ddcea0525b62fd35ba76e136828a1Timo Sirainendbox_list_rename_mailbox_pre(struct mailbox_list *list,
8b2cf1c1bd8ddcea0525b62fd35ba76e136828a1Timo Sirainen const char *oldname, const char *newname);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *dir, const char *fname,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen const char *mailbox_name,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen enum mailbox_list_file_type type,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen enum mailbox_info_flags *flags);
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainenstatic int
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainendbox_get_list_settings(struct mailbox_list_settings *list_set,
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen const char *data, struct mail_storage *storage,
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char **layout_r, const char **alt_dir_r,
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen const char **error_r)
9b7eeffb5752b500ac62ba1fd01c4a8c4ada14e9Timo Sirainen{
9b7eeffb5752b500ac62ba1fd01c4a8c4ada14e9Timo Sirainen bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen *layout_r = "fs";
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen memset(list_set, 0, sizeof(*list_set));
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen list_set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME;
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen list_set->maildir_name = DBOX_MAILDIR_NAME;
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen if (data == NULL || *data == '\0' || *data == ':') {
b565a6a7a66fb9f224d00c06a950e3c1c585c18eTimo Sirainen /* we won't do any guessing for this format. */
b565a6a7a66fb9f224d00c06a950e3c1c585c18eTimo Sirainen if (debug)
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen i_info("dbox: mailbox location not given");
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen *error_r = "Root mail directory not given";
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen return -1;
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen }
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen if (debug)
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen i_info("dbox: data=%s", data);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen return mailbox_list_settings_parse(data, list_set, storage->ns,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen layout_r, alt_dir_r, error_r);
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen}
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainenstatic struct mail_storage *dbox_alloc(void)
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen{
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct dbox_storage *storage;
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen pool_t pool;
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen
f988b93c2ef773987bcdcbfb4cca39b955e3a392Timo Sirainen pool = pool_alloconly_create("dbox storage", 512+256);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen storage = p_new(pool, struct dbox_storage, 1);
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen storage->storage = dbox_storage;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen storage->storage.pool = pool;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen storage->storage.storage_class = &dbox_storage;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return &storage->storage;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen}
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic int dbox_create(struct mail_storage *_storage, const char *data,
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char **error_r)
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen{
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen struct dbox_storage *storage = (struct dbox_storage *)_storage;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen struct mailbox_list_settings list_set;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen struct stat st;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char *layout, *alt_dir;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen if (dbox_get_list_settings(&list_set, data, _storage,
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen &layout, &alt_dir, error_r) < 0)
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen return -1;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen list_set.mail_storage_flags = &_storage->flags;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen list_set.lock_method = &_storage->lock_method;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if ((_storage->flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) != 0) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (stat(list_set.root_dir, &st) < 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (errno == ENOENT) {
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen *error_r = t_strdup_printf(
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen "Root mail directory doesn't exist: %s",
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen list_set.root_dir);
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen } else if (errno == EACCES) {
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen *error_r = mail_storage_eacces_msg("stat",
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen list_set.root_dir);
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen } else {
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen *error_r = t_strdup_printf(
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen "stat(%s) failed: %m",
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen list_set.root_dir);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return -1;
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen }
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen } else if (mkdir_parents(list_set.root_dir,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen CREATE_MODE) == 0 || errno == EEXIST) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen } else if (errno == EACCES) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen *error_r = mail_storage_eacces_msg("mkdir", list_set.root_dir);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return -1;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen } else {
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen *error_r = t_strdup_printf("mkdir(%s) failed: %m",
683eebe490bbe5caec246c535a10ea9f93f5c330Timo Sirainen list_set.root_dir);
683eebe490bbe5caec246c535a10ea9f93f5c330Timo Sirainen return -1;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen }
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen return -1;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen storage->list_module_ctx.super = _storage->list->v;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen storage->alt_dir = p_strdup(_storage->pool, alt_dir);
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen _storage->list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen _storage->list->v.delete_mailbox = dbox_list_delete_mailbox;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen _storage->list->v.rename_mailbox = dbox_list_rename_mailbox;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen _storage->list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen MODULE_CONTEXT_SET_FULL(_storage->list, dbox_mailbox_list_module,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen storage, &storage->list_module_ctx);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* finish list init after we've overridden vfuncs */
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen mailbox_list_init(_storage->list, _storage->ns, &list_set,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen mail_storage_get_list_flags(_storage->flags));
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen return 0;
ceb43cc04edb94445fab8f914bc4da6d740403d1Timo Sirainen}
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainenstatic int create_dbox(struct mail_storage *storage, const char *path)
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen{
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen mode_t mode;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen gid_t gid;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen mailbox_list_get_dir_permissions(storage->list, &mode, &gid);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (mkdir_parents_chown(path, mode, (uid_t)-1, gid) < 0 &&
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen errno != EEXIST) {
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen mail_storage_set_critical(storage,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen "mkdir(%s) failed: %m", path);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return -1;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return 0;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen}
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstatic const char *
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainendbox_get_alt_path(struct dbox_storage *storage, const char *path)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen{
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen const char *root;
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen unsigned int len;
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen if (storage->alt_dir == NULL)
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen return NULL;
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen root = mailbox_list_get_path(storage->storage.list, NULL,
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen MAILBOX_LIST_PATH_TYPE_DIR);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen len = strlen(root);
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen if (strncmp(path, root, len) != 0 && path[len] == '/') {
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen /* can't determine the alt path - shouldn't happen */
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen return NULL;
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return t_strconcat(storage->alt_dir, path + len, NULL);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen}
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainenstatic struct mailbox *
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainendbox_open(struct dbox_storage *storage, const char *name,
71da447014454c84828d9dface77219875554d7dTimo Sirainen enum mailbox_open_flags flags)
71da447014454c84828d9dface77219875554d7dTimo Sirainen{
71da447014454c84828d9dface77219875554d7dTimo Sirainen struct mail_storage *_storage = &storage->storage;
71da447014454c84828d9dface77219875554d7dTimo Sirainen struct dbox_mailbox *mbox;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct mail_index *index;
const char *path, *value;
pool_t pool;
path = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
index = index_storage_alloc(_storage, name, flags, DBOX_INDEX_PREFIX);
mail_index_set_fsync_types(index, MAIL_INDEX_SYNC_TYPE_APPEND |
MAIL_INDEX_SYNC_TYPE_EXPUNGE);
pool = pool_alloconly_create("dbox mailbox", 1024+512);
mbox = p_new(pool, struct dbox_mailbox, 1);
mbox->ibox.box = dbox_mailbox;
mbox->ibox.box.pool = pool;
mbox->ibox.storage = &storage->storage;
mbox->ibox.mail_vfuncs = &dbox_mail_vfuncs;
mbox->ibox.index = index;
mbox->path = p_strdup(pool, path);
mbox->alt_path = p_strdup(pool, dbox_get_alt_path(storage, path));
mbox->storage = storage;
mbox->last_interactive_change = ioloop_time;
value = getenv("DBOX_ROTATE_SIZE");
if (value != NULL)
mbox->rotate_size = (uoff_t)strtoul(value, NULL, 10) * 1024;
else
mbox->rotate_size = DBOX_DEFAULT_ROTATE_SIZE;
mbox->rotate_size = 0; /* FIXME: currently anything else doesn't work */
value = getenv("DBOX_ROTATE_MIN_SIZE");
if (value != NULL)
mbox->rotate_min_size = (uoff_t)strtoul(value, NULL, 10) * 1024;
else
mbox->rotate_min_size = DBOX_DEFAULT_ROTATE_MIN_SIZE;
if (mbox->rotate_min_size > mbox->rotate_size)
mbox->rotate_min_size = mbox->rotate_size;
value = getenv("DBOX_ROTATE_DAYS");
if (value != NULL)
mbox->rotate_days = (unsigned int)strtoul(value, NULL, 10);
else
mbox->rotate_days = DBOX_DEFAULT_ROTATE_DAYS;
value = getenv("DBOX_MAX_OPEN_FILES");
if (value != NULL)
mbox->max_open_files = (unsigned int)strtoul(value, NULL, 10);
else
mbox->max_open_files = DBOX_DEFAULT_MAX_OPEN_FILES;
i_array_init(&mbox->open_files, I_MIN(mbox->max_open_files, 128));
mbox->dbox_ext_id =
mail_index_ext_register(index, "dbox", 0,
sizeof(struct dbox_mail_index_record),
sizeof(uint32_t));
mbox->dbox_hdr_ext_id =
mail_index_ext_register(index, "dbox-hdr",
sizeof(struct dbox_index_header), 0, 0);
mbox->dbox_index = dbox_index_init(mbox);
index_storage_mailbox_init(&mbox->ibox, name, flags, FALSE);
return &mbox->ibox.box;
}
static bool
dbox_cleanup_if_exists(struct mail_storage *storage, const char *path)
{
struct stat st;
if (stat(path, &st) < 0)
return FALSE;
/* check once in a while if there are temp files to clean up */
if (st.st_atime > st.st_ctime + DBOX_TMP_DELETE_SECS) {
/* there haven't been any changes to this directory since we
last checked it. */
} else if (st.st_atime < ioloop_time - DBOX_TMP_SCAN_SECS) {
/* time to scan */
const char *prefix =
mailbox_list_get_global_temp_prefix(storage->list);
(void)unlink_old_files(path, prefix,
ioloop_time - DBOX_TMP_DELETE_SECS);
}
return TRUE;
}
static struct mailbox *
dbox_mailbox_open(struct mail_storage *_storage, const char *name,
struct istream *input, enum mailbox_open_flags flags)
{
struct dbox_storage *storage = (struct dbox_storage *)_storage;
const char *path;
if (input != NULL) {
mail_storage_set_critical(_storage,
"dbox doesn't support streamed mailboxes");
return NULL;
}
path = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (dbox_cleanup_if_exists(_storage, path))
return dbox_open(storage, name, flags);
else if (errno == ENOENT) {
if (strcmp(name, "INBOX") == 0) {
/* INBOX always exists, create it */
if (create_dbox(_storage, path) < 0)
return NULL;
return dbox_open(storage, name, flags);
}
mail_storage_set_error(_storage, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
} else if (errno == EACCES) {
mail_storage_set_critical(_storage, "%s",
mail_storage_eacces_msg("stat", path));
} else {
mail_storage_set_critical(_storage, "stat(%s) failed: %m",
path);
}
return NULL;
}
static int dbox_storage_mailbox_close(struct mailbox *box)
{
struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
int ret = 0;
if (box->opened) {
/* see if we want to flush dirty flags */
ret = dbox_sync(mbox, TRUE);
}
dbox_index_deinit(&mbox->dbox_index);
dbox_files_free(mbox);
array_free(&mbox->open_files);
return index_storage_mailbox_close(box) < 0 ? -1 : ret;
}
static int dbox_mailbox_create(struct mail_storage *_storage,
const char *name, bool directory)
{
struct dbox_storage *storage = (struct dbox_storage *)_storage;
const char *path, *alt_path;
struct stat st;
path = mailbox_list_get_path(_storage->list, name,
directory ? MAILBOX_LIST_PATH_TYPE_DIR :
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (stat(path, &st) == 0) {
mail_storage_set_error(_storage, MAIL_ERROR_NOTPOSSIBLE,
"Mailbox already exists");
return -1;
}
/* make sure the alt path doesn't exist yet. it shouldn't (except with
race conditions with RENAME/DELETE), but if something crashed and
left it lying around we don't want to start overwriting files in
it. */
alt_path = directory ? NULL : dbox_get_alt_path(storage, path);
if (alt_path != NULL && stat(alt_path, &st) == 0) {
mail_storage_set_error(_storage, MAIL_ERROR_NOTPOSSIBLE,
"Mailbox already exists");
return -1;
}
return create_dbox(_storage, path);
}
static int
dbox_delete_nonrecursive(struct mailbox_list *list, const char *path,
const char *name)
{
DIR *dir;
struct dirent *d;
string_t *full_path;
unsigned int dir_len;
bool unlinked_something = FALSE;
dir = opendir(path);
if (dir == NULL) {
if (errno == ENOENT)
return 0;
if (!mailbox_list_set_error_from_errno(list)) {
mailbox_list_set_critical(list,
"opendir(%s) failed: %m", path);
}
return -1;
}
full_path = t_str_new(256);
str_append(full_path, path);
str_append_c(full_path, '/');
dir_len = str_len(full_path);
errno = 0;
while ((d = readdir(dir)) != NULL) {
if (d->d_name[0] == '.') {
/* skip . and .. */
if (d->d_name[1] == '\0')
continue;
if (d->d_name[1] == '.' && d->d_name[2] == '\0')
continue;
}
str_truncate(full_path, dir_len);
str_append(full_path, d->d_name);
/* trying to unlink() a directory gives either EPERM or EISDIR
(non-POSIX). it doesn't really work anywhere in practise,
so don't bother stat()ing the file first */
if (unlink(str_c(full_path)) == 0)
unlinked_something = TRUE;
else if (errno != ENOENT && errno != EISDIR && errno != EPERM) {
mailbox_list_set_critical(list, "unlink(%s) failed: %m",
str_c(full_path));
}
}
if (closedir(dir) < 0) {
mailbox_list_set_critical(list, "closedir(%s) failed: %m",
path);
}
if (rmdir(path) == 0)
unlinked_something = TRUE;
else if (errno != ENOENT && errno != ENOTEMPTY) {
mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path);
return -1;
}
if (!unlinked_something) {
mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
t_strdup_printf("Directory %s isn't empty, "
"can't delete it.", name));
return -1;
}
return 1;
}
static int
dbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
{
struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
struct stat st;
const char *path, *alt_path;
bool deleted = FALSE;
int ret;
/* Make sure the indexes are closed before trying to delete the
directory that contains them. It can still fail with some NFS
implementations if indexes are opened by another session, but
that can't really be helped. */
index_storage_destroy_unrefed();
/* delete the index and control directories */
if (storage->list_module_ctx.super.delete_mailbox(list, name) < 0)
return -1;
/* check if the mailbox actually exists */
path = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if ((ret = dbox_delete_nonrecursive(list, path, name)) > 0) {
/* delete the mailbox first */
alt_path = dbox_get_alt_path(storage, path);
if (alt_path != NULL) {
if (dbox_delete_nonrecursive(list, alt_path, name) < 0)
return -1;
}
/* try to delete the directory also */
deleted = TRUE;
path = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_DIR);
} else if (errno != ENOENT) {
mailbox_list_set_critical(list, "stat(%s) failed: %m", path);
return -1;
} else {
/* mailbox not found - what about the directory? */
path = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_DIR);
if (stat(path, &st) == 0) {
/* delete the directory */
} else if (errno == ENOENT) {
mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
return -1;
} else if (!mailbox_list_set_error_from_errno(list)) {
mailbox_list_set_critical(list, "stat(%s) failed: %m",
path);
return -1;
}
}
alt_path = dbox_get_alt_path(storage, path);
if (alt_path != NULL)
(void)rmdir(alt_path);
if (rmdir(path) == 0)
return 0;
else if (errno == ENOTEMPTY) {
if (deleted)
return 0;
mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
t_strdup_printf("Directory %s isn't empty, "
"can't delete it.", name));
} else if (!mailbox_list_set_error_from_errno(list)) {
mailbox_list_set_critical(list, "rmdir() failed for %s: %m",
path);
}
return -1;
}
static bool
dbox_list_rename_get_alt_paths(struct mailbox_list *list,
const char *oldname, const char *newname,
const char **oldpath_r, const char **newpath_r)
{
struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
const char *path;
if (storage->alt_dir == NULL)
return FALSE;
path = mailbox_list_get_path(list, oldname, MAILBOX_LIST_PATH_TYPE_DIR);
*oldpath_r = dbox_get_alt_path(storage, path);
if (*oldpath_r == NULL)
return FALSE;
path = mailbox_list_get_path(list, newname, MAILBOX_LIST_PATH_TYPE_DIR);
*newpath_r = dbox_get_alt_path(storage, path);
i_assert(*newpath_r != NULL);
return TRUE;
}
static int
dbox_list_rename_mailbox_pre(struct mailbox_list *list,
const char *oldname, const char *newname)
{
const char *alt_oldpath, *alt_newpath;
struct stat st;
if (!dbox_list_rename_get_alt_paths(list, oldname, newname,
&alt_oldpath, &alt_newpath))
return 0;
if (stat(alt_newpath, &st) == 0) {
/* race condition or a directory left there lying around?
safest to just report error. */
mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
"Target mailbox already exists");
return -1;
} else if (errno != ENOENT) {
mailbox_list_set_critical(list, "stat(%s) failed: %m",
alt_newpath);
return -1;
}
return 0;
}
static int
dbox_list_rename_mailbox(struct mailbox_list *list,
const char *oldname, const char *newname)
{
struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
const char *alt_oldpath, *alt_newpath;
if (storage->list_module_ctx.super.rename_mailbox(list, oldname,
newname) < 0)
return -1;
if (!dbox_list_rename_get_alt_paths(list, oldname, newname,
&alt_oldpath, &alt_newpath))
return 0;
if (rename(alt_oldpath, alt_newpath) == 0) {
/* ok */
} else if (errno != ENOENT) {
/* renaming is done already, so just log the error */
mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
alt_oldpath, alt_newpath);
}
return 0;
}
static void dbox_notify_changes(struct mailbox *box)
{
struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
const char *path;
if (box->notify_callback == NULL)
index_mailbox_check_remove_all(&mbox->ibox);
else {
path = t_strdup_printf("%s/"DBOX_INDEX_PREFIX".log",
mbox->path);
index_mailbox_check_add(&mbox->ibox, path);
}
}
static int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx
ATTR_UNUSED,
const char *dir, const char *fname,
const char *mailbox_name ATTR_UNUSED,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags)
{
const char *path, *maildir_path;
struct stat st, st2;
int ret = 1;
/* try to avoid stat() with these checks */
if (type != MAILBOX_LIST_FILE_TYPE_DIR &&
type != MAILBOX_LIST_FILE_TYPE_SYMLINK &&
type != MAILBOX_LIST_FILE_TYPE_UNKNOWN) {
/* it's a file */
*flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
return 0;
}
/* need to stat() then */
path = t_strconcat(dir, "/", fname, NULL);
if (stat(path, &st) == 0) {
if (!S_ISDIR(st.st_mode)) {
/* non-directory */
*flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
ret = 0;
} else if (st.st_nlink == 2) {
/* no subdirectories */
*flags |= MAILBOX_NOCHILDREN;
} else if (*ctx->list->set.maildir_name != '\0') {
/* default configuration: we have one directory
containing the mailboxes. if there are 3 links,
either this is a selectable mailbox without children
or non-selectable mailbox with children */
if (st.st_nlink > 3)
*flags |= MAILBOX_CHILDREN;
} else {
/* non-default configuration: all subdirectories are
child mailboxes. */
if (st.st_nlink > 2)
*flags |= MAILBOX_CHILDREN;
}
} else if (errno == ENOENT) {
/* doesn't exist - probably a non-existing subscribed mailbox */
*flags |= MAILBOX_NONEXISTENT;
} else {
/* non-selectable. probably either access denied, or symlink
destination not found. don't bother logging errors. */
*flags |= MAILBOX_NOSELECT;
}
if ((*flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) {
/* make sure it's a selectable mailbox */
maildir_path = t_strconcat(path, "/"DBOX_MAILDIR_NAME, NULL);
if (stat(maildir_path, &st2) < 0 || !S_ISDIR(st2.st_mode))
*flags |= MAILBOX_NOSELECT;
if (st.st_nlink == 3 && *ctx->list->set.maildir_name != '\0') {
/* now we know what link count 3 means. */
if ((*flags & MAILBOX_NOSELECT) != 0)
*flags |= MAILBOX_CHILDREN;
else
*flags |= MAILBOX_NOCHILDREN;
}
}
return ret;
}
static void dbox_class_init(void)
{
dbox_transaction_class_init();
}
static void dbox_class_deinit(void)
{
dbox_transaction_class_deinit();
}
struct mail_storage dbox_storage = {
MEMBER(name) DBOX_STORAGE_NAME,
MEMBER(mailbox_is_file) FALSE,
{
dbox_class_init,
dbox_class_deinit,
dbox_alloc,
dbox_create,
index_storage_destroy,
NULL,
dbox_mailbox_open,
dbox_mailbox_create
}
};
struct mailbox dbox_mailbox = {
MEMBER(name) NULL,
MEMBER(storage) NULL,
{
index_storage_is_readonly,
index_storage_allow_new_keywords,
index_storage_mailbox_enable,
dbox_storage_mailbox_close,
index_storage_get_status,
NULL,
NULL,
dbox_storage_sync_init,
index_mailbox_sync_next,
index_mailbox_sync_deinit,
NULL,
dbox_notify_changes,
index_transaction_begin,
index_transaction_commit,
index_transaction_rollback,
index_transaction_set_max_modseq,
index_keywords_create,
index_keywords_free,
index_keyword_is_valid,
index_storage_get_seq_range,
index_storage_get_uid_range,
index_storage_get_expunged_uids,
dbox_mail_alloc,
index_header_lookup_init,
index_header_lookup_ref,
index_header_lookup_unref,
index_storage_search_init,
index_storage_search_deinit,
index_storage_search_next_nonblock,
index_storage_search_next_update_seq,
dbox_save_alloc,
dbox_save_begin,
dbox_save_continue,
dbox_save_finish,
dbox_save_cancel,
mail_storage_copy,
index_storage_is_inconsistent
}
};