[dovecot-cvs] dovecot/src/lib-storage/index/mbox mbox-storage.c, 1.170, 1.171

tss at dovecot.org tss at dovecot.org
Thu Mar 29 10:59:25 EEST 2007


Update of /var/lib/cvs/dovecot/src/lib-storage/index/mbox
In directory talvi:/tmp/cvs-serv11000/lib-storage/index/mbox

Modified Files:
	mbox-storage.c 
Log Message:
Moved delete/rename operations to mailbox_list API. Fixed mbox/maildir to
work with either fs/maildir++ directory layout. They can be changed by
appending :LAYOUT=fs|maildir++ to mail_location.



Index: mbox-storage.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-storage/index/mbox/mbox-storage.c,v
retrieving revision 1.170
retrieving revision 1.171
diff -u -d -r1.170 -r1.171
--- mbox-storage.c	27 Mar 2007 19:59:31 -0000	1.170
+++ mbox-storage.c	29 Mar 2007 07:59:20 -0000	1.171
@@ -31,6 +31,10 @@
 	((st).st_size == 0 ? MAILBOX_UNMARKED : \
 	 (st).st_atime < (st).st_mtime ? MAILBOX_MARKED : MAILBOX_UNMARKED)
 
+#define MBOX_LIST_CONTEXT(obj) \
+	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
+					mbox_mailbox_list_module_id))
+
 /* NOTE: must be sorted for istream-header-filter. Note that it's not such
    a good idea to change this list, as the messages will then change from
    client's point of view. So if you do it, change all mailboxes' UIDVALIDITY
@@ -65,8 +69,14 @@
 extern struct mail_storage mbox_storage;
 extern struct mailbox mbox_mailbox;
 
-static bool mbox_mailbox_list_module_id_set = FALSE;
-static unsigned int mbox_mailbox_list_module_id;
+static unsigned int mbox_mailbox_list_module_id = 0;
+
+static int mbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+				     const char *dir, const char *fname,
+				     enum mailbox_list_file_type type,
+				     enum mailbox_info_flags *flags);
+static int mbox_list_delete_mailbox(struct mailbox_list *list,
+				    const char *name);
 
 int mbox_set_syscall_error(struct mbox_mailbox *mbox, const char *function)
 {
@@ -83,21 +93,6 @@
 	return -1;
 }
 
-static bool mbox_handle_errors(struct index_storage *istorage)
-{
-	struct mail_storage *storage = &istorage->storage;
-
-	if (ENOACCESS(errno))
-		mail_storage_set_error(storage, MAIL_STORAGE_ERR_NO_PERMISSION);
-	else if (ENOSPACE(errno))
-		mail_storage_set_error(storage, "Not enough disk space");
-	else if (ENOTFOUND(errno))
-		mail_storage_set_error(storage, "Directory structure is broken");
-	else
-		return FALSE;
-	return TRUE;
-}
-
 static bool mbox_is_file(const char *path, const char *name, bool debug)
 {
 	struct stat st;
@@ -282,13 +277,16 @@
 
 static int
 mbox_get_list_settings(struct mailbox_list_settings *list_set,
-		       const char *data, enum mail_storage_flags flags)
+		       const char *data, enum mail_storage_flags flags,
+		       const char **layout_r)
 {
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 	const char *p;
 	struct stat st;
 	bool autodetect;
 
+	*layout_r = "fs";
+
 	memset(list_set, 0, sizeof(*list_set));
 	list_set->subscription_fname = MBOX_SUBSCRIPTION_FILE_NAME;
 	list_set->maildir_name = "";
@@ -329,6 +327,8 @@
 				} else if (strncmp(p, "INDEX=", 6) == 0) {
 					list_set->index_dir =
 						t_strcut(p+6, ':');
+				} else if (strncmp(p, "LAYOUT=", 7) == 0) {
+					*layout_r = t_strcut(p+7, ':');
 				}
 				p = strchr(p, ':');
 			} while (p != NULL);
@@ -384,9 +384,7 @@
 mbox_list_get_path(struct mailbox_list *list, const char *name,
 		   enum mailbox_list_path_type type)
 {
-	struct mbox_storage *storage =
-		*((void **)array_idx_modifiable(&list->module_contexts,
-						mbox_mailbox_list_module_id));
+	struct mbox_storage *storage = MBOX_LIST_CONTEXT(list);
 	const char *path, *p;
 
 	path = storage->list_super.get_path(list, name, type);
@@ -410,10 +408,10 @@
 	struct index_storage *istorage;
 	struct mailbox_list_settings list_set;
 	struct mailbox_list *list;
-	const char *error;
+	const char *layout, *error;
 	pool_t pool;
 
-	if (mbox_get_list_settings(&list_set, data, flags) < 0)
+	if (mbox_get_list_settings(&list_set, data, flags, &layout) < 0)
 		return NULL;
 	list_set.mail_storage_flags = &flags;
 	list_set.lock_method = &lock_method;
@@ -421,22 +419,21 @@
 	pool = pool_alloconly_create("storage", 512+256);
 	storage = p_new(pool, struct mbox_storage, 1);
 
-	if (mailbox_list_init("fs", &list_set,
+	if (mailbox_list_init(layout, &list_set,
 			      mail_storage_get_list_flags(flags),
-			      mailbox_storage_list_is_mailbox, storage,
 			      &list, &error) < 0) {
-		i_error("mbox fs: %s", error);
+		i_error("mbox %s: %s", layout, error);
 		pool_unref(pool);
 		return NULL;
 	}
-
 	storage->list_super = list->v;
-	list->v.get_path = mbox_list_get_path;
-
-	if (!mbox_mailbox_list_module_id_set) {
-		mbox_mailbox_list_module_id_set = TRUE;
-		mbox_mailbox_list_module_id = mailbox_list_module_id++;
+	if (strcmp(layout, "fs") == 0 && *list_set.maildir_name == '\0') {
+		/* have to use .imap/ directories */
+		list->v.get_path = mbox_list_get_path;
 	}
