changeset 815:ef64c9a50326 HEAD

Support for separate location of mbox INBOX folder.
author Timo Sirainen <tss@iki.fi>
date Fri, 20 Dec 2002 08:00:06 +0200
parents a6d24244c23f
children ae9b55f7164e
files doc/mail-storages.txt dovecot-example.conf src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/mbox/mbox-list.c src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/mail-storage.h
diffstat 6 files changed, 127 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- a/doc/mail-storages.txt	Fri Dec 20 07:01:09 2002 +0200
+++ b/doc/mail-storages.txt	Fri Dec 20 08:00:06 2002 +0200
@@ -16,14 +16,18 @@
 mbox
 ----
 
-mbox storage currently requires that all mail is in user-writable
-directory, eg. ~/mail. Especially using /var/mail/user is NOT supported.
-You may however create a ~/mail/inbox hardlink (or symlink if not chrooted)
-to pointing to /var/mail/user. All files in the mail directory are
-considered as mailboxes. Directories specify subfolders in IMAP. "inbox"
-file specifies the INBOX folder and can not be named otherwise. Indexes are
-stored into ".imap/<mailbox name>/" directories.
+mbox storage requires an IMAP root directory where to store IMAP folders
+and some other information. mbox file for INBOX is a special case which may
+exist elsewhere in filesystem.
 
+All files in the IMAP root directory not beginning with a "." are treated
+as IMAP folders. Directories are treated as being IMAP subfolders.
+
+Dovecot's indexes are stored in ".imap/<mailbox name>/" directories under
+the folder directories.
+
+MAIL environment for mbox is: <root folder>|<INBOX path>[:INBOX=<path>].
+For example ~/Mail:INBOX=/var/mail/username.
 
 Detecting what to use
 ---------------------
@@ -44,9 +48,12 @@
 we have rwx access to it. If directory isn't known, / and ~/Maildir are
 checked. / is checked because we could be chrooted.
 
-mbox storage is autodetected by checking if .imap/ (+rwx), inbox (+rw) or
-mbox (+rw) exists in directory. If directory isn't known, / is tried first.
-After that, ~/mail and ~/Mail directories are used.
+mbox storage requires detecting INBOX location and IMAP root directory. If
+MAIL environment points to file (+rw) it's treated as the INBOX file. If it
+points to directory (+rwx) which contains .imap/ (+rwx), inbox (+rw) or
+mbox (+rw) it's treated as IMAP root directory. If INBOX isn't known,
+imap_root/inbox file is used by default. If root directory isn't known, we
+try to find it from /, ~/mail or ~/Mail.
 
 
 Files created by Dovecot
--- a/dovecot-example.conf	Fri Dec 20 07:01:09 2002 +0200
+++ b/dovecot-example.conf	Fri Dec 20 08:00:06 2002 +0200
@@ -153,7 +153,8 @@
 # username. Some examples:
 #
 #   maildir:/var/mail/%1u/%u/Maildir
-#   mbox:~/mail/
+#   mbox:~/mail/:INBOX=/var/mail/%u
+#   mbox:/var/mail/%d/%n/
 #
 #default_mail_env = 
 
--- a/src/lib-storage/index/maildir/maildir-storage.c	Fri Dec 20 07:01:09 2002 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Fri Dec 20 08:00:06 2002 +0200
@@ -518,6 +518,7 @@
 	NULL,
 	NULL,
 	NULL,
+	NULL,
 	NULL, NULL
 };
 
--- a/src/lib-storage/index/mbox/mbox-list.c	Fri Dec 20 07:01:09 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-list.c	Fri Dec 20 08:00:06 2002 +0200
@@ -17,7 +17,7 @@
 
 static int mbox_find_path(MailStorage *storage, ImapMatchGlob *glob,
 			  MailboxFunc func, void *context,
-			  const char *relative_dir, int *found_inbox)
+			  const char *relative_dir)
 {
 	DIR *dirp;
 	struct dirent *d;
@@ -113,15 +113,12 @@
 			func(storage, listpath, MAILBOX_NOSELECT, context);
 
 			if (!mbox_find_path(storage, glob, func,
-					    context, listpath, NULL)) {
+					    context, listpath)) {
 				failed = TRUE;
 				break;
 			}
