Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-storage/index/mbox/mbox-storage.c @ 296:d66aa1f1fb2d HEAD
Added fast-flag for mailbox opening, which doesn't do any index compressing
or cache updating. This flag is set when mailbox is opened by APPEND, COPY
or STATUS (ie. not SELECT/EXAMINE).
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 23 Sep 2002 13:42:20 +0300 |
parents | 95d21ab87eeb |
children | 21a9d15025c7 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" #include "unlink-directory.h" #include "subscription-file/subscription-file.h" #include "mail-custom-flags.h" #include "mbox-index.h" #include "mbox-storage.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #define CREATE_MODE 0770 /* umask() should limit it more */ extern MailStorage mbox_storage; static Mailbox mbox_mailbox; static int mbox_autodetect(const char *data) { const char *path; struct stat st; path = t_strconcat(data, "/.imap", NULL); if (stat(path, &st) == 0 && S_ISDIR(st.st_mode) && access(path, R_OK|W_OK|X_OK) == 0) return TRUE; path = t_strconcat(data, "/inbox", NULL); if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode) && access(path, R_OK|W_OK) == 0) return TRUE; path = t_strconcat(data, "/mbox", NULL); if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode) && access(path, R_OK|W_OK) == 0) return TRUE; return FALSE; } static MailStorage *mbox_create(const char *data, const char *user) { MailStorage *storage; const char *home, *path; if (data == NULL || *data == '\0') { /* we'll need to figure out the mail location ourself. it's root dir if we've already chroot()ed, otherwise either $HOME/mail or $HOME/Mail */ if (mbox_autodetect("")) data = "/"; else { home = getenv("HOME"); if (home != NULL) { path = t_strconcat(home, "/mail", NULL); if (access(path, R_OK|W_OK|X_OK) == 0) data = path; else { path = t_strconcat(home, "/Mail", NULL); if (access(path, R_OK|W_OK|X_OK) == 0) data = path; } } } } if (data == NULL) return NULL; storage = i_new(MailStorage, 1); memcpy(storage, &mbox_storage, sizeof(MailStorage)); storage->dir = i_strdup(data); storage->user = i_strdup(user); return storage; } static void mbox_free(MailStorage *storage) { i_free(storage->dir); i_free(storage->user); i_free(storage); } static int mbox_is_valid_name(MailStorage *storage, const char *name) { const char *p; int newdir; if (name[0] == '\0' || name[0] == storage->hierarchy_sep) return FALSE; /* make sure there's no "../" or "..\" stuff */ newdir = TRUE; for (p = name; *p != '\0'; p++) { if (newdir && p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\\')) return FALSE; newdir = p[0] == '/' || p[0] == '\\'; } return TRUE; } static const char *mbox_get_index_dir(const char *mbox_path) { const char *p, *rootpath; p = strrchr(mbox_path, '/'); if (p == NULL) return t_strconcat(".imap/", mbox_path); else { rootpath = t_strdup_until(mbox_path, p); return t_strconcat(rootpath, "/.imap/", p+1, NULL); } } static int create_mbox_index_dirs(const char *mbox_path, int verify) { const char *index_dir, *imap_dir; index_dir = mbox_get_index_dir(mbox_path); imap_dir = t_strdup_until(index_dir, strstr(index_dir, ".imap/") + 5); if (mkdir(imap_dir, CREATE_MODE) == -1 && errno != EEXIST) return FALSE; if (mkdir(index_dir, CREATE_MODE) == -1 && (errno != EEXIST || !verify)) return FALSE; return TRUE; } static void verify_inbox(MailStorage *storage) { char path[1024]; int fd; i_snprintf(path, sizeof(path), "%s/inbox", storage->dir); /* make sure inbox file itself exists */ fd = open(path, O_RDWR | O_CREAT | O_EXCL); if (fd != -1) (void)close(fd); /* make sure the index directories exist */ (void)create_mbox_index_dirs(path, TRUE); } static Mailbox *mbox_open(MailStorage *storage, const char *name, int readonly, int fast) { IndexMailbox *ibox; const char *path, *index_dir; /* name = "foo/bar" mbox_path = "/mail/foo/bar" index_dir = "/mail/foo/.imap/bar" */ path = t_strconcat(storage->dir, "/", name, NULL); index_dir = mbox_get_index_dir(path); ibox = index_storage_init(storage, &mbox_mailbox, mbox_index_alloc(index_dir, path), name, readonly, fast); if (ibox != NULL) ibox->expunge_locked = mbox_expunge_locked; return (Mailbox *) ibox; } static Mailbox *mbox_open_mailbox(MailStorage *storage, const char *name, int readonly, int fast) { struct stat st; char path[1024]; mail_storage_clear_error(storage); /* INBOX is always case-insensitive */ if (strcasecmp(name, "INBOX") == 0) { /* make sure inbox exists */ verify_inbox(storage); return mbox_open(storage, "inbox", readonly, fast); } if (!mbox_is_valid_name(storage, name)) { mail_storage_set_error(storage, "Invalid mailbox name"); return FALSE; } i_snprintf(path, sizeof(path), "%s/%s", storage->dir, name); if (stat(path, &st) == 0) { /* exists - make sure the required directories are also there */ (void)create_mbox_index_dirs(path, TRUE); return mbox_open(storage, name, readonly, fast); } else if (errno == ENOENT) { mail_storage_set_error(storage, "Mailbox doesn't exist: %s", name); return NULL; } else { mail_storage_set_critical(storage, "Can't open mailbox %s: %m", name); return NULL; } } static int mbox_create_mailbox(MailStorage *storage, const char *name) { struct stat st; char path[1024]; int fd; mail_storage_clear_error(storage); if (strcasecmp(name, "INBOX") == 0) name = "inbox"; if (!mbox_is_valid_name(storage, name)) { mail_storage_set_error(storage, "Invalid mailbox name"); return FALSE; } /* make sure it doesn't exist already */ i_snprintf(path, sizeof(path), "%s/%s", storage->dir, name); if (stat(path, &st) == 0) { mail_storage_set_error(storage, "Mailbox already exists"); return FALSE; } if (errno != EEXIST) { mail_storage_set_critical(storage, "stat() failed for mbox " "file %s: %m", path); return FALSE; } /* create the mailbox file */ fd = open(path, O_RDWR | O_CREAT | O_EXCL); if (fd != -1) { (void)close(fd); return TRUE; } else if (errno == EEXIST) { /* mailbox was just created between stat() and open() call.. */ mail_storage_set_error(storage, "Mailbox already exists"); return FALSE; } else { mail_storage_set_critical(storage, "Can't create mailbox " "%s: %m", name); return FALSE; } } static int mbox_delete_mailbox(MailStorage *storage, const char *name) { const char *index_dir; char path[1024]; mail_storage_clear_error(storage); if (strcasecmp(name, "INBOX") == 0) { mail_storage_set_error(storage, "INBOX can't be deleted."); return FALSE; } if (!mbox_is_valid_name(storage, name)) { mail_storage_set_error(storage, "Invalid mailbox name"); return FALSE; } /* first unlink the mbox file */ i_snprintf(path, sizeof(path), "%s/%s", storage->dir, name); if (unlink(path) == -1) { if (errno == ENOENT) { mail_storage_set_error(storage, "Mailbox doesn't exist: %s", name); } else { mail_storage_set_critical(storage, "Can't delete mbox " "file %s: %m", path); } return FALSE; } /* next delete the index directory */ index_dir = mbox_get_index_dir(path); if (!unlink_directory(index_dir)) { mail_storage_set_critical(storage, "unlink_directory(%s) " "failed: %m", index_dir); return FALSE; } return TRUE; } static int mbox_rename_mailbox(MailStorage *storage, const char *oldname, const char *newname) { const char *old_indexdir, *new_indexdir; char oldpath[1024], newpath[1024]; mail_storage_clear_error(storage); if (!mbox_is_valid_name(storage, oldname) || !mbox_is_valid_name(storage, newname)) { mail_storage_set_error(storage, "Invalid mailbox name"); return FALSE; } if (strcasecmp(oldname, "INBOX") == 0) oldname = "inbox"; /* NOTE: renaming INBOX works just fine with us, it's simply created the next time it's needed. */ i_snprintf(oldpath, sizeof(oldpath), "%s/%s", storage->dir, oldname); i_snprintf(newpath, sizeof(newpath), "%s/%s", storage->dir, newname); if (link(oldpath, newpath) == 0) { (void)unlink(oldpath); /* ... */ } else if (errno == EEXIST) { mail_storage_set_error(storage, "Target mailbox already exists"); return FALSE; } else { mail_storage_set_critical(storage, "link(%s, %s) failed: %m", oldpath, newpath); return FALSE; } /* we need to rename the index directory as well */ old_indexdir = mbox_get_index_dir(oldpath); new_indexdir = mbox_get_index_dir(newpath); (void)rename(old_indexdir, new_indexdir); return TRUE; } static int mbox_get_mailbox_name_status(MailStorage *storage, const char *name, MailboxNameStatus *status) { struct stat st; char path[1024]; mail_storage_clear_error(storage); if (strcasecmp(name, "INBOX") == 0) name = "inbox"; if (!mbox_is_valid_name(storage, name)) { *status = MAILBOX_NAME_INVALID; return TRUE; } i_snprintf(path, sizeof(path), "%s/%s", storage->dir, name); if (stat(path, &st) == 0) { *status = MAILBOX_NAME_EXISTS; return TRUE; } else if (errno == ENOENT) { *status = MAILBOX_NAME_VALID; return TRUE; } else { mail_storage_set_critical(storage, "mailbox name status: " "stat(%s) failed: %m", path); return FALSE; } } static void mbox_storage_close(Mailbox *box) { IndexMailbox *ibox = (IndexMailbox *) box; if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_EXCLUSIVE)) mail_storage_set_index_error(ibox); else { /* update flags by rewrite mbox file */ mbox_index_rewrite(ibox->index); (void)ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK); } index_storage_close(box); } MailStorage mbox_storage = { "mbox", /* name */ '/', /* hierarchy_sep - can't be changed */ mbox_create, mbox_free, mbox_autodetect, mbox_open_mailbox, mbox_create_mailbox, mbox_delete_mailbox, mbox_rename_mailbox, mbox_find_mailboxes, subsfile_set_subscribed, mbox_find_subscribed, mbox_get_mailbox_name_status, mail_storage_get_last_error, NULL, NULL, NULL }; static Mailbox mbox_mailbox = { NULL, /* name */ NULL, /* storage */ mbox_storage_close, index_storage_get_status, index_storage_sync, index_storage_expunge, index_storage_update_flags, index_storage_copy, index_storage_fetch, index_storage_search, mbox_storage_save, mail_storage_is_inconsistency_error, FALSE, FALSE };