+	list->v.iter_is_mailbox = mbox_list_iter_is_mailbox;
+	list->v.delete_mailbox = mbox_list_delete_mailbox;
+
 	array_idx_set(&list->module_contexts,
 		      mbox_mailbox_list_module_id, &storage);
 
@@ -667,8 +664,7 @@
 		  struct istream *input, enum mailbox_open_flags flags)
 {
 	struct mbox_storage *storage = (struct mbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-	const char *path;
+	const char *path, *error;
 	struct stat st;
 
 	mail_storage_clear_error(_storage);
@@ -702,8 +698,10 @@
 
 	if (ENOTFOUND(errno)) {
 		mail_storage_set_error(_storage,
-			MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-	} else if (!mbox_handle_errors(istorage)) {
+			MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name);
+	} else if (mail_storage_errno2str(&error))
+		mail_storage_set_error(_storage, "%s", error);
+	else {
 		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
 					  path);
 	}
@@ -714,8 +712,7 @@
 static int mbox_mailbox_create(struct mail_storage *_storage, const char *name,
 			       bool directory)
 {
-	struct index_storage *storage = (struct index_storage *)_storage;
-	const char *path, *p;
+	const char *path, *p, *error;
 	struct stat st;
 	int fd;
 
@@ -726,17 +723,6 @@
 		return -1;
 	}
 
-	if (strncasecmp(name, "INBOX/", 6) == 0) {
-		/* We might actually be able to create mailboxes under INBOX
-		   because the real INBOX file isn't usually named as INBOX
-		   in the root mail directory. that would anyway be a special
-		   case which would require special handling elsewhere, so just
-		   don't allow it. */
-		mail_storage_set_error(_storage,
-				"Mailbox doesn't allow inferior mailboxes");
-		return -1;
-	}
-
 	/* make sure it doesn't exist already */
 	path = mailbox_list_get_path(_storage->list, name,
 				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
@@ -749,7 +735,9 @@
 		if (errno == ENOTDIR) {
 			mail_storage_set_error(_storage,
 				"Mailbox doesn't allow inferior mailboxes");
-		} else if (!mbox_handle_errors(storage)) {
+		} else if (mail_storage_errno2str(&error))
+			mail_storage_set_error(_storage, "%s", error);
+		else {
 			mail_storage_set_critical(_storage,
 				"stat() failed for mbox file %s: %m", path);
 		}
@@ -761,11 +749,12 @@
 	if (p != NULL) {
 		p = t_strdup_until(path, p);
 		if (mkdir_parents(p, CREATE_MODE) < 0) {
-			if (mbox_handle_errors(storage))
-				return -1;
-
-			mail_storage_set_critical(_storage,
-				"mkdir_parents(%s) failed: %m", p);
+			if (mail_storage_errno2str(&error))
+				mail_storage_set_error(_storage, "%s", error);
+			else {
+				mail_storage_set_critical(_storage,
+					"mkdir_parents(%s) failed: %m", p);
+			}
 			return -1;
 		}
 
@@ -785,194 +774,15 @@
 	if (errno == EEXIST) {
 		/* mailbox was just created between stat() and open() call.. */
 		mail_storage_set_error(_storage, "Mailbox already exists");
-	} else if (!mbox_handle_errors(storage)) {
+	} else if (mail_storage_errno2str(&error))
+		mail_storage_set_error(_storage, "%s", error);
+	else {
 		mail_storage_set_critical(_storage,
 			"Can't create mailbox %s: %m", name);
 	}
 	return -1;
 }
 
-static int mbox_mailbox_delete(struct mail_storage *_storage, const char *name)
-{
-	struct index_storage *storage = (struct index_storage *)_storage;
-	const char *index_dir, *path;
-	struct stat st;
-
-	mail_storage_clear_error(_storage);
-
-	if (strcmp(name, "INBOX") == 0) {
-		mail_storage_set_error(_storage, "INBOX can't be deleted.");
-		return -1;
-	}
-
-	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
-		mail_storage_set_error(_storage, "Invalid mailbox name");
-		return -1;
-	}
-
-	path = mailbox_list_get_path(_storage->list, name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	if (lstat(path, &st) < 0) {
-		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(_storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(_storage,
-				"lstat() failed for %s: %m", path);
-		}
-		return -1;
-	}
-
-	if (S_ISDIR(st.st_mode)) {
-		/* deleting a directory. allow it only if it doesn't contain
-		   anything. Delete the ".imap" directory first in case there
-		   have been indexes. */
-		index_dir = mailbox_list_get_path(_storage->list, name,
-					MAILBOX_LIST_PATH_TYPE_MAILBOX);
-
-		if (*index_dir != '\0' && rmdir(index_dir) < 0 &&
-		    !ENOTFOUND(errno) && errno != ENOTEMPTY) {
-			if (!mbox_handle_errors(storage)) {
-				mail_storage_set_critical(_storage,
-					"rmdir() failed for %s: %m", index_dir);
-				return -1;
-			}
-		}
-
-		if (rmdir(path) == 0)
-			return 0;
-
-		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(_storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-		} else if (errno == ENOTEMPTY) {
-			mail_storage_set_error(_storage,
-				"Folder %s isn't empty, can't delete it.",
-				name);
-		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(_storage,
-				"rmdir() failed for %s: %m", path);
-		}
-		return -1;
-	}
-
-	/* delete the index directory first, so that if we crash we don't
-	   leave indexes for deleted mailboxes lying around */
-	index_dir = mailbox_list_get_path(_storage->list, name,
-					  MAILBOX_LIST_PATH_TYPE_INDEX);
-	if (*index_dir != '\0') {
-		index_storage_destroy_unrefed();
-
-		if (unlink_directory(index_dir, TRUE) < 0 && errno != ENOENT) {
-			mail_storage_set_critical(_storage,
-				"unlink_directory(%s) failed: %m", index_dir);
-			return -1;
-		}
-	}
-
-	if (unlink(path) < 0) {
-		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(_storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(_storage,
-				"unlink() failed for %s: %m", path);
-		}
-		return -1;
-	}
-
-	return 0;
-}
-
-static int mbox_mailbox_rename(struct mail_storage *_storage,
-			       const char *oldname, const char *newname)
-{
-	struct index_storage *storage = (struct index_storage *)_storage;
-	const char *oldpath, *newpath, *old_indexdir, *new_indexdir, *p;
-	struct stat st;
-
-	mail_storage_clear_error(_storage);
-
-	if (!mailbox_list_is_valid_existing_name(_storage->list, oldname) ||
-	    !mailbox_list_is_valid_create_name(_storage->list, newname)) {
-		mail_storage_set_error(_storage, "Invalid mailbox name");
-		return -1;
-	}
-
-	if (strncasecmp(newname, "INBOX/", 6) == 0) {
-		/* Not allowed - see explanation in mbox_mailbox_create */
-		mail_storage_set_error(_storage,
-			"Target mailbox doesn't allow inferior mailboxes");
-		return -1;
-	}
-
-	oldpath = mailbox_list_get_path(_storage->list, oldname,
-					MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	newpath = mailbox_list_get_path(_storage->list, newname,
-					MAILBOX_LIST_PATH_TYPE_MAILBOX);
-
-	/* create the hierarchy */
-	p = strrchr(newpath, '/');
-	if (p != NULL) {
-		p = t_strdup_until(newpath, p);
-		if (mkdir_parents(p, CREATE_MODE) < 0) {
-			if (mbox_handle_errors(storage))
-				return -1;
-
-			mail_storage_set_critical(_storage,
-				"mkdir_parents(%s) failed: %m", p);
-			return -1;
-		}
-	}
-
-	/* first check that the destination mailbox doesn't exist.
-	   this is racy, but we need to be atomic and there's hardly any
-	   possibility that someone actually tries to rename two mailboxes
-	   to same new one */
-	if (lstat(newpath, &st) == 0) {
-		mail_storage_set_error(_storage,
-				       "Target mailbox already exists");
-		return -1;
-	} else if (errno == ENOTDIR) {
-		mail_storage_set_error(_storage,
-			"Target mailbox doesn't allow inferior mailboxes");
-		return -1;
-	} else if (errno != ENOENT && errno != EACCES) {
-		mail_storage_set_critical(_storage, "lstat(%s) failed: %m",
-					  newpath);
-		return -1;
-	}
-
-	/* NOTE: renaming INBOX works just fine with us, it's simply recreated
-	   the next time it's needed. */
-	if (rename(oldpath, newpath) < 0) {
-		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(_storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname);
-		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(_storage,
-				"rename(%s, %s) failed: %m", oldpath, newpath);
-		}
-		return -1;
-	}
-
-	/* we need to rename the index directory as well */
-	old_indexdir = mailbox_list_get_path(_storage->list, oldname,
-					     MAILBOX_LIST_PATH_TYPE_INDEX);
-	new_indexdir = mailbox_list_get_path(_storage->list, newname,
-					     MAILBOX_LIST_PATH_TYPE_INDEX);
-	if (*old_indexdir != '\0') {
-		if (rename(old_indexdir, new_indexdir) < 0 &&
-		    errno != ENOENT) {
-			mail_storage_set_critical(_storage,
-						  "rename(%s, %s) failed: %m",
-						  old_indexdir, new_indexdir);
-		}
-	}
-
-	return 0;
-}
-
 static int mbox_storage_close(struct mailbox *box)
 {
 	struct mbox_mailbox *mbox = (struct mbox_mailbox *)box;
@@ -1019,26 +829,39 @@
 		index_mailbox_check_add(&mbox->ibox, mbox->path);
 }
 
-static int mbox_is_mailbox(struct mail_storage *storage,
-			   const char *dir, const char *fname,
-			   enum mailbox_list_iter_flags iter_flags,
-			   enum mailbox_info_flags *flags,
-			   enum mailbox_list_file_type type)
+static bool
+is_inbox_file(struct mailbox_list *list, const char *path, const char *fname)
+{
+	const char *inbox_path;
+
+	if (strcasecmp(fname, "INBOX") != 0)
+		return FALSE;
+
+	inbox_path = mailbox_list_get_path(list, "INBOX",
+					   MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	return strcmp(inbox_path, path) == 0;
+}
+
+static int mbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+				     const char *dir, const char *fname,
+				     enum mailbox_list_file_type type,
+				     enum mailbox_info_flags *flags_r)
 {
+	struct mail_storage *storage = MBOX_LIST_CONTEXT(ctx->list);
 	const char *path, *root_dir;
 	size_t len;
 	struct stat st;
 	int ret = 1;
 
 	if (strcmp(fname, MBOX_INDEX_DIR_NAME) == 0) {
-		*flags = MAILBOX_NOSELECT;
+		*flags_r = MAILBOX_NOSELECT;
 		return 0;
 	}
 	if (strcmp(fname, MBOX_SUBSCRIPTION_FILE_NAME) == 0) {
 		root_dir = mailbox_list_get_path(storage->list, NULL,
 					MAILBOX_LIST_PATH_TYPE_MAILBOX);
 		if (strcmp(root_dir, dir) == 0) {
-			*flags = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
+			*flags_r = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
 			return 0;
 		}
 	}
@@ -1046,19 +869,19 @@
 	/* skip all .lock files */
 	len = strlen(fname);
 	if (len > 5 && strcmp(fname+len-5, ".lock") == 0) {
-		*flags = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
+		*flags_r = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
 		return 0;
 	}
 
 	/* try to avoid stat() with these checks */
 	if (type == MAILBOX_LIST_FILE_TYPE_DIR) {
-		*flags |= MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+		*flags_r = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
 		return 1;
 	}
 	if (type != MAILBOX_LIST_FILE_TYPE_SYMLINK &&
 	    type != MAILBOX_LIST_FILE_TYPE_UNKNOWN &&
-	    (iter_flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) {
-		*flags |= MAILBOX_NOINFERIORS;
+	    (ctx->flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) {
+		*flags_r = MAILBOX_NOINFERIORS;
 		return 1;
 	}
 
@@ -1067,11 +890,19 @@
 	path = t_strconcat(dir, "/", fname, NULL);
 	if (stat(path, &st) == 0) {
 		if (S_ISDIR(st.st_mode))
-			*flags |= MAILBOX_NOSELECT | MAILBOX_CHILDREN;
-		else
-			*flags |= MAILBOX_NOINFERIORS | STAT_GET_MARKED(st);
+			*flags_r = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+		else {
+			*flags_r = MAILBOX_NOINFERIORS | STAT_GET_MARKED(st);
+			if (is_inbox_file(ctx->list, path, fname) &&
+			    strcmp(fname, "INBOX") != 0) {
+				/* it's possible for INBOX to have child
+				   mailboxes as long as the inbox file itself
+				   isn't in <mail root>/INBOX */
+				*flags_r &= ~MAILBOX_NOINFERIORS;
+			}
+		}
 	} else if (errno == EACCES || errno == ELOOP)
-		*flags |= MAILBOX_NOSELECT;
+		*flags_r = MAILBOX_NOSELECT;
 	else if (ENOTFOUND(errno))
 		ret = 0;
 	else {
@@ -1083,8 +914,91 @@
 	return ret;
 }
 
+static int mbox_list_delete_mailbox(struct mailbox_list *list,
+				    const char *name)
+{
+	struct mbox_storage *storage = MBOX_LIST_CONTEXT(list);
+	struct stat st;
+	const char *path, *index_dir, *error;
+
+	path = mailbox_list_get_path(list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (lstat(path, &st) < 0) {
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+		} else if (mail_storage_errno2str(&error))
+			mailbox_list_set_error(list, error);
+		else {
+			mailbox_list_set_critical(list,
+				"lstat() failed for %s: %m", path);
+		}
+		return -1;
+	}
+
+	if (S_ISDIR(st.st_mode)) {
+		/* deleting a directory. allow it only if it doesn't contain
+		   anything. Delete the ".imap" directory first in case there
+		   have been indexes. */
+		index_dir = mailbox_list_get_path(list, name,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		index_dir = *index_dir == '\0' ? "" :
+			t_strconcat(index_dir, "/"MBOX_INDEX_DIR_NAME, NULL);
+
+		if (*index_dir != '\0' && rmdir(index_dir) < 0 &&
+		    !ENOTFOUND(errno) && errno != ENOTEMPTY) {
+			if (mail_storage_errno2str(&error))
+				mailbox_list_set_error(list, error);
+			else {
+				mailbox_list_set_critical(list,
+					"rmdir() failed for %s: %m", index_dir);
+			}
+			return -1;
+		}
+
+		if (rmdir(path) == 0)
+			return 0;
+
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+		} else if (errno == ENOTEMPTY) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				"Directory %s isn't empty, can't delete it.",
+				name));
+		} else if (mail_storage_errno2str(&error))
+			mailbox_list_set_error(list, error);
+		else {
+			mailbox_list_set_critical(list,
+				"rmdir() failed for %s: %m", path);
+		}
+		return -1;
+	}
+
+	/* delete index / control files first */
+	index_storage_destroy_unrefed();
+	if (storage->list_super.delete_mailbox(list, name) < 0)
+		return -1;
+
+	if (unlink(path) < 0) {
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+		} else if (mail_storage_errno2str(&error))
+			mailbox_list_set_error(list, error);
+		else {
+			mailbox_list_set_critical(list,
+				"unlink() failed for %s: %m", path);
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
 static void mbox_class_init(void)
 {
+	mbox_mailbox_list_module_id = mailbox_list_module_id++;
 	mbox_transaction_class_init();
 }
 
@@ -1106,9 +1020,6 @@
 		index_storage_set_callbacks,
 		mbox_mailbox_open,
 		mbox_mailbox_create,
-		mbox_mailbox_delete,
-		mbox_mailbox_rename,
-		mbox_is_mailbox,
 		index_storage_get_last_error
 	}
 };



More information about the dovecot-cvs mailing list