-		} else if (match > 0) {
-			if (found_inbox != NULL &&
-			    strcasecmp(listpath, "inbox") == 0)
-				*found_inbox = TRUE;
-
+		} else if (match > 0 &&
+			   strcmp(fullpath, storage->inbox_file) != 0) {
 			func(storage, listpath, MAILBOX_NOINFERIORS, context);
 		}
 	}
@@ -150,7 +147,6 @@
 {
 	ImapMatchGlob *glob;
 	const char *relative_dir;
-	int found_inbox;
 
 	/* check that we're not trying to do any "../../" lists */
 	if (!mbox_is_valid_mask(mask)) {
@@ -165,18 +161,13 @@
 	relative_dir = mask_get_dir(mask);
 
 	glob = imap_match_init(mask, TRUE, '/');
-
-	found_inbox = FALSE;
-	if (!mbox_find_path(storage, glob, func, context,
-			    relative_dir, &found_inbox))
-		return FALSE;
+	if (relative_dir == NULL && imap_match(glob, "INBOX") > 0) {
+		/* INBOX exists always, even if the file doesn't. */
+		func(storage, "INBOX", MAILBOX_NOINFERIORS, context);
+	}
 
-	if (!found_inbox && relative_dir == NULL &&
-	    imap_match(glob, "INBOX") > 0) {
-		/* INBOX always exists, even if the file doesn't. */
-		func(storage, "INBOX", MAILBOX_UNMARKED | MAILBOX_NOINFERIORS,
-		     context);
-	}
+	if (!mbox_find_path(storage, glob, func, context, relative_dir))
+		return FALSE;
 
 	return TRUE;
 }
--- a/src/lib-storage/index/mbox/mbox-storage.c	Fri Dec 20 07:01:09 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Fri Dec 20 08:00:06 2002 +0200
@@ -24,7 +24,13 @@
 	const char *path;
 	struct stat st;
 
-        path = t_strconcat(data, "/.imap", NULL);
+	/* Is it INBOX file? */
+	if (*data != '\0' && stat(data, &st) == 0 && !S_ISDIR(st.st_mode) &&
+	    access(data, R_OK|W_OK) == 0)
+		return TRUE;
+
+	/* or directory for IMAP folders? */
+	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;
@@ -42,39 +48,69 @@
 	return FALSE;
 }
 
+static const char *get_root_dir(void)
+{
+	const char *home, *path;
+
+	if (mbox_autodetect(""))
+		return "/";
+
+	home = getenv("HOME");
+	if (home != NULL) {
+		path = t_strconcat(home, "/mail", NULL);
+		if (access(path, R_OK|W_OK|X_OK) == 0)
+			return path;
+
+		path = t_strconcat(home, "/Mail", NULL);
+		if (access(path, R_OK|W_OK|X_OK) == 0)
+			return path;
+	}
+
+	return NULL;
+}
+
 static MailStorage *mbox_create(const char *data, const char *user)
 {
 	MailStorage *storage;
-	const char *home, *path;
+	const char *root_dir, *inbox_file, *p;
+	struct stat st;
+
+	root_dir = inbox_file = NULL;
 
 	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;
-				}
+		root_dir = get_root_dir();
+	} else {
+		/* <root folder> | <INBOX path> [:INBOX=<path>]
+		   [:<reserved for future>] */
+		p = strchr(data, ':');
+		if (p == NULL) {
+			if (stat(data, &st) == 0 && S_ISDIR(st.st_mode))
+				root_dir = data;
+			else {
+				root_dir = get_root_dir();
+				inbox_file = data;
 			}
+		} else {
+			root_dir = t_strdup_until(data, p);
+			if (strncmp(p+1, "INBOX=", 6) == 0)
+				inbox_file = t_strcut(p+7, ':');
 		}
 	}
 
-	if (data == NULL)
+	if (root_dir == NULL)
 		return NULL;
 
+	if (inbox_file == NULL)
+		inbox_file = t_strconcat(root_dir, "/inbox", NULL);
+
 	storage = i_new(MailStorage, 1);
 	memcpy(storage, &mbox_storage, sizeof(MailStorage));
 
-	storage->dir = i_strdup(data);
+	storage->dir = i_strdup(root_dir);
+	storage->inbox_file = i_strdup(inbox_file);
 	storage->user = i_strdup(user);
 	storage->callbacks = i_new(MailStorageCallbacks, 1);
 	return storage;
@@ -143,19 +179,25 @@
 
 static void verify_inbox(MailStorage *storage)
 {
-	char path[PATH_MAX];
+	const char *index_dir;
 	int fd;
 
-	if (str_path(path, sizeof(path), storage->dir, "inbox") < 0)
-		return;
-
 	/* make sure inbox file itself exists */
-	fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660);
+	fd = open(storage->inbox_file, O_RDWR | O_CREAT | O_EXCL, 0660);
 	if (fd != -1)
 		(void)close(fd);
 
 	/* make sure the index directories exist */
-	(void)create_mbox_index_dirs(path, TRUE);
+	index_dir = t_strconcat(storage->dir, "/INBOX", NULL);
+	(void)create_mbox_index_dirs(index_dir, TRUE);
+}
+
+static const char *mbox_get_path(MailStorage *storage, const char *name)
+{
+	if (strcasecmp(name, "INBOX") == 0)
+		return storage->inbox_file;
+	else
+		return t_strconcat(storage->dir, "/", name, NULL);
 }
 
 static Mailbox *mbox_open(MailStorage *storage, const char *name,
@@ -165,11 +207,20 @@
 	MailIndex *index;
 	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);
+	if (strcasecmp(name, "INBOX") == 0) {
+		/* name = "INBOX"
+		   path = "<inbox_file>/INBOX"
+		   index_dir = "/mail/.imap/INBOX" */
+		path = storage->inbox_file;
+		index_dir = mbox_get_index_dir(t_strconcat(storage->dir,
+							   "/INBOX", NULL));
+	} else {
+		/* name = "foo/bar"
+		   path = "/mail/foo/bar"
+		   index_dir = "/mail/foo/.imap/bar" */
+		path = mbox_get_path(storage, name);
+		index_dir = mbox_get_index_dir(path);
+	}
 
 	index = index_storage_lookup_ref(index_dir);
 	if (index == NULL) {
@@ -189,8 +240,8 @@
 static Mailbox *mbox_open_mailbox(MailStorage *storage, const char *name,
 				  int readonly, int fast)
 {
+	const char *path;
 	struct stat st;
-	char path[PATH_MAX];
 
 	mail_storage_clear_error(storage);
 
@@ -198,7 +249,7 @@
 	if (strcasecmp(name, "INBOX") == 0) {
 		/* make sure inbox exists */
 		verify_inbox(storage);
-		return mbox_open(storage, "inbox", readonly, fast);
+		return mbox_open(storage, "INBOX", readonly, fast);
 	}
 
 	if (!mbox_is_valid_name(storage, name)) {
@@ -206,8 +257,8 @@
 		return FALSE;
 	}
 
-	if (str_path(path, sizeof(path), storage->dir, name) == 0 &&
-	    stat(path, &st) == 0) {
+	path = mbox_get_path(storage, name);
+	if (stat(path, &st) == 0) {
 		/* exists - make sure the required directories are also there */
 		(void)create_mbox_index_dirs(path, TRUE);
 
@@ -225,14 +276,14 @@
 
 static int mbox_create_mailbox(MailStorage *storage, const char *name)
 {
+	const char *path;
 	struct stat st;
-	char path[PATH_MAX];
 	int fd;
 
 	mail_storage_clear_error(storage);
 
 	if (strcasecmp(name, "INBOX") == 0)
-		name = "inbox";
+		name = "INBOX";
 
 	if (!mbox_is_valid_name(storage, name)) {
 		mail_storage_set_error(storage, "Invalid mailbox name");
@@ -240,12 +291,7 @@
 	}
 
 	/* make sure it doesn't exist already */
-	if (str_path(path, sizeof(path), storage->dir, name) < 0) {
-		mail_storage_set_error(storage, "Mailbox name too long: %s",
-				       name);
-		return FALSE;
-	}
-
+	path = mbox_get_path(storage, name);
 	if (stat(path, &st) == 0) {
 		mail_storage_set_error(storage, "Mailbox already exists");
 		return FALSE;
@@ -275,8 +321,7 @@
 
 static int mbox_delete_mailbox(MailStorage *storage, const char *name)
 {
-	const char *index_dir;
-	char path[PATH_MAX];
+	const char *index_dir, *path;
 
 	mail_storage_clear_error(storage);
 
@@ -290,13 +335,8 @@
 		return FALSE;
 	}
 
-	if (str_path(path, sizeof(path), storage->dir, name) < 0) {
-		mail_storage_set_error(storage, "Mailbox name too long: %s",
-				       name);
-		return FALSE;
-	}
-
 	/* first unlink the mbox file */
+	path = mbox_get_path(storage, name);
 	if (unlink(path) == -1) {
 		if (errno == ENOENT) {
 			mail_storage_set_error(storage,
@@ -323,8 +363,7 @@
 static int mbox_rename_mailbox(MailStorage *storage, const char *oldname,
 			       const char *newname)
 {
-	const char *old_indexdir, *new_indexdir;
-	char oldpath[PATH_MAX], newpath[PATH_MAX];
+	const char *oldpath, *newpath, *old_indexdir, *new_indexdir;
 
 	mail_storage_clear_error(storage);
 
@@ -335,18 +374,10 @@
 	}
 
 	if (strcasecmp(oldname, "INBOX") == 0)
-		oldname = "inbox";
+		oldname = "INBOX";
 
-	if (str_path(oldpath, sizeof(oldpath), storage->dir, oldname) < 0) {
-		mail_storage_set_error(storage, "Mailbox name too long: %s",
-				       oldname);
-		return FALSE;
-	}
-	if (str_path(newpath, sizeof(newpath), storage->dir, newname) < 0) {
-		mail_storage_set_error(storage, "Mailbox name too long: %s",
-				       newname);
-		return FALSE;
-	}
+	oldpath = mbox_get_path(storage, oldname);
+	newpath = mbox_get_path(storage, newname);
 
 	/* NOTE: renaming INBOX works just fine with us, it's simply created
 	   the next time it's needed. */
@@ -374,24 +405,19 @@
 					MailboxNameStatus *status)
 {
 	struct stat st;
-	char path[PATH_MAX];
+	const char *path;
 
 	mail_storage_clear_error(storage);
 
 	if (strcasecmp(name, "INBOX") == 0)
-		name = "inbox";
+		name = "INBOX";
 
 	if (!mbox_is_valid_name(storage, name)) {
 		*status = MAILBOX_NAME_INVALID;
 		return TRUE;
 	}
 
-	if (str_path(path, sizeof(path), storage->dir, name) < 0) {
-		mail_storage_set_error(storage, "Mailbox name too long: %s",
-				       name);
-		return FALSE;
-	}
-
+	path = mbox_get_path(storage, name);
 	if (stat(path, &st) == 0) {
 		*status = MAILBOX_NAME_EXISTS;
 		return TRUE;
@@ -441,6 +467,7 @@
 	NULL,
 	NULL,
 	NULL,
+	NULL,
 	NULL, NULL
 };
 
--- a/src/lib-storage/mail-storage.h	Fri Dec 20 07:01:09 2002 +0200
+++ b/src/lib-storage/mail-storage.h	Fri Dec 20 08:00:06 2002 +0200
@@ -115,6 +115,7 @@
 
 /* private: */
 	char *dir; /* root directory */
+	char *inbox_file; /* INBOX file for mbox */
 	char *user; /* name of user accessing the storage */
 	char *error;