patch10.txt   [plain text]


diff -Nur dovecot-1.1.7+patch9/COPYING dovecot-patch/COPYING
--- dovecot-1.1.7+patch9/COPYING	2009-01-06 10:08:10.000000000 -0600
+++ dovecot-patch/COPYING	2009-01-06 10:08:19.000000000 -0600
@@ -80,6 +80,7 @@
 src/auth/db-od.h
 src/auth/passdb-od.c
 src/auth/userdb-od.c
+src/mail-common/*
 
  Copyright (c) 2008 Apple Inc. All rights reserved.
  
diff -Nur dovecot-1.1.7+patch9/configure.in dovecot-patch/configure.in
--- dovecot-1.1.7+patch9/configure.in	2009-01-06 10:08:10.000000000 -0600
+++ dovecot-patch/configure.in	2009-01-06 10:08:19.000000000 -0600
@@ -2405,6 +2405,7 @@
 src/imap/Makefile
 src/imap-login/Makefile
 src/login-common/Makefile
+src/mail-common/Makefile
 src/master/Makefile
 src/pop3/Makefile
 src/pop3-login/Makefile
diff -Nur dovecot-1.1.7+patch9/dovecot-example.conf dovecot-patch/dovecot-example.conf
--- dovecot-1.1.7+patch9/dovecot-example.conf	2009-01-06 10:08:10.000000000 -0600
+++ dovecot-patch/dovecot-example.conf	2009-01-06 10:08:19.000000000 -0600
@@ -516,8 +516,22 @@
   # /tmp/gdbhelper.* files:
   #   mail_executable = /usr/libexec/dovecot/gdbhelper /usr/libexec/dovecot/imap
   #
+  # (APPLE) Must use default executable when mail_process_per_connection=no
+  # to maintain compatibility with mail executables that understand only
+  # mail_process_per_connection=yes.  Force override by having the last
+  # character of your executable's name be !.
   #mail_executable = /usr/libexec/dovecot/imap
 
+  # (APPLE) Should each connected client have its own mail process
+  # (yes), or should one mail process serve multiple clients (no)?  Yes
+  # is more secure, no is more scalable.  Same idea as
+  # login_process_per_connection.
+  #mail_process_per_connection = yes
+
+  # (APPLE) Maximum number of concurrent connections allowed per each
+  # mail process.  Meaningful only when mail_process_per_connection=no.
+  #mail_max_connections = 20
+
   # Maximum IMAP command line length in bytes. Some clients generate very long
   # command lines with huge mailboxes, so you may need to raise this if you get
   # "Too long argument" or "IMAP command line too large" errors often.
@@ -577,8 +591,23 @@
 
   # POP3 executable location. See IMAP's mail_executable above for examples
   # how this could be changed.
+  #
+  # (APPLE) Must use default executable when mail_process_per_connection=no
+  # to maintain compatibility with mail executables that understand only
+  # mail_process_per_connection=yes.  Force override by having the last
+  # character of your executable's name be !.
   #mail_executable = /usr/libexec/dovecot/pop3
 
+  # (APPLE) Should each connected client have its own mail process
+  # (yes), or should one mail process serve multiple clients (no)?  Yes
+  # is more secure, no is more scalable.  Same idea as
+  # login_process_per_connection.
+  #mail_process_per_connection = yes
+
+  # (APPLE) Maximum number of concurrent connections allowed per each
+  # mail process.  Meaningful only when mail_process_per_connection=no.
+  #mail_max_connections = 20
+
   # Don't try to set mails non-recent or seen with POP3 sessions. This is
   # mostly intended to reduce disk I/O. With maildir it doesn't move files
   # from new/ to cur/, with mbox it doesn't write Status-header.
diff -Nur dovecot-1.1.7+patch9/src/Makefile.am dovecot-patch/src/Makefile.am
--- dovecot-1.1.7+patch9/src/Makefile.am	2008-10-26 10:00:45.000000000 -0500
+++ dovecot-patch/src/Makefile.am	2009-01-06 10:08:19.000000000 -0600
@@ -23,6 +23,7 @@
 	dict \
 	master \
 	login-common \
+	mail-common \
 	imap-login \
 	imap \
 	$(POP3D) \
diff -Nur dovecot-1.1.7+patch9/src/deliver/deliver.c dovecot-patch/src/deliver/deliver.c
--- dovecot-1.1.7+patch9/src/deliver/deliver.c	2009-01-06 10:08:10.000000000 -0600
+++ dovecot-patch/src/deliver/deliver.c	2009-01-06 10:08:19.000000000 -0600
@@ -810,7 +810,8 @@
 	const char *auth_socket;
 	const char *home, *destaddr, *user, *value, *errstr, *path;
 	ARRAY_TYPE(string) extra_fields;
-	struct mail_namespace *ns, *raw_ns;
+	struct mail_user *mail_user, *raw_mail_user;
+	struct mail_namespace *raw_ns;
 	struct mail_storage *storage;
 	struct mailbox *box;
 	struct raw_mailbox *raw_box;
@@ -819,7 +820,6 @@
 	struct mailbox_header_lookup_ctx *headers_ctx;
 	struct mail *mail;
 	uid_t process_euid;
-	pool_t namespace_pool;
 	bool stderr_rejection = FALSE;
 	bool keep_environment = FALSE;
 	bool user_auth = FALSE;
@@ -1043,13 +1043,16 @@
 
 	module_dir_init(modules);
 
-	namespace_pool = pool_alloconly_create("namespaces", 1024);
-	if (mail_namespaces_init(namespace_pool, user, &ns) < 0)
+	mail_user = mail_user_init(user, home);
+	if (mail_namespaces_init(mail_user) < 0)
 		i_fatal("Namespace initialization failed");
 
-	raw_ns = mail_namespaces_init_empty(namespace_pool);
+	/* create a separate mail user for the internal namespace */
+	raw_mail_user = mail_user_init(user, NULL);
+	raw_ns = mail_namespaces_init_empty(raw_mail_user);
 	raw_ns->flags |= NAMESPACE_FLAG_INTERNAL;
-	if (mail_storage_create(raw_ns, "raw", "/tmp", user,
+
+	if (mail_storage_create(raw_ns, "raw", "/tmp",
 				MAIL_STORAGE_FLAG_FULL_FS_ACCESS,
 				FILE_LOCK_METHOD_FCNTL, &errstr) < 0)
 		i_fatal("Couldn't create internal raw storage: %s", errstr);
@@ -1086,7 +1089,8 @@
 	if (deliver_mail == NULL)
 		ret = -1;
 	else {
-		if (deliver_mail(ns, &storage, mail, destaddr, mailbox) <= 0) {
+		if (deliver_mail(mail_user->namespaces, &storage, mail,
+				 destaddr, mailbox) <= 0) {
 			/* if message was saved, don't bounce it even though
 			   the script failed later. */
 			ret = saved_mail ? 0 : -1;
@@ -1098,12 +1102,14 @@
 
 	if (ret < 0 && !tried_default_save) {
 		/* plugins didn't handle this. save into the default mailbox. */
-		ret = deliver_save(ns, &storage, mailbox, mail, 0, NULL);
+		ret = deliver_save(mail_user->namespaces,
+				   &storage, mailbox, mail, 0, NULL);
 	}
 	if (ret < 0 && strcasecmp(mailbox, "INBOX") != 0) {
 		/* still didn't work. try once more to save it
 		   to INBOX. */
-		ret = deliver_save(ns, &storage, "INBOX", mail, 0, NULL);
+		ret = deliver_save(mail_user->namespaces,
+				   &storage, "INBOX", mail, 0, NULL);
 	}
 
 	if (ret < 0 ) {
@@ -1150,8 +1156,8 @@
 	mailbox_transaction_rollback(&t);
 	mailbox_close(&box);
 
-	mail_namespaces_deinit(&raw_ns);
-	mail_namespaces_deinit(&ns);
+	mail_user_deinit(&mail_user);
+	mail_user_deinit(&raw_mail_user);
 
 	module_dir_unload(&modules);
 	mail_storage_deinit();
diff -Nur dovecot-1.1.7+patch9/src/imap/Makefile.am dovecot-patch/src/imap/Makefile.am
--- dovecot-1.1.7+patch9/src/imap/Makefile.am	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/imap/Makefile.am	2009-01-06 10:08:19.000000000 -0600
@@ -9,6 +9,7 @@
 	-I$(top_srcdir)/src/lib-imap \
 	-I$(top_srcdir)/src/lib-index \
 	-I$(top_srcdir)/src/lib-storage \
+	-I$(top_srcdir)/src/mail-common \
 	-DMODULEDIR=\""$(moduledir)"\"
 
 imap_LDFLAGS = -export-dynamic
@@ -23,6 +24,7 @@
 	../lib-storage/list/libstorage_list.a \
 	$(STORAGE_LIBS) \
 	../lib-imap/libimap.a \
+	../mail-common/libmail-common.a \
 	../lib-mail/libmail.a \
 	../lib-dict/libdict.a \
 	../lib-charset/libcharset.a \
diff -Nur dovecot-1.1.7+patch9/src/imap/client.c dovecot-patch/src/imap/client.c
--- dovecot-1.1.7+patch9/src/imap/client.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/imap/client.c	2009-01-06 10:08:19.000000000 -0600
@@ -10,13 +10,16 @@
 #include "var-expand.h"
 #include "commands.h"
 #include "mail-namespace.h"
+#include "master.h"		/* APPLE */
+#include "ioloop.h"		/* APPLE */
 
 #include <stdlib.h>
 #include <unistd.h>
 
 extern struct mail_storage_callbacks mail_storage_callbacks;
 
-static struct client *my_client; /* we don't need more than one currently */
+static struct client *clients;		/* APPLE was my_client, now list */
+static bool process_per_connection = TRUE;	/* APPLE */
 
 static bool client_handle_input(struct client *client);
 
@@ -27,14 +30,18 @@
 	client_destroy(client, "Disconnected for inactivity");
 }
 
+static /* APPLE */
 struct client *client_create(int fd_in, int fd_out,
-			     struct mail_namespace *namespaces)
+			     struct mail_user *user,
+			     unsigned int connection_id)	/* APPLE */
 {
 	struct client *client;
+	struct mail_namespace *ns;
 
 	/* always use nonblocking I/O */
 	net_set_nonblock(fd_in, TRUE);
-	net_set_nonblock(fd_out, TRUE);
+	if (fd_out != fd_in)			/* APPLE */
+		net_set_nonblock(fd_out, TRUE);
 
 	client = i_new(struct client, 1);
 	client->fd_in = fd_in;
@@ -50,22 +57,76 @@
 				      client_idle_timeout, client);
 
 	client->command_pool = pool_alloconly_create("client command", 1024*12);
-	client->namespaces = namespaces;
+	client->user = user;
 
-	while (namespaces != NULL) {
-		mail_storage_set_callbacks(namespaces->storage,
+	for (ns = user->namespaces; ns != NULL; ns = ns->next) {
+		mail_storage_set_callbacks(ns->storage,
 					   &mail_storage_callbacks, client);
-		namespaces = namespaces->next;
 	}
 
-	i_assert(my_client == NULL);
-	my_client = client;
+	/* APPLE */
+	client->connection_id = connection_id;
+	client->next = clients;
+	clients = client;
 
 	if (hook_client_created != NULL)
 		hook_client_created(&client);
 	return client;
 }
 
+/* APPLE */
+bool client_attach(int fd_in, int fd_out, bool is_standalone)
+{
+	const char *username, *home;
+	struct mail_user *user;
+	struct client *client;
+	unsigned int connection_id;
+
+	username = getenv("USER");
+	if (username == NULL || *username == '\0') {
+		i_error("USER environment missing from client request");
+		return FALSE;
+	}
+
+	home = getenv("HOME");
+
+	user = mail_user_init(username, home);
+	if (mail_namespaces_init(user) < 0) {
+		i_error("Namespace initialization failed for user %s",
+			username);
+		mail_user_deinit(&user);
+		return FALSE;
+	}
+
+	if (process_per_connection)
+		connection_id = 0;
+	else {
+		const char *env = getenv("CONNECTION_ID");
+		if (env == NULL || *env == '\0') {
+			i_error("CONNECTION_ID environment missing from client request");
+			mail_user_deinit(&user);
+			return FALSE;
+		}
+		connection_id = strtoul(env, NULL, 10);
+	}
+	client = client_create(fd_in, fd_out, user, connection_id);
+
+        o_stream_cork(client->output);
+	if (is_standalone) {
+		client_send_line(client, t_strconcat(
+			"* PREAUTH [CAPABILITY ",
+			str_c(capability_string), "] "
+			"Logged in as ", username, NULL));
+	} else if (getenv("IMAPLOGINTAG") != NULL) {
+		/* Support for mailfront */
+		client_send_line(client, t_strconcat(getenv("IMAPLOGINTAG"),
+						     " OK Logged in.", NULL));
+	}
+        o_stream_uncork(client->output);
+
+	return TRUE;
+}
+
 void client_command_cancel(struct client_command_context **_cmd)
 {
 	struct client_command_context *cmd = *_cmd;
@@ -132,14 +193,23 @@
 void client_destroy(struct client *client, const char *reason)
 {
 	struct client_command_context *cmd;
+	struct client **pos;			/* APPLE */
+	unsigned int connection_id;		/* APPLE */
+
 	i_assert(!client->destroyed);
 	client->destroyed = TRUE;
 
 	if (!client->disconnected) {
+		const char *prefix;			/* APPLE */
+
 		client->disconnected = TRUE;
 		if (reason == NULL)
 			reason = client_get_disconnect_reason(client);
-		i_info("%s %s", reason, client_stats(client));
+
+		/* APPLE */
+		prefix = process_per_connection ? "" :
+			t_strconcat("User ", client->user->username, ": ", NULL);
+		i_info("%s%s %s", prefix, reason, client_stats(client));
 	}
 
 	i_stream_close(client->input);
@@ -161,7 +231,7 @@
 
 	if (client->mailbox != NULL)
 		mailbox_close(&client->mailbox);
-	mail_namespaces_deinit(&client->namespaces);
+	mail_user_deinit(&client->user);
 
 	if (client->free_parser != NULL)
 		imap_parser_destroy(&client->free_parser);
@@ -182,21 +252,47 @@
 	}
 
 	pool_unref(&client->command_pool);
+
+	/* APPLE */
+	for (pos = &clients; *pos != NULL; pos = &(*pos)->next) {
+		if (*pos == client) {
+			*pos = client->next;
+			break;
+		}
+	}
+	io_env_clean();
+	connection_id = client->connection_id;
+
 	i_free(client);
 
-	/* quit the program */
-	my_client = NULL;
-	io_loop_stop(ioloop);
+	if (process_per_connection) {			/* APPLE */
+		/* quit the program */
+		i_assert(clients == NULL);
+		io_loop_stop(ioloop);
+	} else {
+		/* (APPLE) run until master says stop, or can't say stop
+		   and there are no more clients.  otherwise there's a
+		   race if there's an incoming request from the master. */
+		if (!master_send_disconnect(connection_id) &&
+		    clients == NULL)
+			io_loop_stop(ioloop);
+	}
 }
 
 void client_disconnect(struct client *client, const char *reason)
 {
+	const char *prefix;			/* APPLE */
+
 	i_assert(reason != NULL);
 
 	if (client->disconnected)
 		return;
 
-	i_info("Disconnected: %s %s", reason, client_stats(client));
+	/* APPLE */
+	prefix = process_per_connection ? "" :
+		t_strconcat("User ", client->user->username, ": ", NULL);
+	i_info("%sDisconnected: %s %s", prefix, reason, client_stats(client));
+
 	client->disconnected = TRUE;
 	(void)o_stream_flush(client->output);
 
@@ -809,15 +905,51 @@
 	}
 }
 
-void clients_init(void)
+void clients_init(bool persistent_mail_process)		/* APPLE */
 {
-	my_client = NULL;
+	clients = NULL;
+	process_per_connection = !persistent_mail_process;	/* APPLE */
 }
 
 void clients_deinit(void)
 {
-	if (my_client != NULL) {
-		client_send_line(my_client, "* BYE Server shutting down.");
-		client_destroy(my_client, "Server shutting down");
+	struct client *next;
+
+	/* APPLE */
+	while (clients != NULL) {
+		next = clients->next;
+		client_send_line(clients, "* BYE Server shutting down.");
+		client_destroy(clients, "Server shutting down");
+		clients = next;
+	}
+}
+
+/* APPLE */
+bool clients_connected(void)
+{
+	return clients != NULL;
+}
+
+/* APPLE */
+void clients_info(string_t *str)
+{
+	struct client *client;
+	int n;
+
+	n = 0;
+	for (client = clients; client != NULL; client = client->next)
+		++n;
+
+	str_printfa(str, "%d connected user%s", n, n == 1 ? "" : "s");
+
+	if (n > 0) {
+		str_append(str, " (");
+		n = 0;
+		for (client = clients; client != NULL; client = client->next) {
+			if (n++ > 0)
+				str_append(str, ", ");
+			str_append(str, client->user->username);
+		}
+		str_append_c(str, ')');
 	}
 }
diff -Nur dovecot-1.1.7+patch9/src/imap/client.h dovecot-patch/src/imap/client.h
--- dovecot-1.1.7+patch9/src/imap/client.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/imap/client.h	2009-01-06 10:08:19.000000000 -0600
@@ -56,14 +56,28 @@
 	unsigned int temp_executed:1; /* temporary execution state tracking */
 };
 
+/* APPLE moved from imap-fetch-body.c */
+#include "message-size.h"
+struct partial_cache {
+	unsigned int select_counter;
+	unsigned int uid;
+
+	uoff_t physical_start;
+	bool cr_skipped;
+	struct message_size pos;
+};
+
 struct client {
+	struct client *next;			/* APPLE */
+	unsigned int connection_id;		/* APPLE */
+
 	int fd_in, fd_out;
 	struct io *io;
 	struct istream *input;
 	struct ostream *output;
 	struct timeout *to_idle, *to_idle_output;
 
-        struct mail_namespace *namespaces;
+        struct mail_user *user;
 	struct mailbox *mailbox;
         struct mailbox_keywords keywords;
 	unsigned int select_counter; /* increased when mailbox is changed */
@@ -84,6 +98,9 @@
 	struct client_command_context *input_lock;
 	struct client_command_context *output_lock;
 
+	/* APPLE made per-client, was global */
+	struct partial_cache last_partial;
+
 	/* syncing marks this TRUE when it sees \Deleted flags. this is by
 	   EXPUNGE for Outlook-workaround. */
 	unsigned int sync_seen_deletes:1;
@@ -96,10 +113,9 @@
 					   found a new line */
 };
 
-/* Create new client with specified input/output handles. socket specifies
-   if the handle is a socket. */
-struct client *client_create(int fd_in, int fd_out,
-			     struct mail_namespace *namespaces);
+/* Create new client with specified input/output handles.
+   APPLE was client_create() */
+bool client_attach(int fd_in, int fd_out, bool is_standalone);
 void client_destroy(struct client *client, const char *reason);
 
 /* Disconnect client connection */
@@ -125,8 +141,10 @@
 bool client_read_string_args(struct client_command_context *cmd,
 			     unsigned int count, ...);
 
-void clients_init(void);
+void clients_init(bool persistent_mail_process);	/* APPLE */
 void clients_deinit(void);
+bool clients_connected(void);				/* APPLE */
+void clients_info(string_t *str);			/* APPLE */
 
 void client_command_cancel(struct client_command_context **cmd);
 void client_command_free(struct client_command_context **cmd);
diff -Nur dovecot-1.1.7+patch9/src/imap/cmd-list.c dovecot-patch/src/imap/cmd-list.c
--- dovecot-1.1.7+patch9/src/imap/cmd-list.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/imap/cmd-list.c	2009-01-06 10:08:19.000000000 -0600
@@ -185,7 +185,7 @@
 	}
 
 	/* find the INBOX flags */
-	ns = mail_namespace_find_inbox(ctx->cmd->client->namespaces);
+	ns = mail_namespace_find_inbox(ctx->cmd->client->user->namespaces);
 	list_iter = mailbox_list_iter_init(ns->list, "INBOX", 0);
 	info = mailbox_list_iter_next(list_iter);
 	if (info != NULL) {
@@ -738,13 +738,13 @@
 	/* Special request to return the hierarchy delimiter and mailbox root
 	   name. If namespace has a prefix, it's returned as the mailbox root.
 	   Otherwise we'll emulate UW-IMAP behavior. */
-	ns = mail_namespace_find_visible(client->namespaces, &ref);
+	ns = mail_namespace_find_visible(client->user->namespaces, &ref);
 	if (ns != NULL) {
 		ns_prefix = ns->prefix;
 		ns_sep = ns->sep;
 	} else {
 		ns_prefix = "";
-		ns_sep = mail_namespace_get_root_sep(client->namespaces);
+		ns_sep = mail_namespace_get_root_sep(client->user->namespaces);
 	}
 
 	str = t_str_new(64);
@@ -788,7 +788,7 @@
 
 	ctx = p_new(cmd->pool, struct cmd_list_context, 1);
 	ctx->cmd = cmd;
-	ctx->ns = client->namespaces;
+	ctx->ns = client->user->namespaces;
 	ctx->lsub = lsub;
 
 	cmd->context = ctx;
diff -Nur dovecot-1.1.7+patch9/src/imap/cmd-namespace.c dovecot-patch/src/imap/cmd-namespace.c
--- dovecot-1.1.7+patch9/src/imap/cmd-namespace.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/imap/cmd-namespace.c	2009-01-06 10:08:19.000000000 -0600
@@ -42,11 +42,11 @@
 	str = t_str_new(256);
 	str_append(str, "* NAMESPACE ");
 
-        list_namespaces(client->namespaces, NAMESPACE_PRIVATE, str);
+        list_namespaces(client->user->namespaces, NAMESPACE_PRIVATE, str);
 	str_append_c(str, ' ');
-	list_namespaces(client->namespaces, NAMESPACE_SHARED, str);
+	list_namespaces(client->user->namespaces, NAMESPACE_SHARED, str);
 	str_append_c(str, ' ');
-        list_namespaces(client->namespaces, NAMESPACE_PUBLIC, str);
+        list_namespaces(client->user->namespaces, NAMESPACE_PUBLIC, str);
 
 	client_send_line(client, str_c(str));
 	client_send_tagline(cmd, "OK Namespace completed.");
diff -Nur dovecot-1.1.7+patch9/src/imap/cmd-subscribe.c dovecot-patch/src/imap/cmd-subscribe.c
--- dovecot-1.1.7+patch9/src/imap/cmd-subscribe.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/imap/cmd-subscribe.c	2009-01-06 10:08:19.000000000 -0600
@@ -35,7 +35,7 @@
 		return FALSE;
 
 	verify_name = mailbox;
-	ns = mail_namespace_find_subscribable(cmd->client->namespaces,
+	ns = mail_namespace_find_subscribable(cmd->client->user->namespaces,
 					      &mailbox);
 	if (ns == NULL) {
 		client_send_tagline(cmd, "NO Unknown namespace.");
@@ -49,7 +49,7 @@
 		verify_name = t_strndup(verify_name, strlen(verify_name)-1);
 	}
 
-	if (have_listable_namespace_prefix(cmd->client->namespaces,
+	if (have_listable_namespace_prefix(cmd->client->user->namespaces,
 					   verify_name)) {
 		/* subscribing to a listable namespace prefix, allow it. */
 	} else {
diff -Nur dovecot-1.1.7+patch9/src/imap/commands-util.c dovecot-patch/src/imap/commands-util.c
--- dovecot-1.1.7+patch9/src/imap/commands-util.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/imap/commands-util.c	2009-01-06 10:08:19.000000000 -0600
@@ -23,7 +23,7 @@
 {
 	struct mail_namespace *ns;
 
-	ns = mail_namespace_find(cmd->client->namespaces, mailbox);
+	ns = mail_namespace_find(cmd->client->user->namespaces, mailbox);
 	if (ns != NULL)
 		return ns;
 
diff -Nur dovecot-1.1.7+patch9/src/imap/imap-fetch-body.c dovecot-patch/src/imap/imap-fetch-body.c
--- dovecot-1.1.7+patch9/src/imap/imap-fetch-body.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/imap/imap-fetch-body.c	2009-01-06 10:08:19.000000000 -0600
@@ -32,16 +32,7 @@
 	unsigned int peek:1;
 };
 
-struct partial_cache {
-	unsigned int select_counter;
-	unsigned int uid;
-
-	uoff_t physical_start;
-	bool cr_skipped;
-	struct message_size pos;
-};
-
-static struct partial_cache last_partial = { 0, 0, 0, 0, { 0, 0, 0 } };
+/* APPLE made last_partial per-client */
 
 static bool seek_partial(unsigned int select_counter, unsigned int uid,
 			 struct partial_cache *partial, struct istream *stream,
@@ -224,10 +215,11 @@
 
 	ctx->cur_offset += ret;
 	if (ctx->update_partial) {
-		last_partial.cr_skipped = ctx->skip_cr != 0;
-		last_partial.pos.physical_size =
-			ctx->cur_input->v_offset - last_partial.physical_start;
-		last_partial.pos.virtual_size += ret;
+		/* APPLE made last_partial per-client */
+		ctx->client->last_partial.cr_skipped = ctx->skip_cr != 0;
+		ctx->client->last_partial.pos.physical_size =
+			ctx->cur_input->v_offset - ctx->client->last_partial.physical_start;
+		ctx->client->last_partial.pos.virtual_size += ret;
 	}
 
 	return ctx->cur_offset == ctx->cur_size;
@@ -312,7 +304,8 @@
 	} else {
 		ctx->skip_cr =
 			seek_partial(ctx->select_counter, ctx->cur_mail->uid,
-				     &last_partial, ctx->cur_input, body->skip);
+				     &ctx->client->last_partial, ctx->cur_input, body->skip);
+				/* APPLE made last_partial per-client */
 	}
 
 	return fetch_stream(ctx, size);
diff -Nur dovecot-1.1.7+patch9/src/imap/main.c dovecot-patch/src/imap/main.c
--- dovecot-1.1.7+patch9/src/imap/main.c	2008-11-15 11:33:28.000000000 -0600
+++ dovecot-patch/src/imap/main.c	2009-01-06 10:08:19.000000000 -0600
@@ -16,6 +16,8 @@
 #include "commands.h"
 #include "mail-namespace.h"
 #include "imap-thread.h"
+#include "master.h"		/* APPLE */
+#include "env-util.h"		/* APPLE */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -25,6 +27,9 @@
 #define IS_STANDALONE() \
         (getenv("LOGGED_IN") == NULL && getenv("IMAPLOGINTAG") == NULL)
 
+/* APPLE */
+#define IS_PERSISTENT()	(getenv("PERSISTENT_MAIL_PROCESS") != NULL)
+
 struct client_workaround_list {
 	const char *name;
 	enum client_workarounds num;
@@ -46,7 +51,6 @@
 static struct io *log_io = NULL;
 static struct module *modules = NULL;
 static char log_prefix[128]; /* syslog() needs this to be permanent */
-static pool_t namespace_pool;
 
 void (*hook_client_created)(struct client **client) = NULL;
 
@@ -61,6 +65,21 @@
 	io_loop_stop(ioloop);
 }
 
+/* APPLE */
+#ifdef SIGINFO
+static void sig_info(int signo ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+	string_t *msg;
+
+	msg = t_str_new(512);
+	clients_info(msg);
+	str_append(msg, "; ");
+	master_info(msg);
+
+	i_info("IMAP process %d: %s", getpid(), str_c(msg));
+}
+#endif
+
 static void log_error_callback(void *context ATTR_UNUSED)
 {
 	io_loop_stop(ioloop);
@@ -162,9 +181,8 @@
 
 static void main_init(void)
 {
-	struct client *client;
-	struct mail_namespace *ns;
 	const char *user, *str;
+	int fd_in, fd_out;			/* APPLE */
 
 	lib_signals_init();
         lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
@@ -178,6 +196,7 @@
 			user = getlogin();
 		if (user == NULL)
 			i_fatal("USER environment missing");
+		env_put(i_strconcat("USER=", user, NULL));	/* APPLE */
 	}
 
 	if (getenv("DEBUG") != NULL) {
@@ -202,9 +221,17 @@
         mail_storage_init();
 	mail_storage_register_all();
 	mailbox_list_register_all();
-	clients_init();
+	clients_init(IS_PERSISTENT());			/* APPLE */
 	commands_init();
 
+	/* APPLE */
+	master_init(IS_PERSISTENT() ? 0 : -1,
+	    ioloop, client_attach, clients_connected);
+#ifdef SIGINFO
+	if (IS_PERSISTENT())
+		lib_signals_set_handler(SIGINFO, TRUE, sig_info, NULL);
+#endif
+
 	module_dir_init(modules);
 
 	if (getenv("DUMP_CAPABILITY") != NULL) {
@@ -230,27 +257,22 @@
 
         parse_workarounds();
 
-	namespace_pool = pool_alloconly_create("namespaces", 1024);
-	if (mail_namespaces_init(namespace_pool, user, &ns) < 0)
-		i_fatal("Namespace initialization failed");
-	client = client_create(0, 1, ns);
-
-        o_stream_cork(client->output);
-	if (IS_STANDALONE()) {
-		client_send_line(client, t_strconcat(
-			"* PREAUTH [CAPABILITY ",
-			str_c(capability_string), "] "
-			"Logged in as ", user, NULL));
-	} else if (getenv("IMAPLOGINTAG") != NULL) {
-		/* Support for mailfront */
-		client_send_line(client, t_strconcat(getenv("IMAPLOGINTAG"),
-						     " OK Logged in.", NULL));
+	/* APPLE */
+	if (IS_PERSISTENT())
+		fd_in = fd_out = 3;
+	else {
+		fd_in = 0;
+		fd_out = 1;
 	}
-        o_stream_uncork(client->output);
+
+	if (!client_attach(fd_in, fd_out, IS_STANDALONE()))
+		i_fatal("Failed to initialize for client");
 }
 
 static void main_deinit(void)
 {
+	master_deinit();		/* APPLE */
+
 	if (log_io != NULL)
 		io_remove(&log_io);
 	clients_deinit();
@@ -260,7 +282,6 @@
         mail_storage_deinit();
 	dict_driver_unregister(&dict_driver_client);
 	random_deinit();
-	pool_unref(&namespace_pool);
 
 	str_free(&capability_string);
 
@@ -271,8 +292,10 @@
 int main(int argc ATTR_UNUSED, char *argv[], char *envp[])
 {
 #ifdef DEBUG
-	if (getenv("LOGGED_IN") != NULL && getenv("GDB") == NULL)
-		fd_debug_verify_leaks(3, 1024);
+	if (getenv("LOGGED_IN") != NULL && getenv("GDB") == NULL) {
+		int base = IS_PERSISTENT() ? 4 : 3;		/* APPLE */
+		fd_debug_verify_leaks(base, 1024);
+	}
 #endif
 	if (IS_STANDALONE() && getuid() == 0 &&
 	    net_getpeername(1, NULL, NULL) == 0) {
@@ -286,6 +309,10 @@
 	lib_init();
 	drop_privileges();
 
+	/* APPLE */
+	if (IS_PERSISTENT())
+		io_env_enable();
+
         process_title_init(argv, envp);
 	ioloop = io_loop_create();
 
diff -Nur dovecot-1.1.7+patch9/src/lib/env-util.c dovecot-patch/src/lib/env-util.c
--- dovecot-1.1.7+patch9/src/lib/env-util.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/env-util.c	2009-01-06 10:08:19.000000000 -0600
@@ -5,35 +5,141 @@
 
 #include <stdlib.h>
 
+extern char **environ;			/* APPLE */
+
 static pool_t pool = NULL;
+static bool use_private_environ;	/* APPLE */
+static int private_environ_alloc;	/* APPLE */
+static int private_environ_used;	/* APPLE */
+
+/* APPLE - Home grown version of putenv that allocates only from the
+   environment pool.  Mixing putenv() with env_clean() leaks heap. */
+static int private_putenv(char *env)
+{
+	char *eq;
+	size_t cmplen;
+	char **epp;
+
+	if ((eq = strchr(env, '=')) == NULL || eq == env) {
+		errno = EINVAL;
+		return -1;
+	}
+	cmplen = eq - env + 1;
+
+	/* remove all matching keys from environ */
+	epp = environ;
+	while (*epp) {
+		if (strncmp(*epp, env, cmplen) == 0) {
+			/* found a match, remove it */
+			char **xpp = epp;
+			p_free(pool, *xpp);
+			do
+				*xpp = *(xpp + 1);
+			while (*xpp++);
+			--private_environ_used;
+
+			/* be defensive: keep looking */
+		} else
+			++epp;
+	}
+
+	/* make room for the new entry */
+	if (private_environ_used >= private_environ_alloc) {
+		int new_alloc = private_environ_used + 10;
+		char **new_environ = p_realloc(pool, environ,
+		    private_environ_alloc * sizeof *environ,
+		    new_alloc * sizeof *environ);
+		if (new_environ == NULL) {
+			errno = ENOMEM;
+			return -1;
+		}
+		environ = new_environ;
+		private_environ_alloc = new_alloc;
+	}
+
+	/* append the entry */
+	i_assert(private_environ_used > 0);
+	epp = &environ[private_environ_used++ - 1];
+	i_assert(*epp == NULL);
+	*epp++ = env;
+	*epp = NULL;
+
+	return 0;
+}
 
 void env_put(const char *env)
 {
+	int error;			/* APPLE */
+
 	if (pool == NULL) {
 		pool = pool_alloconly_create(MEMPOOL_GROWING"Environment",
-					     2048);
+					     4096);	/* APPLE */
 	}
-	if (putenv(p_strdup(pool, env)) != 0)
+
+	/* APPLE */
+	error = use_private_environ ? private_putenv(p_strdup(pool, env)) :
+				      putenv(p_strdup(pool, env));
+	if (error != 0)
 		i_fatal("putenv(%s) failed: %m", env);
 }
 
 void env_clean(void)
 {
-#ifdef HAVE_CLEARENV
-	if (clearenv() < 0)
-		i_fatal("clearenv() failed");
-#else
-	extern char **environ;
-
-	/* Try to clear the environment.
-
-	   a) environ = NULL crashes on OS X.
-	   b) *environ = NULL doesn't work on FreeBSD 7.0.
-	   c) environ = emptyenv doesn't work on Haiku OS
-	   d) environ = calloc() should work everywhere
-	*/
-	environ = calloc(1, sizeof(*environ));
-#endif
-	/* don't clear the env_pool, otherwise the environment would get
-	   corrupted if we failed to clear it. */
+	env_clean_private(FALSE);		/* APPLE */
+}
+
+void env_clean_private(bool private_env)	/* APPLE */
+{
+	/* APPLE - clean environ without clobbering what it currently
+	   points to because we will need it later */
+
+	if (pool != NULL)
+		pool_unref(&pool);
+
+	pool = pool_alloconly_create("Environment", 4096);
+	use_private_environ = private_env;
+	private_environ_alloc = private_env ? 100 : 1;
+	private_environ_used = 1;
+	environ = p_new(pool, char *, private_environ_alloc);
+	*environ = NULL;
+}
+
+/* APPLE - rest of file */
+
+static char **env_dup(pool_t p, char * const *env)
+{
+	char * const *end;
+	char **copy, **to;
+
+	for (end = env; *end != NULL; ++end)
+		;
+
+	copy = p_new(p, char *, end - env + 1);
+	for (to = copy; env != end; ++to, ++env)
+		*to = p_strdup(p, *env);
+	*to = NULL;
+
+	return copy;
+}
+
+char **env_capture(pool_t p)
+{
+	return env_dup(p, environ);
+}
+
+void env_switch(pool_t newpool, char **newenv)
+{
+	/* this doesn't switch private_environ_{alloc,used} */
+	i_assert(!use_private_environ);
+
+	if (environ == newenv)
+		return;
+
+	if (pool != NULL)
+		pool_unref(&pool);
+
+	pool = newpool;
+	environ = newenv;
+
+	pool_ref(pool);
 }
diff -Nur dovecot-1.1.7+patch9/src/lib/env-util.h dovecot-patch/src/lib/env-util.h
--- dovecot-1.1.7+patch9/src/lib/env-util.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/env-util.h	2009-01-06 10:08:19.000000000 -0600
@@ -6,5 +6,13 @@
 void env_put(const char *env);
 /* Clear all environment variables. */
 void env_clean(void);
+/* (APPLE) Clean the environment and allocate all future environment strings
+   from a private pool (until the next env_clean*() call). */
+void env_clean_private(bool private_env);
+
+/* (APPLE) Clone the environment */
+char **env_capture(pool_t pool);
+/* (APPLE) Replace environ with env */
+void env_switch(pool_t envpool, char **env);
 
 #endif
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-epoll.c dovecot-patch/src/lib/ioloop-epoll.c
--- dovecot-1.1.7+patch9/src/lib/ioloop-epoll.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-epoll.c	2009-01-06 10:08:19.000000000 -0600
@@ -200,6 +200,7 @@
 
 			if (call) {
 				t_id = t_push();
+				io_env_switch(&io->io.env);	/* APPLE */
 				io->io.callback(io->io.context);
 				if (t_pop() != t_id) {
 					i_panic("Leaked a t_pop() call in "
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-internal.h dovecot-patch/src/lib/ioloop-internal.h
--- dovecot-1.1.7+patch9/src/lib/ioloop-internal.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-internal.h	2009-01-06 10:08:19.000000000 -0600
@@ -3,6 +3,7 @@
 
 #include "priorityq.h"
 #include "ioloop.h"
+#include "restrict-access.h"		/* APPLE */
 
 #ifndef IOLOOP_INITIAL_FD_COUNT
 #  define IOLOOP_INITIAL_FD_COUNT 128
@@ -21,11 +22,20 @@
 	unsigned int running:1;
 };
 
+/* APPLE */
+struct io_env {
+	pool_t pool;
+	char **env;
+	struct restrict_access_cred cred;
+};
+
 struct io {
 	enum io_condition condition;
 
 	io_callback_t *callback;
         void *context;
+
+	struct io_env env;			/* APPLE */
 };
 
 struct io_file {
@@ -46,6 +56,8 @@
 
 	timeout_callback_t *callback;
         void *context;
+
+	struct io_env env;			/* APPLE */
 };
 
 int io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r,
@@ -63,4 +75,9 @@
 void io_loop_notify_remove(struct ioloop *ioloop, struct io *io);
 void io_loop_notify_handler_deinit(struct ioloop *ioloop);
 
+/* (APPLE) I/O environment switching */
+void io_env_init(struct io_env *env);
+void io_env_switch(struct io_env *env);
+void io_env_deinit(struct io_env *env);
+
 #endif
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-kqueue.c dovecot-patch/src/lib/ioloop-kqueue.c
--- dovecot-1.1.7+patch9/src/lib/ioloop-kqueue.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-kqueue.c	2009-01-06 10:08:19.000000000 -0600
@@ -147,6 +147,7 @@
 		/* callback is NULL if io_remove() was already called */
 		if (io->io.callback != NULL) {
 			t_id = t_push();
+			io_env_switch(&io->io.env);	/* APPLE */
 			io->io.callback(io->io.context);
 			if (t_pop() != t_id) {
 				i_panic("Leaked a t_pop() call in "
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-notify-dn.c dovecot-patch/src/lib/ioloop-notify-dn.c
--- dovecot-1.1.7+patch9/src/lib/ioloop-notify-dn.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-notify-dn.c	2009-01-06 10:08:19.000000000 -0600
@@ -86,8 +86,10 @@
 
 	for (i = 0; i < ret; i++) {
 		io = io_notify_fd_find(&ctx->fd_ctx, fd_buf[i]);
-		if (io != NULL)
+		if (io != NULL) {
+			io_env_switch(&io->io.env);	/* APPLE */
 			io->io.callback(io->io.context);
+		}
 	}
 }
 
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-notify-fd.c dovecot-patch/src/lib/ioloop-notify-fd.c
--- dovecot-1.1.7+patch9/src/lib/ioloop-notify-fd.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-notify-fd.c	2009-01-06 10:08:19.000000000 -0600
@@ -15,6 +15,7 @@
 	io->io.condition = IO_NOTIFY;
 	io->io.callback = callback;
 	io->io.context = context;
+	io_env_init(&io->io.env);		/* APPLE */
 	io->fd = fd;
 
 	if (ctx->notifies != NULL) {
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-notify-inotify.c dovecot-patch/src/lib/ioloop-notify-inotify.c
--- dovecot-1.1.7+patch9/src/lib/ioloop-notify-inotify.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-notify-inotify.c	2009-01-06 10:08:19.000000000 -0600
@@ -69,6 +69,7 @@
 				   EINVAL */
 				io->fd = -1;
 			}
+			io_env_switch(&io->io.env);		/* APPLE */
 			io->io.callback(io->io.context);
 		}
 	}
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-notify-kqueue.c dovecot-patch/src/lib/ioloop-notify-kqueue.c
--- dovecot-1.1.7+patch9/src/lib/ioloop-notify-kqueue.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-notify-kqueue.c	2009-01-06 10:08:19.000000000 -0600
@@ -70,8 +70,10 @@
 		io = (void *)events[i].udata;
 		/* there can be multiple events for a single io.
 		   call the callback only once if that happens. */
-		if (io->refcount == 2 && io->io.callback != NULL)
+		if (io->refcount == 2 && io->io.callback != NULL) {
+			io_env_switch(&io->io.env);		/* APPLE */
 			io->io.callback(io->io.context);
+		}
 
 		if (--io->refcount == 0)
 			i_free(io);
@@ -130,6 +132,7 @@
 	io->io.condition = IO_NOTIFY;
 	io->io.callback = callback;
 	io->io.context = context;
+	io_env_init(&io->io.env);		/* APPLE */
 	io->refcount = 1;
 	io->fd = fd;
 
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-poll.c dovecot-patch/src/lib/ioloop-poll.c
--- dovecot-1.1.7+patch9/src/lib/ioloop-poll.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-poll.c	2009-01-06 10:08:19.000000000 -0600
@@ -201,6 +201,7 @@
 
 			if (call) {
 				t_id = t_push();
+				io_env_switch(&io->io.env);	/* APPLE */
 				io->io.callback(io->io.context);
 				if (t_pop() != t_id) {
 					i_panic("Leaked a t_pop() call in "
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop-select.c dovecot-patch/src/lib/ioloop-select.c
--- dovecot-1.1.7+patch9/src/lib/ioloop-select.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop-select.c	2009-01-06 10:08:19.000000000 -0600
@@ -139,6 +139,7 @@
 			ret--;
 
 			t_id = t_push();
+			io_env_switch(&io->io.env);		/* APPLE */
 			io->io.callback(io->io.context);
 			if (t_pop() != t_id) {
 				i_panic("Leaked a t_pop() call in "
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop.c dovecot-patch/src/lib/ioloop.c
--- dovecot-1.1.7+patch9/src/lib/ioloop.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop.c	2009-01-06 10:08:19.000000000 -0600
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "ioloop-internal.h"
+#include "env-util.h"			/* APPLE */
 
 #include <unistd.h>
 
@@ -33,6 +34,7 @@
         io->io.condition = condition;
 	io->io.callback = callback;
         io->io.context = context;
+	io_env_init(&io->io.env);		/* APPLE */
 	io->refcount = 1;
 	io->fd = fd;
 
@@ -75,6 +77,7 @@
 	/* make sure the callback doesn't get called anymore.
 	   kqueue code relies on this. */
 	io->callback = NULL;
+	io_env_deinit(&io->env);		/* APPLE */
 
 	if ((io->condition & IO_NOTIFY) != 0)
 		io_loop_notify_remove(current_ioloop, io);
@@ -132,6 +135,7 @@
 
 	timeout->callback = callback;
 	timeout->context = context;
+	io_env_init(&timeout->env);		/* APPLE */
 
 	timeout_update_next(timeout, current_ioloop->running ?
 			    NULL : &ioloop_timeval);
@@ -144,6 +148,7 @@
 	struct timeout *timeout = *_timeout;
 
 	*_timeout = NULL;
+	io_env_deinit(&timeout->env);		/* APPLE */
 	priorityq_remove(current_ioloop->timeouts, &timeout->item);
 	i_free(timeout);
 }
@@ -295,6 +300,7 @@
 		timeout_reset_timeval(timeout, &tv_call);
 
                 t_id = t_push();
+		io_env_switch(&timeout->env);			/* APPLE */
 		timeout->callback(timeout->context);
 		if (t_pop() != t_id) {
 			i_panic("Leaked a t_pop() call in timeout handler %p",
@@ -375,6 +381,7 @@
 		struct timeout *to = (struct timeout *)item;
 
 		i_warning("Timeout leak: %p", (void *)to->callback);
+		io_env_deinit(&to->env);	/* APPLE */
 		i_free(to);
 	}
 	priorityq_deinit(&ioloop->timeouts);
@@ -388,3 +395,51 @@
 
 	i_free(ioloop);
 }
+
+/* APPLE - rest of file */
+
+static bool io_env_enabled = FALSE;
+
+void io_env_enable(void)
+{
+	i_assert(current_ioloop == NULL);
+
+	io_env_enabled = TRUE;
+}
+
+void io_env_init(struct io_env *env)
+{
+	if (!io_env_enabled)
+		return;
+
+	env->pool = pool_alloconly_create("io_env", 8192);
+	env->env = env_capture(env->pool);
+	restrict_access_cred_init(&env->cred);
+}
+
+void io_env_switch(struct io_env *env)
+{
+	if (!io_env_enabled)
+		return;
+
+	env_switch(env->pool, env->env);
+	restrict_access_cred_switch(&env->cred);
+}
+
+void io_env_clean(void)
+{
+	if (!io_env_enabled)
+		return;
+
+	env_clean();
+	restrict_access_cred_clean();
+}
+
+void io_env_deinit(struct io_env *env)
+{
+	if (!io_env_enabled)
+		return;
+
+	pool_unref(&env->pool);
+	restrict_access_cred_deinit(&env->cred);
+}
diff -Nur dovecot-1.1.7+patch9/src/lib/ioloop.h dovecot-patch/src/lib/ioloop.h
--- dovecot-1.1.7+patch9/src/lib/ioloop.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/ioloop.h	2009-01-06 10:08:19.000000000 -0600
@@ -91,4 +91,8 @@
 /* Destroy I/O loop and set ioloop pointer to NULL. */
 void io_loop_destroy(struct ioloop **ioloop);
 
+/* (APPLE) I/O environment switching */
+void io_env_enable(void);
+void io_env_clean(void);
+
 #endif
diff -Nur dovecot-1.1.7+patch9/src/lib/network.c dovecot-patch/src/lib/network.c
--- dovecot-1.1.7+patch9/src/lib/network.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/network.c	2009-01-06 10:08:19.000000000 -0600
@@ -272,6 +272,12 @@
 #endif
 }
 
+/* APPLE */
+int net_set_sndbuf(int fd, int size)
+{
+	return setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof size);
+}
+
 void net_get_ip_any4(struct ip_addr *ip)
 {
 	ip->family = AF_INET;
diff -Nur dovecot-1.1.7+patch9/src/lib/network.h dovecot-patch/src/lib/network.h
--- dovecot-1.1.7+patch9/src/lib/network.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/network.h	2009-01-06 10:08:19.000000000 -0600
@@ -57,6 +57,8 @@
 /* Set TCP_CORK if supported, ie. don't send out partial frames.
    Returns 0 if ok, -1 if failed. */
 int net_set_cork(int fd, bool cork);
+/* (APPLE) Set socket send buffer size. */
+int net_set_sndbuf(int fd, int size);
 
 /* Set IP to contain INADDR_ANY for IPv4 or IPv6. The IPv6 any address may
    include IPv4 depending on the system (Linux yes, BSD no). */
diff -Nur dovecot-1.1.7+patch9/src/lib/restrict-access.c dovecot-patch/src/lib/restrict-access.c
--- dovecot-1.1.7+patch9/src/lib/restrict-access.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/restrict-access.c	2009-01-06 11:31:00.000000000 -0600
@@ -13,23 +13,55 @@
 #include <time.h>
 #include <grp.h>
 
-static gid_t process_primary_gid = (gid_t)-1;
+/* APPLE significant refactoring around global state variables.
+   process_primary_gid and process_using_priv_gid folded into struct
+   restrict_access_cred. */
+
+static struct restrict_access_cred global_cred;
+static struct restrict_access_cred *current_cred;
+static bool allow_switching;
+
 static gid_t process_privileged_gid = (gid_t)-1;
-static bool process_using_priv_gid = FALSE;
+
+/* APPLE */
+static void restrict_access_init(void)
+{
+	size_t i;
+
+	global_cred.uid = (uid_t) -1;
+	global_cred.gid = (gid_t) -1;
+	global_cred.ngroups = 0;
+	for (i = 0; i < N_ELEMENTS(global_cred.gidset); i++)
+		global_cred.gidset[i] = (gid_t) -1;
+	global_cred.primary_gid = (gid_t) -1;
+	global_cred.using_priv_gid = FALSE;
+
+	current_cred = &global_cred;
+	allow_switching = FALSE;
+}
 
 void restrict_access_set_env(const char *user, uid_t uid,
 			     gid_t gid, gid_t privileged_gid,
 			     const char *chroot_dir,
 			     gid_t first_valid_gid, gid_t last_valid_gid,
-			     const char *extra_groups)
+			     const char *extra_groups,
+			     bool lenient)		/* APPLE */
 {
 	if (user != NULL && *user != '\0')
 		env_put(t_strconcat("RESTRICT_USER=", user, NULL));
 	if (chroot_dir != NULL && *chroot_dir != '\0')
 		env_put(t_strconcat("RESTRICT_CHROOT=", chroot_dir, NULL));
 
+	if (lenient)			/* APPLE */
+		env_put(t_strdup_printf("ADVISE_SETUID=%s", dec2str(uid)));
+	else		/* APPLE reduce code deltas */
 	env_put(t_strdup_printf("RESTRICT_SETUID=%s", dec2str(uid)));
+
+	if (lenient)			/* APPLE */
+		env_put(t_strdup_printf("ADVISE_SETGID=%s", dec2str(gid)));
+	else		/* APPLE reduce code deltas */
 	env_put(t_strdup_printf("RESTRICT_SETGID=%s", dec2str(gid)));
+
 	if (privileged_gid != (gid_t)-1) {
 		env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s",
 					dec2str(privileged_gid)));
@@ -158,11 +190,13 @@
 	const char *const *tmp, *empty = NULL;
 	unsigned int i, gid_count;
 	bool add_primary_gid;
+	gid_t process_primary_gid;		/* APPLE reduce code deltas */
 
 	/* if we're using a privileged GID, we can temporarily drop our
 	   effective GID. we still want to be able to use its privileges,
 	   so add it to supplementary groups. */
 	add_primary_gid = process_privileged_gid != (gid_t)-1;
+	process_primary_gid = global_cred.primary_gid;		/* APPLE */
 
 	tmp = extra_groups == NULL ? &empty :
 		t_strsplit_spaces(extra_groups, ", ");
@@ -217,15 +251,40 @@
 
 void restrict_access_by_env(bool disallow_root)
 {
+	const char *restrict_setgid, *advise_setgid;		/* APPLE */
+	const char *restrict_setuid, *advise_setuid;		/* APPLE */
 	const char *env;
 	uid_t uid;
 	bool is_root, have_root_group, preserve_groups = FALSE;
 	bool allow_root_gid;
+	gid_t process_primary_gid;		/* APPLE reduce code deltas */
+
+	/* APPLE */
+	if (current_cred == NULL)
+		restrict_access_init();
 
 	is_root = geteuid() == 0;
 
+	/* APPLE */
+	restrict_setgid = getenv("RESTRICT_SETGID");
+	advise_setgid = getenv("ADVISE_SETGID");
+	restrict_setuid = getenv("RESTRICT_SETUID");
+	advise_setuid = getenv("ADVISE_SETUID");
+
+	/* APPLE */
+	allow_switching =
+		restrict_setgid == NULL && restrict_setuid == NULL &&
+		advise_setgid != NULL && advise_setuid != NULL;
+	if (allow_switching && !is_root) {
+		if (seteuid(0) < 0)
+			i_fatal("seteuid(0) to allow switching failed "
+				"with uid=%u, euid=%u: %m",
+				getuid(), geteuid());
+	}
+	i_assert(!global_cred.using_priv_gid);
+
 	/* set the primary/privileged group */
-	env = getenv("RESTRICT_SETGID");
+	env = restrict_setgid != NULL ? restrict_setgid : advise_setgid;
 	process_primary_gid = env == NULL || *env == '\0' ? (gid_t)-1 :
 		(gid_t)strtoul(env, NULL, 10);
 	env = getenv("RESTRICT_SETGID_PRIV");
@@ -237,6 +296,14 @@
 	    process_privileged_gid != (gid_t)-1) {
 		if (process_primary_gid == (gid_t)-1)
 			process_primary_gid = getegid();
+
+		/* APPLE */
+		if (allow_switching) {
+			if (setgid(process_primary_gid) < 0)
+				i_fatal("setgid(%u) to allow switching failed "
+					"with euid=%u: %m",
+					process_primary_gid, geteuid());
+		} else		/* APPLE reduce code deltas */
 		restrict_init_groups(process_primary_gid,
 				     process_privileged_gid);
 	} else {
@@ -244,6 +311,10 @@
 			process_primary_gid = getegid();
 	}
 
+	/* APPLE */
+	global_cred.primary_gid = process_primary_gid;
+	global_cred.gid = process_primary_gid;
+
 	/* set system user's groups */
 	env = getenv("RESTRICT_USER");
 	if (env != NULL && *env != '\0' && is_root) {
@@ -261,6 +332,17 @@
 		fix_groups_list(env, preserve_groups, &have_root_group);
 	} T_END;
 
+	/* APPLE */
+	global_cred.ngroups = getgroups(N_ELEMENTS(global_cred.gidset),
+					global_cred.gidset);
+	if (global_cred.ngroups < 0)
+		i_fatal("getgroups() failed: %m");
+	else {
+		size_t i = global_cred.ngroups;
+		while (i < N_ELEMENTS(global_cred.gidset))
+			global_cred.gidset[i++] = (gid_t) -1;
+	}
+
 	/* chrooting */
 	env = getenv("RESTRICT_CHROOT");
 	if (env != NULL && *env != '\0') {
@@ -286,16 +368,32 @@
 	}
 
 	/* uid last */
-	env = getenv("RESTRICT_SETUID");
+	env = restrict_setuid != NULL ? restrict_setuid : advise_setuid;
 	uid = env == NULL || *env == '\0' ? 0 : (uid_t)strtoul(env, NULL, 10);
 	if (uid != 0) {
+		/* APPLE */
+		if (allow_switching) {
+			if (seteuid(uid) < 0)
+				i_fatal("seteuid(%u) to allow switching failed "
+					"with uid=%u, euid=%u: %m",
+					uid, getuid(), geteuid());
+		} else		/* APPLE reduce code deltas */
 		if (setuid(uid) != 0) {
 			i_fatal("setuid(%s) failed with euid=%s: %m",
 				dec2str(uid), dec2str(geteuid()));
 		}
 	}
 
+	/* APPLE */
+	global_cred.uid = uid;
+
 	/* verify that we actually dropped the privileges */
+
+	/* APPLE */
+	if (allow_switching) {
+		if (geteuid() == 0)
+			i_fatal("Running as user root isn't permitted");
+	} else		/* APPLE reduce code deltas */
 	if (uid != 0 || disallow_root) {
 		if (setuid(0) == 0) {
 			if (uid == 0)
@@ -312,6 +410,11 @@
 	else
 		allow_root_gid = FALSE;
 
+	/* APPLE */
+	if (allow_switching) {
+		if (getgid() == 0 || getegid() == 0)
+			i_fatal("Running as group 0 isn't permitted");
+	} else		/* APPLE reduce code deltas */
 	if (!allow_root_gid && uid != 0) {
 		if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
 			if (process_primary_gid == 0)
@@ -323,15 +426,20 @@
 		}
 	}
 
+	/* APPLE */
+	current_cred = &global_cred;
+
 	/* clear the environment, so we don't fail if we get back here */
 	env_put("RESTRICT_USER=");
 	env_put("RESTRICT_CHROOT=");
 	env_put("RESTRICT_SETUID=");
+	env_put("ADVISE_SETUID=");		/* APPLE */
 	if (process_privileged_gid == (gid_t)-1) {
 		/* if we're dropping privileges before executing and
 		   a privileged group is set, the groups must be fixed
 		   after exec */
 		env_put("RESTRICT_SETGID=");
+		env_put("ADVISE_SETGID=");	/* APPLE */
 		env_put("RESTRICT_SETGID_PRIV=");
 	}
 	env_put("RESTRICT_SETEXTRAGROUPS=");
@@ -341,7 +449,12 @@
 
 int restrict_access_use_priv_gid(void)
 {
-	i_assert(!process_using_priv_gid);
+	/* APPLE */
+	if (current_cred == NULL)
+		restrict_access_init();
+	i_assert(!allow_switching || current_cred != &global_cred);
+
+	i_assert(!current_cred->using_priv_gid);
 
 	if (process_privileged_gid == (gid_t)-1)
 		return 0;
@@ -349,21 +462,99 @@
 		i_error("setegid(privileged) failed: %m");
 		return -1;
 	}
-	process_using_priv_gid = TRUE;
+	current_cred->using_priv_gid = TRUE;
 	return 0;
 }
 
 void restrict_access_drop_priv_gid(void)
 {
-	if (!process_using_priv_gid)
+	/* APPLE */
+	if (current_cred == NULL)
+		restrict_access_init();
+	i_assert(!allow_switching || current_cred != &global_cred);
+
+	if (!current_cred->using_priv_gid)
 		return;
 
-	if (setegid(process_primary_gid) < 0)
+	if (setegid(current_cred->primary_gid) < 0)
 		i_fatal("setegid(primary) failed: %m");
-	process_using_priv_gid = FALSE;
+	current_cred->using_priv_gid = FALSE;
 }
 
 bool restrict_access_have_priv_gid(void)
 {
+	/* APPLE */
+	if (current_cred == NULL)
+		restrict_access_init();
+
 	return process_privileged_gid != (gid_t)-1;
 }
+
+/* APPLE - rest of file */
+
+void restrict_access_cred_init(struct restrict_access_cred *cred)
+{
+	i_assert(cred != current_cred);
+
+	*cred = *current_cred;
+}
+
+void restrict_access_cred_switch(struct restrict_access_cred *cred)
+{
+	gid_t gid, current_gid;
+	bool switch_gid, switch_groups, switch_uid;
+
+	if (current_cred == cred)
+		return;
+
+	gid = cred->using_priv_gid ? process_privileged_gid : cred->primary_gid;
+	current_gid = current_cred->using_priv_gid ? process_privileged_gid : current_cred->primary_gid;
+	switch_gid = gid != current_gid;
+	switch_groups = cred->ngroups != current_cred->ngroups ||
+		memcmp(cred->gidset, current_cred->gidset,
+		       cred->ngroups * sizeof cred->gidset[0]) != 0;
+	switch_uid = cred->uid != current_cred->uid;
+
+	if (switch_gid || switch_groups || switch_uid) {
+		if (seteuid(0) < 0)
+			i_fatal("seteuid(0) to switch cred failed "
+				"with uid=%u, euid=%u: %m",
+				getuid(), geteuid());
+	}
+	if (switch_gid) {
+		if (setegid(gid) < 0)
+			i_fatal("setegid(%u) failed with euid=%u: %m",
+				gid, geteuid());
+	}
+	if (switch_groups) {
+		if (setgroups(cred->ngroups, cred->gidset) < 0)
+			i_fatal("setgroups() failed with euid=%u: %m",
+				geteuid());
+	}
+	if (switch_gid || switch_groups || switch_uid) {
+		if (seteuid(cred->uid) < 0)
+			i_fatal("seteuid(%u) failed with uid=%u, euid=%u: %m",
+				cred->uid, getuid(), geteuid());
+	}
+
+	current_cred = cred;
+
+	if (getuid() != 0 || geteuid() != cred->uid || getegid() != gid)
+		i_panic("pid %d: restrict_access_cred_switch failed;"
+			" uid=%u euid=%u gid=%u egid=%u", getpid(), getuid(),
+			geteuid(), getgid(), getegid());
+}
+
+void restrict_access_cred_clean(void)
+{
+	restrict_access_cred_switch(&global_cred);
+}
+
+void restrict_access_cred_deinit(struct restrict_access_cred *cred)
+{
+	i_assert(cred != &global_cred);
+	if (current_cred == cred) {
+		global_cred = *cred;
+		current_cred = &global_cred;
+	}
+}
diff -Nur dovecot-1.1.7+patch9/src/lib/restrict-access.h dovecot-patch/src/lib/restrict-access.h
--- dovecot-1.1.7+patch9/src/lib/restrict-access.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib/restrict-access.h	2009-01-06 10:08:19.000000000 -0600
@@ -1,6 +1,16 @@
 #ifndef RESTRICT_ACCESS_H
 #define RESTRICT_ACCESS_H
 
+/* APPLE */
+struct restrict_access_cred {
+	uid_t uid;
+	gid_t gid;
+	int ngroups;
+	gid_t gidset[NGROUPS_MAX];
+	gid_t primary_gid;
+	unsigned int using_priv_gid:1;
+};
+
 /* set environment variables so they can be read with
    restrict_access_by_env(). If privileged_gid != (gid_t)-1,
    the privileged GID can be temporarily enabled/disabled. */
@@ -8,7 +18,8 @@
 			     gid_t gid, gid_t privileged_gid,
 			     const char *chroot_dir,
 			     gid_t first_valid_gid, gid_t last_valid_gid,
-			     const char *extra_groups);
+			     const char *extra_groups,
+			     bool lenient);		/* APPLE */
 
 /* chroot, setuid() and setgid() based on environment variables.
    If disallow_roots is TRUE, we'll kill ourself if we didn't have the
@@ -22,4 +33,10 @@
 /* Returns TRUE if privileged GID exists for this process. */
 bool restrict_access_have_priv_gid(void);
 
+/* (APPLE) Credential switching */
+void restrict_access_cred_init(struct restrict_access_cred *cred);
+void restrict_access_cred_switch(struct restrict_access_cred *cred);
+void restrict_access_cred_clean(void);
+void restrict_access_cred_deinit(struct restrict_access_cred *cred);
+
 #endif
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/Makefile.am dovecot-patch/src/lib-storage/Makefile.am
--- dovecot-1.1.7+patch9/src/lib-storage/Makefile.am	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/Makefile.am	2009-01-06 10:08:19.000000000 -0600
@@ -15,6 +15,7 @@
 	mail-namespace.c \
 	mail-search.c \
 	mail-storage.c \
+	mail-user.c \
 	mailbox-list.c \
 	mailbox-tree.c \
 	mailbox-uidvalidity.c
@@ -26,6 +27,7 @@
 	mail-search.h \
 	mail-storage.h \
 	mail-storage-private.h \
+	mail-user.h \
 	mailbox-list.h \
 	mailbox-list-private.h \
 	mailbox-tree.h \
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/index/cydir/cydir-storage.c dovecot-patch/src/lib-storage/index/cydir/cydir-storage.c
--- dovecot-1.1.7+patch9/src/lib-storage/index/cydir/cydir-storage.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/index/cydir/cydir-storage.c	2009-01-06 10:08:19.000000000 -0600
@@ -34,10 +34,10 @@
 
 static int
 cydir_get_list_settings(struct mailbox_list_settings *list_set,
-			const char *data, enum mail_storage_flags flags,
+			const char *data, struct mail_storage *storage,
 			const char **layout_r, const char **error_r)
 {
-	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 
 	*layout_r = "fs";
 
@@ -55,8 +55,8 @@
 
 	if (debug)
 		i_info("cydir: data=%s", data);
-	return mailbox_list_settings_parse(data, list_set, layout_r, NULL,
-					   error_r);
+	return mailbox_list_settings_parse(data, list_set, storage->ns,
+					   layout_r, NULL, error_r);
 }
 
 static struct mail_storage *cydir_alloc(void)
@@ -80,7 +80,7 @@
 	struct stat st;
 	const char *layout;
 
-	if (cydir_get_list_settings(&list_set, data, _storage->flags,
+	if (cydir_get_list_settings(&list_set, data, _storage,
 				    &layout, error_r) < 0)
 		return -1;
 	list_set.mail_storage_flags = &_storage->flags;
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/index/dbox/dbox-storage.c dovecot-patch/src/lib-storage/index/dbox/dbox-storage.c
--- dovecot-1.1.7+patch9/src/lib-storage/index/dbox/dbox-storage.c	2008-11-15 10:34:22.000000000 -0600
+++ dovecot-patch/src/lib-storage/index/dbox/dbox-storage.c	2009-01-06 10:08:19.000000000 -0600
@@ -46,11 +46,11 @@
 
 static int
 dbox_get_list_settings(struct mailbox_list_settings *list_set,
-		       const char *data, enum mail_storage_flags flags,
+		       const char *data, struct mail_storage *storage,
 		       const char **layout_r, const char **alt_dir_r,
 		       const char **error_r)
 {
-	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 
 	*layout_r = "fs";
 
@@ -68,8 +68,8 @@
 
 	if (debug)
 		i_info("dbox: data=%s", data);
-	return mailbox_list_settings_parse(data, list_set, layout_r, alt_dir_r,
-					   error_r);
+	return mailbox_list_settings_parse(data, list_set, storage->ns,
+					   layout_r, alt_dir_r, error_r);
 }
 
 static struct mail_storage *dbox_alloc(void)
@@ -93,7 +93,7 @@
 	struct stat st;
 	const char *layout, *alt_dir;
 
-	if (dbox_get_list_settings(&list_set, data, _storage->flags,
+	if (dbox_get_list_settings(&list_set, data, _storage,
 				   &layout, &alt_dir, error_r) < 0)
 		return -1;
 	list_set.mail_storage_flags = &_storage->flags;
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/index/maildir/maildir-storage.c dovecot-patch/src/lib-storage/index/maildir/maildir-storage.c
--- dovecot-1.1.7+patch9/src/lib-storage/index/maildir/maildir-storage.c	2008-11-15 10:47:56.000000000 -0600
+++ dovecot-patch/src/lib-storage/index/maildir/maildir-storage.c	2009-01-06 10:08:19.000000000 -0600
@@ -5,7 +5,6 @@
 #include "array.h"
 #include "hostpid.h"
 #include "str.h"
-#include "home-expand.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
 #include "unlink-old-files.h"
@@ -63,11 +62,13 @@
 
 static int
 maildir_get_list_settings(struct mailbox_list_settings *list_set,
-			  const char *data, enum mail_storage_flags flags,
+			  const char *data, struct mail_storage *storage,
 			  const char **layout_r, const char **error_r)
 {
+	enum mail_storage_flags flags = storage->flags;
+	struct mail_user *user = storage->ns->user;
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
-	const char *home, *path;
+	const char *path;
 
 	*layout_r = MAILDIR_PLUSPLUS_DRIVER_NAME;
 
@@ -82,9 +83,9 @@
 		}
 
 		/* we'll need to figure out the maildir location ourself.
-		   It's $HOME/Maildir unless we are chrooted. */
-		if ((home = getenv("HOME")) != NULL) {
-			path = t_strconcat(home, "/Maildir", NULL);
+		   It's ~/Maildir unless we are chrooted. */
+		if (user->home != NULL) {
+			path = t_strconcat(user->home, "/Maildir", NULL);
 			if (access(path, R_OK|W_OK|X_OK) == 0) {
 				if (debug) {
 					i_info("maildir: root exists (%s)",
@@ -99,7 +100,7 @@
 			}
 		} else {
 			if (debug)
-				i_info("maildir: HOME not set");
+				i_info("maildir: Home directory not set");
 		}
 
 		if (access("/cur", R_OK|W_OK|X_OK) == 0) {
@@ -110,8 +111,8 @@
 	} else {
 		if (debug)
 			i_info("maildir: data=%s", data);
-		if (mailbox_list_settings_parse(data, list_set, layout_r, NULL,
-						error_r) < 0)
+		if (mailbox_list_settings_parse(data, list_set, storage->ns,
+						layout_r, NULL, error_r) < 0)
 			return -1;
 	}
 
@@ -194,7 +195,7 @@
 	const char *layout;
 	struct stat st;
 
-	if (maildir_get_list_settings(&list_set, data, flags, &layout,
+	if (maildir_get_list_settings(&list_set, data, _storage, &layout,
 				      error_r) < 0)
 		return -1;
 	list_set.mail_storage_flags = &_storage->flags;
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/index/mbox/mbox-save.c dovecot-patch/src/lib-storage/index/mbox/mbox-save.c
--- dovecot-1.1.7+patch9/src/lib-storage/index/mbox/mbox-save.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/index/mbox/mbox-save.c	2009-01-06 10:08:19.000000000 -0600
@@ -136,9 +136,11 @@
 		const char *line;
 
 		if (from_envelope == NULL) {
-			from_envelope =
-				t_strconcat(ctx->mbox->storage->storage.user,
-					    "@", my_hostdomain, NULL);
+			struct mail_storage *storage =
+				&ctx->mbox->storage->storage;
+
+			from_envelope = t_strconcat(storage->ns->user->username,
+						    "@", my_hostdomain, NULL);
 		}
 
 		/* save in local timezone, no matter what it was given with */
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/index/mbox/mbox-storage.c dovecot-patch/src/lib-storage/index/mbox/mbox-storage.c
--- dovecot-1.1.7+patch9/src/lib-storage/index/mbox/mbox-storage.c	2008-11-15 09:52:45.000000000 -0600
+++ dovecot-patch/src/lib-storage/index/mbox/mbox-storage.c	2009-01-06 10:08:19.000000000 -0600
@@ -7,7 +7,6 @@
 #include "restrict-access.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
-#include "home-expand.h"
 #include "mbox-storage.h"
 #include "mbox-lock.h"
 #include "mbox-file.h"
@@ -186,12 +185,12 @@
 	return FALSE;
 }
 
-static const char *get_root_dir(enum mail_storage_flags flags)
+static const char *get_root_dir(struct mail_storage *storage)
 {
 	const char *home, *path;
-	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 
-	home = getenv("HOME");
+	home = storage->ns->user->home;
 	if (home != NULL) {
 		path = t_strconcat(home, "/mail", NULL);
 		if (access(path, R_OK|W_OK|X_OK) == 0) {
@@ -214,7 +213,7 @@
 
 	if (debug)
 		i_info("mbox: checking if we are chrooted:");
-	if (mbox_autodetect("", flags))
+	if (mbox_autodetect("", storage->flags))
 		return "/";
 
 	if (debug)
@@ -224,11 +223,12 @@
 }
 
 static const char *
-get_inbox_file(const char *root_dir, bool only_root, bool debug)
+get_inbox_file(const char *user, const char *root_dir,
+	       bool only_root, bool debug)
 {
-	const char *user, *path;
+	const char *path;
 
-	if (!only_root && (user = getenv("USER")) != NULL) {
+	if (!only_root) {
 		path = t_strconcat("/var/mail/", user, NULL);
 		if (access(path, R_OK|W_OK) == 0) {
 			if (debug)
@@ -254,11 +254,12 @@
 	return path;
 }
 
-static const char *create_root_dir(bool debug, const char **error_r)
+static const char *create_root_dir(struct mail_storage *storage,
+				   const char **error_r)
 {
 	const char *home, *path;
 
-	home = getenv("HOME");
+	home = storage->ns->user->home;
 	if (home == NULL) {
 		*error_r = "Root mail directory not set and "
 			"home directory is missing";
@@ -271,16 +272,17 @@
 		return NULL;
 	}
 
-	if (debug)
+	if ((storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0)
 		i_info("mbox: root directory created: %s", path);
 	return path;
 }
 
 static int
 mbox_get_list_settings(struct mailbox_list_settings *list_set,
-		       const char *data, enum mail_storage_flags flags,
+		       const char *data, struct mail_storage *storage,
 		       const char **layout_r, const char **error_r)
 {
+	enum mail_storage_flags flags = storage->flags;
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 	const char *p;
 	struct stat st;
@@ -301,8 +303,8 @@
 
 		/* 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 */
-		list_set->root_dir = get_root_dir(flags);
+		   either ~/mail or ~/Mail */
+		list_set->root_dir = get_root_dir(storage);
 	} else {
 		if (debug)
 			i_info("mbox: data=%s", data);
@@ -310,11 +312,11 @@
 		if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) == 0 &&
 		    p == NULL && data[strlen(data)-1] != '/') {
 			/* if the data points to a file, treat it as an INBOX */
-			data = home_expand(data);
+			data = mail_user_home_expand(storage->ns->user, data);
 			if (stat(data, &st) < 0 || S_ISDIR(st.st_mode))
 				list_set->root_dir = data;
 			else {
-				list_set->root_dir = get_root_dir(flags);
+				list_set->root_dir = get_root_dir(storage);
 				list_set->inbox_path = data;
 			}
 		} else if (strncmp(data, "INBOX=", 6) == 0) {
@@ -323,6 +325,7 @@
 			return -1;
 		} else {
 			if (mailbox_list_settings_parse(data, list_set,
+							storage->ns,
 							layout_r, NULL,
 							error_r) < 0)
 				return -1;
@@ -335,7 +338,7 @@
 			return -1;
 		}
 
-		list_set->root_dir = create_root_dir(debug, error_r);
+		list_set->root_dir = create_root_dir(storage, error_r);
 		if (list_set->root_dir == NULL)
 			return -1;
 	} else {
@@ -365,7 +368,8 @@
 
 	if (list_set->inbox_path == NULL) {
 		list_set->inbox_path =
-			get_inbox_file(list_set->root_dir, !autodetect, debug);
+			get_inbox_file(storage->ns->user->username,
+				       list_set->root_dir, !autodetect, debug);
 	}
 	return 0;
 }
@@ -437,8 +441,8 @@
 	struct mailbox_list_settings list_set;
 	const char *layout;
 
-	if (mbox_get_list_settings(&list_set, data,
-				   _storage->flags, &layout, error_r) < 0)
+	if (mbox_get_list_settings(&list_set, data, _storage,
+				   &layout, error_r) < 0)
 		return -1;
 	list_set.mail_storage_flags = &_storage->flags;
 	list_set.lock_method = &_storage->lock_method;
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/index/raw/raw-storage.c dovecot-patch/src/lib-storage/index/raw/raw-storage.c
--- dovecot-1.1.7+patch9/src/lib-storage/index/raw/raw-storage.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/index/raw/raw-storage.c	2009-01-06 10:08:19.000000000 -0600
@@ -27,10 +27,10 @@
 
 static int
 raw_get_list_settings(struct mailbox_list_settings *list_set,
-		      const char *data, enum mail_storage_flags flags,
+		      const char *data, struct mail_storage *storage,
 		      const char **layout_r, const char **error_r)
 {
-	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 
 	*layout_r = "fs";
 
@@ -48,8 +48,8 @@
 
 	if (debug)
 		i_info("raw: data=%s", data);
-	return mailbox_list_settings_parse(data, list_set, layout_r, NULL,
-					   error_r);
+	return mailbox_list_settings_parse(data, list_set, storage->ns,
+					   layout_r, NULL, error_r);
 }
 
 static struct mail_storage *raw_alloc(void)
@@ -73,7 +73,7 @@
 	struct stat st;
 	const char *layout;
 
-	if (raw_get_list_settings(&list_set, data, _storage->flags,
+	if (raw_get_list_settings(&list_set, data, _storage,
 				  &layout, error_r) < 0)
 		return -1;
 	list_set.mail_storage_flags = &_storage->flags;
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mail-namespace.c dovecot-patch/src/lib-storage/mail-namespace.c
--- dovecot-1.1.7+patch9/src/lib-storage/mail-namespace.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/mail-namespace.c	2009-01-06 10:08:19.000000000 -0600
@@ -28,14 +28,14 @@
 }
 
 static struct mail_namespace *
-namespace_add_env(pool_t pool, const char *data, unsigned int num,
-		  const char *user, enum mail_storage_flags flags,
+namespace_add_env(const char *data, unsigned int num,
+		  struct mail_user *user, enum mail_storage_flags flags,
 		  enum file_lock_method lock_method)
 {
         struct mail_namespace *ns;
 	const char *sep, *type, *prefix, *error;
 
-	ns = p_new(pool, struct mail_namespace, 1);
+	ns = p_new(user->pool, struct mail_namespace, 1);
 
 	sep = getenv(t_strdup_printf("NAMESPACE_%u_SEP", num));
 	type = getenv(t_strdup_printf("NAMESPACE_%u_TYPE", num));
@@ -76,9 +76,10 @@
 
 	if (sep != NULL)
 		ns->sep = *sep;
-	ns->prefix = p_strdup(pool, prefix);
+	ns->prefix = p_strdup(user->pool, prefix);
+	ns->user = user;
 
-	if (mail_storage_create(ns, NULL, data, user, flags, lock_method,
+	if (mail_storage_create(ns, NULL, data, flags, lock_method,
 				&error) < 0) {
 		i_error("Namespace '%s': %s", ns->prefix, error);
 		return NULL;
@@ -159,26 +160,7 @@
 	return TRUE;
 }
 
-static struct mail_namespace *
-namespaces_sort(struct mail_namespace *src)
-{
-	struct mail_namespace **tmp, *next, *dest = NULL;
-
-	for (; src != NULL; src = next) {
-		next = src->next;
-
-		for (tmp = &dest; *tmp != NULL; tmp = &(*tmp)->next) {
-			if (strlen(src->prefix) < strlen((*tmp)->prefix))
-				break;
-		}
-		src->next = *tmp;
-		*tmp = src;
-	}
-	return dest;
-}
-
-int mail_namespaces_init(pool_t pool, const char *user,
-			 struct mail_namespace **namespaces_r)
+int mail_namespaces_init(struct mail_user *user)
 {
 	struct mail_namespace *namespaces, *ns, **ns_p;
 	enum mail_storage_flags flags;
@@ -199,7 +181,7 @@
 			break;
 
 		T_BEGIN {
-			*ns_p = namespace_add_env(pool, data, i, user, flags,
+			*ns_p = namespace_add_env(data, i, user, flags,
 						  lock_method);
 		} T_END;
 
@@ -212,8 +194,7 @@
 	if (namespaces != NULL) {
 		if (!namespaces_check(namespaces))
 			return -1;
-		namespaces = namespaces_sort(namespaces);
-		*namespaces_r = namespaces;
+		mail_user_add_namespace(user, namespaces);
 
 		if (hook_mail_namespaces_created != NULL) {
 			T_BEGIN {
@@ -232,13 +213,14 @@
 			mail = t_strconcat("maildir:", mail, NULL);
 	}
 
-	ns = p_new(pool, struct mail_namespace, 1);
+	ns = p_new(user->pool, struct mail_namespace, 1);
 	ns->type = NAMESPACE_PRIVATE;
 	ns->flags = NAMESPACE_FLAG_INBOX | NAMESPACE_FLAG_LIST |
 		NAMESPACE_FLAG_SUBSCRIPTIONS;
 	ns->prefix = "";
+	ns->user = user;
 
-	if (mail_storage_create(ns, NULL, mail, user, flags, lock_method,
+	if (mail_storage_create(ns, NULL, mail, flags, lock_method,
 				&error) < 0) {
 		if (mail != NULL && *mail != '\0')
 			i_error("mail_location: %s", error);
@@ -248,7 +230,7 @@
 		}
 		return -1;
 	}
-	*namespaces_r = ns;
+	user->namespaces = ns;
 
 	if (hook_mail_namespaces_created != NULL) {
 		T_BEGIN {
@@ -258,14 +240,17 @@
 	return 0;
 }
 
-struct mail_namespace *mail_namespaces_init_empty(pool_t pool)
+struct mail_namespace *
+mail_namespaces_init_empty(struct mail_user *user)
 {
 	struct mail_namespace *ns;
 
-	ns = p_new(pool, struct mail_namespace, 1);
+	ns = p_new(user->pool, struct mail_namespace, 1);
+	ns->user = user;
 	ns->prefix = "";
 	ns->flags = NAMESPACE_FLAG_INBOX | NAMESPACE_FLAG_LIST |
 		NAMESPACE_FLAG_SUBSCRIPTIONS;
+	user->namespaces = ns;
 	return ns;
 }
 
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mail-namespace.h dovecot-patch/src/lib-storage/mail-namespace.h
--- dovecot-1.1.7+patch9/src/lib-storage/mail-namespace.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/mail-namespace.h	2009-01-06 10:08:19.000000000 -0600
@@ -1,6 +1,8 @@
 #ifndef MAIL_NAMESPACE_H
 #define MAIL_NAMESPACE_H
 
+#include "mail-user.h"
+
 enum namespace_type {
 	NAMESPACE_PRIVATE,
 	NAMESPACE_SHARED,
@@ -32,6 +34,7 @@
 	const char *prefix;
 	size_t prefix_len;
 
+	struct mail_user *user;
 	struct mailbox_list *list;
 	/* FIXME: we should support multiple storages in one namespace */
 	struct mail_storage *storage;
@@ -40,9 +43,8 @@
 /* Called after namespaces has been created */
 extern void (*hook_mail_namespaces_created)(struct mail_namespace *namespaces);
 
-int mail_namespaces_init(pool_t pool, const char *user,
-			 struct mail_namespace **namespaces_r);
-struct mail_namespace *mail_namespaces_init_empty(pool_t pool);
+int mail_namespaces_init(struct mail_user *user);
+struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user);
 void mail_namespaces_deinit(struct mail_namespace **namespaces);
 
 /* Update hierarchy separators in given name to real_sep characters. */
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mail-storage-private.h dovecot-patch/src/lib-storage/mail-storage-private.h
--- dovecot-1.1.7+patch9/src/lib-storage/mail-storage-private.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/mail-storage-private.h	2009-01-06 10:08:19.000000000 -0600
@@ -59,7 +59,6 @@
 	struct mail_namespace *ns;
 	struct mailbox_list *list;
 
-	const char *user; /* name of user accessing the storage */
 	enum mail_storage_flags flags;
 	enum file_lock_method lock_method;
 	unsigned int keyword_max_len;
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mail-storage.c dovecot-patch/src/lib-storage/mail-storage.c
--- dovecot-1.1.7+patch9/src/lib-storage/mail-storage.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/mail-storage.c	2009-01-06 10:08:19.000000000 -0600
@@ -167,8 +167,7 @@
 }
 
 int mail_storage_create(struct mail_namespace *ns, const char *driver,
-			const char *data, const char *user,
-			enum mail_storage_flags flags,
+			const char *data, enum mail_storage_flags flags,
 			enum file_lock_method lock_method,
 			const char **error_r)
 {
@@ -212,7 +211,6 @@
 		storage = classes[i]->v.alloc();
 		storage->flags = flags;
 		storage->lock_method = lock_method;
-		storage->user = p_strdup(storage->pool, user);
 		storage->ns = ns;
 
 		storage->callbacks =
@@ -237,7 +235,7 @@
 			return -1;
 		}
 
-		home = getenv("HOME");
+		home = ns->user->home;
 		if (home == NULL || *home == '\0') home = "(not set)";
 
 		*error_r = t_strdup_printf(
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mail-storage.h dovecot-patch/src/lib-storage/mail-storage.h
--- dovecot-1.1.7+patch9/src/lib-storage/mail-storage.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/mail-storage.h	2009-01-06 10:08:19.000000000 -0600
@@ -219,8 +219,7 @@
    from data. If data is NULL, it uses the first storage that exists.
    The storage is put into ns->storage. */
 int mail_storage_create(struct mail_namespace *ns, const char *driver,
-			const char *data, const char *user,
-			enum mail_storage_flags flags,
+			const char *data, enum mail_storage_flags flags,
 			enum file_lock_method lock_method,
 			const char **error_r);
 void mail_storage_destroy(struct mail_storage **storage);
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mail-user.c dovecot-patch/src/lib-storage/mail-user.c
--- dovecot-1.1.7+patch9/src/lib-storage/mail-user.c	1969-12-31 18:00:00.000000000 -0600
+++ dovecot-patch/src/lib-storage/mail-user.c	2009-01-06 10:08:19.000000000 -0600
@@ -0,0 +1,79 @@
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "mail-namespace.h"
+#include "mail-user.h"
+
+struct mail_user_module_register mail_user_module_register = { 0 };
+void (*hook_mail_user_created)(struct mail_user *user) = NULL;
+
+static void mail_user_deinit_base(struct mail_user *user)
+{
+	mail_namespaces_deinit(&user->namespaces);
+	pool_unref(&user->pool);
+}
+
+struct mail_user *mail_user_init(const char *username, const char *home)
+{
+	struct mail_user *user;
+	pool_t pool;
+
+	i_assert(username != NULL);
+
+	pool = pool_alloconly_create("mail user", 512);
+	user = p_new(pool, struct mail_user, 1);
+	user->pool = pool;
+	user->username = p_strdup(pool, username);
+	user->home = p_strdup(pool, home);
+	user->v.deinit = mail_user_deinit_base;
+	p_array_init(&user->module_contexts, user->pool, 5);
+
+	if (hook_mail_user_created != NULL)
+		hook_mail_user_created(user);
+	return user;
+}
+
+void mail_user_deinit(struct mail_user **_user)
+{
+	struct mail_user *user = *_user;
+
+	*_user = NULL;
+	user->v.deinit(user);
+}
+
+void mail_user_add_namespace(struct mail_user *user, struct mail_namespace *ns)
+{
+	struct mail_namespace **tmp, *next;
+
+	for (; ns != NULL; ns = next) {
+		next = ns->next;
+
+		tmp = &user->namespaces;
+		for (; *tmp != NULL; tmp = &(*tmp)->next) {
+			if (strlen(ns->prefix) < strlen((*tmp)->prefix))
+				break;
+		}
+		ns->next = *tmp;
+		*tmp = ns;
+	}
+}
+
+const char *mail_user_home_expand(struct mail_user *user, const char *path)
+{
+	(void)mail_user_try_home_expand(user, &path);
+	return path;
+}
+
+int mail_user_try_home_expand(struct mail_user *user, const char **pathp)
+{
+	const char *path = *pathp;
+
+	if (path[0] == '~' && (path[1] == '/' || path[1] == '\0')) {
+		if (user->home == NULL)
+			return -1;
+
+		*pathp = t_strconcat(user->home, path + 1, NULL);
+	}
+	return 0;
+}
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mail-user.h dovecot-patch/src/lib-storage/mail-user.h
--- dovecot-1.1.7+patch9/src/lib-storage/mail-user.h	1969-12-31 18:00:00.000000000 -0600
+++ dovecot-patch/src/lib-storage/mail-user.h	2009-01-06 10:08:19.000000000 -0600
@@ -0,0 +1,47 @@
+#ifndef MAIL_USER_H
+#define MAIL_USER_H
+
+struct mail_user;
+
+struct mail_user_vfuncs {
+	void (*deinit)(struct mail_user *user);
+};
+
+struct mail_user {
+	pool_t pool;
+	struct mail_user_vfuncs v;
+
+	const char *username;
+	const char *home;
+
+	struct mail_namespace *namespaces;
+
+	/* Module-specific contexts. See mail_storage_module_id. */
+	ARRAY_DEFINE(module_contexts, union mail_user_module_context *);
+};
+
+struct mail_user_module_register {
+	unsigned int id;
+};
+
+union mail_user_module_context {
+	struct mail_user_vfuncs super;
+	struct mail_user_module_register *reg;
+};
+extern struct mail_user_module_register mail_user_module_register;
+
+/* Called after user has been created */
+extern void (*hook_mail_user_created)(struct mail_user *user);
+
+struct mail_user *mail_user_init(const char *username, const char *home);
+void mail_user_deinit(struct mail_user **user);
+
+/* Add a new namespace to user's namespaces. */
+void mail_user_add_namespace(struct mail_user *user, struct mail_namespace *ns);
+
+/* Replace ~/ at the beginning of the path with the user's home directory. */
+const char *mail_user_home_expand(struct mail_user *user, const char *path);
+/* Returns 0 if ok, -1 if home directory isn't set. */
+int mail_user_try_home_expand(struct mail_user *user, const char **path);
+
+#endif
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mailbox-list-private.h dovecot-patch/src/lib-storage/mailbox-list-private.h
--- dovecot-1.1.7+patch9/src/lib-storage/mailbox-list-private.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/lib-storage/mailbox-list-private.h	2009-01-06 10:08:19.000000000 -0600
@@ -116,6 +116,7 @@
 
 int mailbox_list_settings_parse(const char *data,
 				struct mailbox_list_settings *set,
+				struct mail_namespace *ns,
 				const char **layout, const char **alt_dir_r,
 				const char **error_r);
 
diff -Nur dovecot-1.1.7+patch9/src/lib-storage/mailbox-list.c dovecot-patch/src/lib-storage/mailbox-list.c
--- dovecot-1.1.7+patch9/src/lib-storage/mailbox-list.c	2008-11-21 13:30:02.000000000 -0600
+++ dovecot-patch/src/lib-storage/mailbox-list.c	2009-01-06 10:08:19.000000000 -0600
@@ -100,17 +100,18 @@
 	return 0;
 }
 
-static const char *fix_path(const char *path)
+static const char *fix_path(struct mail_namespace *ns, const char *path)
 {
 	size_t len = strlen(path);
 
 	if (len > 1 && path[len-1] == '/')
 		path = t_strndup(path, len-1);
-	return home_expand(path);
+	return mail_user_home_expand(ns->user, path);
 }
 
 int mailbox_list_settings_parse(const char *data,
 				struct mailbox_list_settings *set,
+				struct mail_namespace *ns,
 				const char **layout, const char **alt_dir_r,
 				const char **error_r)
 {
@@ -124,7 +125,7 @@
 
 	/* <root dir> */
 	tmp = t_strsplit(data, ":");
-	set->root_dir = fix_path(*tmp);
+	set->root_dir = fix_path(ns, *tmp);
 	tmp++;
 
 	for (; *tmp != NULL; tmp++) {
@@ -138,17 +139,17 @@
 		}
 
 		if (strcmp(key, "INBOX") == 0)
-			set->inbox_path = fix_path(value);
+			set->inbox_path = fix_path(ns, value);
 		else if (strcmp(key, "INDEX") == 0)
-			set->index_dir = fix_path(value);
+			set->index_dir = fix_path(ns, value);
 		else if (strcmp(key, "CONTROL") == 0)
-			set->control_dir = fix_path(value);
+			set->control_dir = fix_path(ns, value);
 		else if (strcmp(key, "ALT") == 0 && alt_dir_r != NULL)
-			*alt_dir_r = fix_path(value);
+			*alt_dir_r = fix_path(ns, value);
 		else if (strcmp(key, "LAYOUT") == 0)
 			*layout = value;
 		else if (strcmp(key, "SUBSCRIPTIONS") == 0)
-			set->subscription_fname = fix_path(value);
+			set->subscription_fname = fix_path(ns, value);
 		else if (strcmp(key, "DIRNAME") == 0)
 			set->maildir_name = value;
 		else {
diff -Nur dovecot-1.1.7+patch9/src/mail-common/Makefile.am dovecot-patch/src/mail-common/Makefile.am
--- dovecot-1.1.7+patch9/src/mail-common/Makefile.am	1969-12-31 18:00:00.000000000 -0600
+++ dovecot-patch/src/mail-common/Makefile.am	2009-01-06 10:08:19.000000000 -0600
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2008 Apple Inc. All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without  
+# modification, are permitted provided that the following conditions  
+# are met:
+# 
+# 1.  Redistributions of source code must retain the above copyright  
+# notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above  
+# copyright notice, this list of conditions and the following  
+# disclaimer in the documentation and/or other materials provided  
+# with the distribution.
+# 3.  Neither the name of Apple Inc. ("Apple") nor the names of its  
+# contributors may be used to endorse or promote products derived  
+# from this software without specific prior written permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND  
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A  
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS  
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT  
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF  
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,  
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
+# SUCH DAMAGE.
+#
+
+noinst_LIBRARIES = libmail-common.a
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-DPKG_RUNDIR=\""$(rundir)"\" \
+	-DSBINDIR=\""$(sbindir)"\"
+
+libmail_common_a_SOURCES = \
+	master.c
+
+noinst_HEADERS = \
+	master.h \
+	persistent-mail-master-interface.h
diff -Nur dovecot-1.1.7+patch9/src/mail-common/Makefile.in dovecot-patch/src/mail-common/Makefile.in
--- dovecot-1.1.7+patch9/src/mail-common/Makefile.in	1969-12-31 18:00:00.000000000 -0600
+++ dovecot-patch/src/mail-common/Makefile.in	2009-01-06 10:08:19.000000000 -0600
@@ -0,0 +1,509 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+#
+# Copyright (c) 2008 Apple Inc. All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without  
+# modification, are permitted provided that the following conditions  
+# are met:
+# 
+# 1.  Redistributions of source code must retain the above copyright  
+# notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above  
+# copyright notice, this list of conditions and the following  
+# disclaimer in the documentation and/or other materials provided  
+# with the distribution.
+# 3.  Neither the name of Apple Inc. ("Apple") nor the names of its  
+# contributors may be used to endorse or promote products derived  
+# from this software without specific prior written permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND  
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A  
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS  
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT  
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF  
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,  
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
+# SUCH DAMAGE.
+#
+
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/mail-common
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+	$(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libmail_common_a_AR = $(AR) $(ARFLAGS)
+libmail_common_a_LIBADD =
+am_libmail_common_a_OBJECTS = master.$(OBJEXT)
+libmail_common_a_OBJECTS = $(am_libmail_common_a_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libmail_common_a_SOURCES)
+DIST_SOURCES = $(libmail_common_a_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTH_CFLAGS = @AUTH_CFLAGS@
+AUTH_LIBS = @AUTH_LIBS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DICT_LIBS = @DICT_LIBS@
+DSYMUTIL = @DSYMUTIL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KRB5CONFIG = @KRB5CONFIG@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_LIBS = @KRB5_LIBS@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBCAP = @LIBCAP@
+LIBICONV = @LIBICONV@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MODULE_LIBS = @MODULE_LIBS@
+MYSQL_CFLAGS = @MYSQL_CFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NMEDIT = @NMEDIT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PGSQL_CFLAGS = @PGSQL_CFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKG_CONFIG = @PKG_CONFIG@
+RAND_LIBS = @RAND_LIBS@
+RANLIB = @RANLIB@
+RPCGEN = @RPCGEN@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CFLAGS = @SQLITE_CFLAGS@
+SQLITE_LIBS = @SQLITE_LIBS@
+SQL_CFLAGS = @SQL_CFLAGS@
+SQL_LIBS = @SQL_LIBS@
+SSL_CFLAGS = @SSL_CFLAGS@
+SSL_LIBS = @SSL_LIBS@
+STORAGE_LIBS = @STORAGE_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dict_drivers = @dict_drivers@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mail_storages = @mail_storages@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+moduledir = @moduledir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rundir = @rundir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sql_drivers = @sql_drivers@
+srcdir = @srcdir@
+ssldir = @ssldir@
+statedir = @statedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LIBRARIES = libmail-common.a
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-DPKG_RUNDIR=\""$(rundir)"\" \
+	-DSBINDIR=\""$(sbindir)"\"
+
+libmail_common_a_SOURCES = \
+	master.c
+
+noinst_HEADERS = \
+	master.h \
+	persistent-mail-master-interface.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  src/mail-common/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu  src/mail-common/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+	-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libmail-common.a: $(libmail_common_a_OBJECTS) $(libmail_common_a_DEPENDENCIES) 
+	-rm -f libmail-common.a
+	$(libmail_common_a_AR) libmail-common.a $(libmail_common_a_OBJECTS) $(libmail_common_a_LIBADD)
+	$(RANLIB) libmail-common.a
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/master.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLIBRARIES ctags distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff -Nur dovecot-1.1.7+patch9/src/mail-common/master.c dovecot-patch/src/mail-common/master.c
--- dovecot-1.1.7+patch9/src/mail-common/master.c	1969-12-31 18:00:00.000000000 -0600
+++ dovecot-patch/src/mail-common/master.c	2009-01-06 10:08:19.000000000 -0600
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without  
+ * modification, are permitted provided that the following conditions  
+ * are met:
+ * 
+ * 1.  Redistributions of source code must retain the above copyright  
+ * notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above  
+ * copyright notice, this list of conditions and the following  
+ * disclaimer in the documentation and/or other materials provided  
+ * with the distribution.
+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its  
+ * contributors may be used to endorse or promote products derived  
+ * from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND  
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A  
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS  
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT  
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF  
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,  
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
+ * SUCH DAMAGE.
+ */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "ostream.h"
+#include "master.h"
+#include "network.h"
+#include "persistent-mail-master-interface.h"
+#include "str-sanitize.h"
+#include "fdpass.h"
+#include "env-util.h"
+#include "restrict-access.h"
+#include "str.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+static int master_fd = -1;
+static struct io *master_io;
+static struct ostream *master_out;
+static struct ioloop *mail_ioloop;
+static bool (*mail_client_attach)(int fd_in, int fd_out, bool is_standalone);
+static bool (*mail_clients_connected)(void);
+
+static struct {
+	uint32_t len;			/* length of body */
+	size_t len_off;			/* how much of len we've read */
+	char body[32];			/* body of message */
+	size_t body_off;		/* how much of body we've read */
+	unsigned int have_len:1;
+	unsigned int have_body:1;
+	unsigned int done:1;
+} master_handshake;
+
+static const char hotpotato[] = HOTPOTATO;
+static struct {
+	char spud[PERSISTENT_MAIL_MASTER_MESSAGE_SIZE];	/* message passed with client fd */
+	unsigned int connection_id;	/* receipt id */
+	int fd;				/* connected client */
+	uint32_t len;			/* length of environment */
+	size_t len_off;			/* how much of len we've read */
+	char env[4096];			/* environment */
+	size_t env_off;			/* how much of env we've read */
+	unsigned int have_fd:1;
+	unsigned int have_len:1;
+	unsigned int have_env:1;
+} master_request;
+
+static ssize_t master_read_resid(void *buf, size_t *off, size_t *resid)
+{
+	while (*resid > 0) {
+		ssize_t r = read(master_fd, (char *) buf + *off, *resid);
+		if (r > 0) {
+			*off += r;
+			*resid -= r;
+			if (*resid == 0)
+				break;
+		} else if (r == 0) {
+			if (getenv("DEBUG"))
+				i_info("Master disconnected (pid %s)",
+				       dec2str(getpid()));
+			master_deinit();
+			break;
+		} else {
+			if (r < 0 && errno != EAGAIN)
+				return -1;
+			break;
+		}
+	}
+	return 0;
+}
+
+static bool master_shake(void)
+{
+	size_t resid;
+	ssize_t r;
+
+	if (!master_handshake.have_len) {
+		resid = sizeof master_handshake.len - master_handshake.len_off;
+		r = master_read_resid(&master_handshake.len,
+				      &master_handshake.len_off,
+				      &resid);
+		if (r < 0) {
+			i_error("Error reading master handshake length: %m");
+			master_deinit();
+			return FALSE;
+		}
+		if (resid > 0)		/* need more */
+			return FALSE;
+
+		/* have the complete len */
+		master_handshake.len = ntohl(master_handshake.len);
+		if (master_handshake.len >= sizeof master_handshake.body) {
+			i_error("Bogus handshake length %u",
+				master_handshake.len);
+			master_deinit();
+			return FALSE;
+		}
+
+		master_handshake.have_len = TRUE;
+	}
+
+	if (!master_handshake.have_body) {
+		i_assert(master_handshake.len < sizeof master_handshake.body);
+		resid = master_handshake.len - master_handshake.body_off;
+		r = master_read_resid(master_handshake.body,
+				      &master_handshake.body_off,
+				      &resid);
+		if (r < 0) {
+			i_error("Error reading master handshake body: %m");
+			master_deinit();
+			return FALSE;
+		}
+		if (resid > 0)		/* need more */
+			return FALSE;
+
+		/* have the complete body */
+		master_handshake.body[master_handshake.len] = '\0';
+		if (master_handshake.len < 8 ||
+		    strncmp(master_handshake.body, "VERSION\t", 8) != 0 ||
+		    atoi(t_strcut(&master_handshake.body[8], '\t')) !=
+		    PERSISTENT_MAIL_MASTER_PROTOCOL_MAJOR_VERSION) {
+			i_error("Bad handshake from master: \"%s\"; "
+				"expected \"VERSION\t%u\t...\"",
+				str_sanitize(master_handshake.body,
+					     master_handshake.len),
+				PERSISTENT_MAIL_MASTER_PROTOCOL_MAJOR_VERSION);
+			master_deinit();
+			return FALSE;
+		}
+
+		master_handshake.have_body = TRUE;
+	}
+
+	return TRUE;
+}
+
+static void master_request_reset(void)
+{
+	memset(master_request.spud, 0, sizeof master_request.spud);
+	master_request.connection_id = 0;
+	master_request.fd = -1;
+	master_request.len = 0;
+	master_request.len_off = 0;
+	master_request.env[0] = '\0';
+	master_request.env_off = 0;
+	master_request.have_fd = FALSE;
+	master_request.have_len = FALSE;
+	master_request.have_env = FALSE;
+}
+
+static bool master_receive_fd(void)
+{
+	ssize_t r;
+
+	r = fd_read(master_fd, master_request.spud,
+	    sizeof master_request.spud, &master_request.fd);
+	if (r < 0 && errno == EAGAIN)
+		return FALSE;
+
+	if (r != sizeof master_request.spud ||
+	    strncmp(master_request.spud, hotpotato, sizeof hotpotato - 1) != 0 ||
+	    master_request.fd < 0) {
+		if (r < 0)
+			i_error("Error receiving client request fd: %m");
+		else if (r == 0) {
+			if (getenv("DEBUG"))
+				i_info("Master disconnected (pid %s)",
+				       dec2str(getpid()));
+		} else
+			i_error("Invalid client request fd received");
+		master_deinit();
+		return FALSE;
+	}
+
+	memcpy(&master_request.connection_id,
+	       &master_request.spud[sizeof hotpotato - 1],
+	       sizeof master_request.connection_id);
+	master_request.connection_id = ntohl(master_request.connection_id);
+
+	return TRUE;
+}
+
+static bool master_receive_len(void)
+{
+	size_t resid;
+	ssize_t r;
+
+	resid = sizeof master_request.len - master_request.len_off;
+	r = master_read_resid(&master_request.len, &master_request.len_off,
+			      &resid);
+	if (r < 0) {
+		i_error("Error reading client env length: %m");
+		master_deinit();
+		return FALSE;
+	}
+	if (resid > 0)		/* need more */
+		return FALSE;
+
+	/* have the complete len */
+	master_request.len = ntohl(master_request.len);
+	if (master_request.len >= sizeof master_request.env) {
+		i_error("Client env too large (%u >= %u)",
+			(unsigned int) master_request.len,
+			(unsigned int) sizeof master_request.env);
+		master_deinit();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static bool master_receive_env(void)
+{
+	size_t resid;
+	ssize_t r;
+
+	i_assert(master_request.len < sizeof master_request.env);
+	resid = master_request.len - master_request.env_off;
+	r = master_read_resid(master_request.env, &master_request.env_off,
+			      &resid);
+	if (r < 0) {
+		i_error("Error reading client env: %m");
+		master_deinit();
+		return FALSE;
+	}
+	if (resid > 0)		/* need more */
+		return FALSE;
+
+	/* have the complete env */
+	master_request.env[master_request.len] = '\0';
+
+	return TRUE;
+}
+
+static bool master_send_ack(unsigned int id)
+{
+	const char *message;
+
+	if (master_fd < 0)
+		return FALSE;
+
+	message = t_strdup_printf("ACK\t%u\n", id);
+	o_stream_send(master_out, message, strlen(message));
+	if (o_stream_flush(master_out) < 0) {
+		master_deinit();
+		return FALSE;
+	}
+	return TRUE;
+}
+
+bool master_send_disconnect(unsigned int connection_id)
+{
+	const char *message;
+
+	if (master_fd < 0)
+		return FALSE;
+
+	message = t_strdup_printf("DISCONNECTED\t%u\n", connection_id);
+	o_stream_send(master_out, message, strlen(message));
+	if (o_stream_flush(master_out) < 0) {
+		master_deinit();
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void master_input(void *context ATTR_UNUSED)
+{
+	const char **env;
+
+	if (!master_handshake.done) {
+		if (master_shake())
+			master_handshake.done = TRUE;
+		else
+			return;
+	}
+
+	if (!master_request.have_fd) {
+		if (master_receive_fd())
+			master_request.have_fd = TRUE;
+		else
+			return;
+
+		if (!master_send_ack(master_request.connection_id))
+			return;
+	}
+
+	if (!master_request.have_len) {
+		if (master_receive_len())
+			master_request.have_len = TRUE;
+		else
+			return;
+	}
+
+	if (!master_request.have_env) {
+		if (master_receive_env())
+			master_request.have_env = TRUE;
+		else
+			return;
+	}
+
+	/* have complete request.  temporarily install the new environ,
+	   but detach it before returning */
+	env = t_strsplit(master_request.env, "\n");
+	env_switch(unsafe_data_stack_pool, (char **) env);
+	restrict_access_by_env(TRUE);
+	if (!mail_client_attach(master_request.fd, master_request.fd, FALSE))
+		close(master_request.fd);
+	io_env_clean();
+
+	master_request_reset();
+}
+
+void master_init(int fd, struct ioloop *ioloop,
+    bool (*client_attach)(int fd_in, int fd_out, bool is_standalone),
+    bool (*clients_connected)(void))
+{
+	const char *handshake;
+
+	master_fd = fd;
+	if (master_fd < 0)
+		return;
+
+	net_set_nonblock(master_fd, TRUE);
+	master_io = io_add(master_fd, IO_READ, master_input, NULL);
+	master_out = o_stream_create_fd(master_fd, 8192, FALSE);
+	mail_ioloop = ioloop;
+	mail_client_attach = client_attach;
+	mail_clients_connected = clients_connected;
+
+	memset(&master_handshake, 0, sizeof master_handshake);
+
+	master_request_reset();
+
+	handshake = t_strdup_printf("VERSION\t%u\t%u\n",
+				    PERSISTENT_MAIL_MASTER_PROTOCOL_MAJOR_VERSION,
+				    PERSISTENT_MAIL_MASTER_PROTOCOL_MINOR_VERSION);
+	(void) o_stream_send_str(master_out, handshake);
+	o_stream_flush(master_out);
+}
+
+void master_deinit(void)
+{
+	if (master_fd < 0)
+		return;
+
+	o_stream_destroy(&master_out);
+	io_remove(&master_io);
+	if (close(master_fd) < 0)
+		i_error("close(master) failed: %m");
+	master_fd = -1;
+
+	if (!mail_clients_connected())
+		io_loop_stop(mail_ioloop);
+	/* else run until they disconnect */
+}
+
+void master_info(string_t *str)
+{
+	if (master_fd < 0)
+		str_append(str, "disconnected from master");
+	else
+		str_append(str, "connected to master");
+}
diff -Nur dovecot-1.1.7+patch9/src/mail-common/master.h dovecot-patch/src/mail-common/master.h
--- dovecot-1.1.7+patch9/src/mail-common/master.h	1969-12-31 18:00:00.000000000 -0600
+++ dovecot-patch/src/mail-common/master.h	2009-01-06 10:08:19.000000000 -0600
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without  
+ * modification, are permitted provided that the following conditions  
+ * are met:
+ * 
+ * 1.  Redistributions of source code must retain the above copyright  
+ * notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above  
+ * copyright notice, this list of conditions and the following  
+ * disclaimer in the documentation and/or other materials provided  
+ * with the distribution.
+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its  
+ * contributors may be used to endorse or promote products derived  
+ * from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND  
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A  
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS  
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT  
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF  
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,  
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
+ * SUCH DAMAGE.
+ */
+
+#ifndef MASTER_H
+#define MASTER_H
+
+void master_init(int fd, struct ioloop *ioloop,
+    bool (*client_attach)(int fd_in, int fd_out, bool is_standalone),
+    bool (*clients_connected)(void));
+void master_deinit(void);
+bool master_send_disconnect(unsigned int connection_id);
+void master_info(string_t *str);
+
+#endif
diff -Nur dovecot-1.1.7+patch9/src/mail-common/persistent-mail-master-interface.h dovecot-patch/src/mail-common/persistent-mail-master-interface.h
--- dovecot-1.1.7+patch9/src/mail-common/persistent-mail-master-interface.h	1969-12-31 18:00:00.000000000 -0600
+++ dovecot-patch/src/mail-common/persistent-mail-master-interface.h	2009-01-06 10:08:19.000000000 -0600
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without  
+ * modification, are permitted provided that the following conditions  
+ * are met:
+ * 
+ * 1.  Redistributions of source code must retain the above copyright  
+ * notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above  
+ * copyright notice, this list of conditions and the following  
+ * disclaimer in the documentation and/or other materials provided  
+ * with the distribution.
+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its  
+ * contributors may be used to endorse or promote products derived  
+ * from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND  
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A  
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS  
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT  
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF  
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,  
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
+ * SUCH DAMAGE.
+ */
+
+#ifndef PERSISTENT_MAIL_MASTER_INTERFACE_H
+#define PERSISTENT_MAIL_MASTER_INTERFACE_H
+
+/* Major version changes are not backwards compatible,
+   minor version numbers can be ignored. */
+#define PERSISTENT_MAIL_MASTER_PROTOCOL_MAJOR_VERSION	1
+#define PERSISTENT_MAIL_MASTER_PROTOCOL_MINOR_VERSION	0
+
+#define HOTPOTATO	"HOTPOTATO\n"
+
+#define	PERSISTENT_MAIL_MASTER_MESSAGE_SIZE	\
+		(sizeof (HOTPOTATO) - 1 + sizeof (uint32_t))
+
+#endif
diff -Nur dovecot-1.1.7+patch9/src/master/Makefile.am dovecot-patch/src/master/Makefile.am
--- dovecot-1.1.7+patch9/src/master/Makefile.am	2008-10-29 11:51:28.000000000 -0500
+++ dovecot-patch/src/master/Makefile.am	2009-01-06 10:08:19.000000000 -0600
@@ -6,6 +6,7 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-settings \
+	-I$(top_srcdir)/src/mail-common \
 	-DSYSCONFDIR=\""$(sysconfdir)"\" \
 	-DPKG_RUNDIR=\""$(rundir)"\" \
 	-DPKG_STATEDIR=\""$(statedir)"\" \
diff -Nur dovecot-1.1.7+patch9/src/master/auth-process.c dovecot-patch/src/master/auth-process.c
--- dovecot-1.1.7+patch9/src/master/auth-process.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/master/auth-process.c	2009-01-06 10:08:19.000000000 -0600
@@ -435,7 +435,8 @@
 
 	/* setup access environment */
 	restrict_access_set_env(set->user, set->uid, set->gid,
-				(gid_t)-1, set->chroot, 0, 0, NULL);
+				(gid_t)-1, set->chroot, 0, 0, NULL,
+				FALSE);			/* APPLE */
 
 	/* set other environment */
 	env_put("DOVECOT_MASTER=1");
diff -Nur dovecot-1.1.7+patch9/src/master/child-process.c dovecot-patch/src/master/child-process.c
--- dovecot-1.1.7+patch9/src/master/child-process.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/master/child-process.c	2009-01-06 10:08:19.000000000 -0600
@@ -42,10 +42,15 @@
 
 void child_process_init_env(void)
 {
+	child_process_init_env_private(FALSE);		/* APPLE */
+}
+
+void child_process_init_env_private(bool private_env)	/* APPLE */
+{
 	int facility;
 
 	/* remove all environment, we don't need them */
-	env_clean();
+	env_clean_private(private_env);			/* APPLE */
 
 	/* we'll log through master process */
 	env_put("LOG_TO_MASTER=1");
diff -Nur dovecot-1.1.7+patch9/src/master/child-process.h dovecot-patch/src/master/child-process.h
--- dovecot-1.1.7+patch9/src/master/child-process.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/master/child-process.h	2009-01-06 10:08:19.000000000 -0600
@@ -31,6 +31,7 @@
 void child_process_remove(pid_t pid);
 
 void child_process_init_env(void);
+void child_process_init_env_private(bool private_env);	/* APPLE */
 void client_process_exec(const char *cmd, const char *title);
 void client_process_exec_argv(const char *executable, const char **argv);
 
diff -Nur dovecot-1.1.7+patch9/src/master/login-process.c dovecot-patch/src/master/login-process.c
--- dovecot-1.1.7+patch9/src/master/login-process.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/master/login-process.c	2009-01-06 10:08:19.000000000 -0600
@@ -520,7 +520,8 @@
 	restrict_access_set_env(NULL, set->login_uid,
 				set->server->login_gid, (gid_t)-1,
 				set->login_chroot ? set->login_dir : NULL,
-				0, 0, NULL);
+				0, 0, NULL,
+				FALSE);			/* APPLE */
 
 	env_put("DOVECOT_MASTER=1");
 
diff -Nur dovecot-1.1.7+patch9/src/master/mail-process.c dovecot-patch/src/master/mail-process.c
--- dovecot-1.1.7+patch9/src/master/mail-process.c	2008-11-15 11:36:54.000000000 -0600
+++ dovecot-patch/src/master/mail-process.c	2009-01-06 11:18:03.000000000 -0600
@@ -33,6 +33,18 @@
    many seconds to finish. */
 #define CHDIR_WARN_SECS 10
 
+/* APPLE */
+struct mail_process {
+	pid_t pid;
+	time_t connected_at;
+};
+
+/* APPLE */
+struct mail_connection {
+	unsigned int connection_id;
+	time_t connected_at;
+};
+
 struct mail_process_group {
 	/* process.type + user + remote_ip identifies this process group */
 	struct child_process process;
@@ -40,13 +52,281 @@
 	struct ip_addr remote_ip;
 
 	/* processes array acts also as refcount */
-	ARRAY_DEFINE(processes, pid_t);
+	ARRAY_DEFINE(processes, struct mail_process);	/* APPLE */
+
+	/* APPLE */
+	ARRAY_DEFINE(connections, struct mail_connection);
+	unsigned int children;
 };
 
+/* APPLE begin */
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "fdpass.h"
+#include "persistent-mail-master-interface.h"
+
+#define MAX_INBUF_SIZE	1024
+#define MAX_OUTBUF_SIZE	(512*1024)
+
+struct persistent_mail_process_group {
+	struct persistent_mail_process_group *next;
+
+	struct settings *set;
+
+	struct persistent_mail_process *processes;
+};
+static struct persistent_mail_process_group *persistent_mail_process_groups;
+
+struct persistent_mail_process {
+	struct persistent_mail_process *next;
+
+	struct persistent_mail_process_group *group;
+
+	pid_t pid;
+	int fd;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	ARRAY_DEFINE(connections, unsigned int);   /* includes pending ones */
+	struct hash_table *pending;
+	unsigned int version_received:1;
+};
+
+struct persistent_mail_pender {
+	int socket_fd;
+};
+
+static unsigned int next_connection_id;
+
+struct hash_table *mail_connections;
+/* APPLE end */
+
 /* type+user -> struct mail_process_group */
 static struct hash_table *mail_process_groups;
 static unsigned int mail_process_count = 0;
 
+/* APPLE */
+static void mail_process_group_dump(const char *pfx, const struct mail_process_group *group)
+{
+	string_t *msg;
+	unsigned int i, count;
+	const struct mail_process *processes;
+	const struct mail_connection *connections;
+
+	msg = t_str_new(512);
+
+	processes = array_get(&group->processes, &count);
+	str_printfa(msg, "%d pid(s)", count);
+	if (count > 0) {
+		str_append(msg, " (");
+		for (i = 0; i < count; i++) {
+			if (i > 0)
+				str_append(msg, ", ");
+			str_printfa(msg, "%d since %.15s",
+				    processes[i].pid,
+				    ctime(&processes[i].connected_at) + 4);
+		}
+		str_append_c(msg, ')');
+	}
+
+	connections = array_get(&group->connections, &count);
+	str_printfa(msg, " and %d connection(s)", count);
+	if (count > 0) {
+		str_append(msg, " (");
+		for (i = 0; i < count; i++) {
+			if (i > 0)
+				str_append(msg, ", ");
+			str_printfa(msg, "%u since %.15s",
+				    connections[i].connection_id,
+				    ctime(&connections[i].connected_at) + 4);
+		}
+		str_append_c(msg, ')');
+	}
+
+	i_info("master: %s: mail_process_group %p user=%s/addr=%s/type=%d"
+	       " children=%u has %s", pfx, group, group->user,
+	       inet_ntoa(group->remote_ip.u.ip4), group->process.type,
+	       group->children, str_c(msg));
+}
+
+/* APPLE */
+static void persistent_mail_process_dump(const char *pfx, const struct persistent_mail_process *process)
+{
+	string_t *msg;
+	unsigned int i, count;
+	const unsigned int *connections;
+
+	msg = t_str_new(512);
+
+	connections = array_get(&process->connections, &count);
+	str_printfa(msg, "%d connection(s)", count);
+	if (count > 0) {
+		str_append(msg, " (");
+		for (i = 0; i < count; i++) {
+			if (i > 0)
+				str_append(msg, ", ");
+			str_printfa(msg, "%u", connections[i]);
+		}
+		str_append_c(msg, ')');
+	}
+
+	i_info("master: %s: persistent_mail_process pid=%d proto=%d has %s",
+	       pfx, process->pid, process->group->set->protocol, str_c(msg));
+}
+
+/* APPLE */
+static void mail_connections_dump(const char *pfx)
+{
+	string_t *msg;
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	msg = t_str_new(512);
+
+	str_printfa(msg, "%d entries", hash_count(mail_connections));
+	if (hash_count(mail_connections) > 0) {
+		int i = 0;
+
+		str_append(msg, " (");
+		iter = hash_iterate_init(mail_connections);
+		while (hash_iterate(iter, &key, &value)) {
+			unsigned int connection_id = POINTER_CAST_TO(key, unsigned int);
+			struct mail_process_group *group = (struct mail_process_group *) value;
+			if (i++ > 0)
+				str_append(msg, ", ");
+			str_printfa(msg, "connID=%u -> group %p user=%s/addr=%s/type=%d", connection_id, group, group->user, inet_ntoa(group->remote_ip.u.ip4), group->process.type);
+		}
+		hash_iterate_deinit(&iter);
+		str_append_c(msg, ')');
+	}
+
+	i_info("master: %s: mail_connections has %s", pfx, str_c(msg));
+}
+
+#ifdef APPLE_OS_X_SERVER
+#include "fcntl.h"
+static void dump_connected_users ( void )
+{
+	int			fd;
+	int			pop3_cnt	= 0;
+	int			imap_cnt	= 0;
+	string_t   *msg_str		= NULL;
+	void	   *key;
+	void	   *value;
+	const char *file_path	= "/var/db/.mailusers.plist";
+	struct hash_iterate_context *iter;
+
+	fd = open( file_path, O_RDWR );
+	if ( fd == -1 )
+	{
+		/* we don't care if it doesn't open, it is created by Server Admin */
+		return;
+	}
+
+	msg_str = t_str_new( 512 );
+	str_append( msg_str, "<dict>\n\t<key>usersArray</key>\n\t<array>\n" );
+
+	iter = hash_iterate_init( mail_process_groups );
+	while ( hash_iterate( iter, &key, &value ) )
+	{
+		struct mail_process_group *group = (struct mail_process_group *)value;
+		int *cnt;
+		time_t earliest_connected_at;
+		unsigned int process_count, connection_count, i;
+		const struct mail_process *processes;
+		const struct mail_connection *connections;
+
+		if ( group->process.type == PROCESS_TYPE_IMAP )
+		{
+			cnt = &imap_cnt;
+			str_append( msg_str, "\t\t<dict>\n\t\t\t<key>type</key>\n\t\t\t<string>imap</string>\n" );
+		}
+		else if ( group->process.type == PROCESS_TYPE_POP3 )
+		{
+			cnt = &pop3_cnt;
+			str_append( msg_str, "\t\t<dict>\n\t\t\t<key>type</key>\n\t\t\t<string>pop3</string>\n" );
+		}
+		else
+		{
+			continue;
+		}
+
+		str_printfa( msg_str, "\t\t\t<key>name</key>\n\t\t\t<string>%s</string>\n", group->user );
+		str_printfa( msg_str, "\t\t\t<key>ipAddress</key>\n\t\t\t<string>%s</string>\n", inet_ntoa(group->remote_ip.u.ip4) );
+
+		earliest_connected_at = ioloop_time;
+		processes = array_get(&group->processes, &process_count);
+		for (i = 0; i < process_count; ++i)
+			if (earliest_connected_at > processes[i].connected_at)
+				earliest_connected_at = processes[i].connected_at;
+		connections = array_get(&group->connections, &connection_count);
+		for (i = 0; i < connection_count; ++i)
+			if (earliest_connected_at > connections[i].connected_at)
+				earliest_connected_at = connections[i].connected_at;
+
+		*cnt += process_count + connection_count;
+		str_printfa(msg_str, "\t\t\t<key>connections</key>\n\t\t\t<integer>%d</integer>\n",
+			    process_count + connection_count);
+		str_printfa(msg_str, "\t\t\t<key>connectionElapsedTime</key>\n\t\t\t<integer>%d</integer>\n",
+			    (int) (ioloop_time - earliest_connected_at));
+
+		str_append( msg_str, "\t\t</dict>\n" );
+	}
+	hash_iterate_deinit( &iter );
+
+	str_printfa( msg_str, "\t</array>\n\t<key>imapRequests</key>\n\t<integer>%d</integer>\n", imap_cnt );
+	str_printfa( msg_str, "\t<key>popRequests</key>\n\t<integer>%d</integer>\n", pop3_cnt );
+	str_printfa( msg_str, "\t<key>totalRequests</key>\n\t<integer>%d</integer>\n", pop3_cnt + imap_cnt );
+	str_append( msg_str, "</dict>\n" );
+
+	if ( lseek(fd, 0, SEEK_SET) == 0 && ftruncate(fd, 0) == 0 )
+	{
+		write( fd, str_c( msg_str ), str_len( msg_str ) );
+	}
+	close( fd );
+}
+#endif
+
+/* APPLE */
+#ifdef SIGINFO
+#include "lib-signals.h"
+static void sig_info(int signo ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+	struct persistent_mail_process_group *group;
+
+	i_info("Mail process status:");
+	mail_connections_dump("status");
+#ifdef APPLE_OS_X_SERVER
+	dump_connected_users();
+#endif
+
+	i_info("Persistent mail process groups:");
+	for (group = persistent_mail_process_groups; group != NULL;
+	     group = group->next) {
+		struct persistent_mail_process *process;
+
+		i_info("pmp_group \"%s\":", group->set->server->name);
+		for (process = group->processes; process != NULL;
+		     process = process->next)
+			persistent_mail_process_dump("status", process);
+	}
+
+	i_info("User+IP mail process groups:");
+	iter = hash_iterate_init(mail_process_groups);
+	while (hash_iterate(iter, &key, &value)) {
+		struct mail_process_group *group = value;
+		mail_process_group_dump("status", group);
+	}
+	hash_iterate_deinit(&iter);
+
+	i_info("End");
+}
+#endif
+
 static unsigned int mail_process_group_hash(const void *p)
 {
 	const struct mail_process_group *group = p;
@@ -93,25 +373,599 @@
 	group->remote_ip = *ip;
 
 	i_array_init(&group->processes, 10);
+	i_array_init(&group->connections, 10);		/* APPLE */
 	hash_insert(mail_process_groups, group, group);
 	return group;
 }
 
 static void
-mail_process_group_add(struct mail_process_group *group, pid_t pid)
+mail_process_group_add(struct mail_process_group *group, pid_t pid,
+		       /* APPLE */
+		       unsigned int connection_id, bool is_persistent)
 {
 	mail_process_count++;
-	array_append(&group->processes, &pid, 1);
+
+	/* APPLE */
+	group->children += 1;
+	if (is_persistent) {
+		struct mail_connection *conn =
+			array_append_space(&group->connections);
+		conn->connection_id = connection_id;
+		conn->connected_at = ioloop_time;
+	} else {
+		struct mail_process *proc =
+			array_append_space(&group->processes);
+		proc->pid = pid;
+		proc->connected_at = ioloop_time;
+	}
+
 	child_process_add(pid, &group->process);
 }
 
 static void mail_process_group_free(struct mail_process_group *group)
 {
+	array_free(&group->connections);		/* APPLE */
 	array_free(&group->processes);
 	i_free(group->user);
 	i_free(group);
 }
 
+/* APPLE begin */
+static struct persistent_mail_pender *
+persistent_mail_pender_new(int socket_fd)
+{
+	struct persistent_mail_pender *pender;
+
+	pender = i_new(struct persistent_mail_pender, 1);
+	pender->socket_fd = socket_fd;
+
+	return pender;
+}
+
+static void
+persistent_mail_pender_destroy(struct persistent_mail_pender *pender)
+{
+	close(pender->socket_fd);
+	i_free(pender);
+}
+
+static void persistent_mail_process_group_create(struct settings *set)
+{
+	struct persistent_mail_process_group *group;
+
+	group = i_new(struct persistent_mail_process_group, 1);
+	group->set = set;
+
+	group->next = persistent_mail_process_groups;
+	persistent_mail_process_groups = group;
+}
+
+static bool
+persistent_mail_process_disconnect(struct persistent_mail_process *process,
+				   unsigned int connection_id)
+{
+	struct mail_process_group *group;
+	const struct mail_connection *conns;
+	const unsigned int *ids;
+	unsigned int i, count;
+
+	group = hash_lookup(mail_connections, POINTER_CAST(connection_id));
+	i_assert(group != NULL);
+	hash_remove(mail_connections, POINTER_CAST(connection_id));
+
+	conns = array_get(&group->connections, &count);
+	for (i = 0; i < count; i++)
+		if (conns[i].connection_id == connection_id)
+			break;
+	i_assert(i != count);
+	array_delete(&group->connections, i, 1);
+	if (--count == 0 && group->children == 0 &&
+	    array_count(&group->processes) == 0) {
+		hash_remove(mail_process_groups, group);
+		mail_process_group_free(group);
+	}
+
+	ids = array_get(&process->connections, &count);
+	for (i = 0; i < count; i++)
+		if (ids[i] == connection_id)
+			break;
+	i_assert(i != count);
+	array_delete(&process->connections, i, 1);
+	return --count > 0;
+}
+
+static void
+persistent_mail_process_destroy(struct persistent_mail_process *process)
+{
+	struct persistent_mail_process **pos;
+	struct hash_iterate_context *iter;
+	void *key, *value;
+	const unsigned int *ids;
+	unsigned int count;
+
+	for (pos = &process->group->processes; *pos != NULL;
+	     pos = &(*pos)->next) {
+		if (*pos == process) {
+			*pos = process->next;
+			break;
+		}
+	}
+
+	iter = hash_iterate_init(process->pending);
+	while (hash_iterate(iter, &key, &value))
+		persistent_mail_pender_destroy(value);
+	hash_iterate_deinit(&iter);
+	hash_table_destroy(&process->pending);
+
+	for (ids = array_get(&process->connections, &count); count > 0;
+	     ids = array_get(&process->connections, &count))
+		persistent_mail_process_disconnect(process, ids[0]);
+	array_free(&process->connections);
+
+	o_stream_destroy(&process->output);
+	i_stream_destroy(&process->input);
+	io_remove(&process->io);
+	if (close(process->fd) < 0)
+		i_error("close(persistent_mail_process) failed: %m");
+	i_free(process);
+}
+
+static void
+persistent_mail_process_group_destroy(struct persistent_mail_process_group *group)
+{
+	struct persistent_mail_process *next;
+
+	while (group->processes != NULL) {
+		next = group->processes->next;
+		persistent_mail_process_destroy(group->processes);
+		group->processes = next;
+	}
+
+	i_free(group);
+}
+
+void persistent_mail_processes_destroy_all(void)
+{
+	struct persistent_mail_process_group *next;
+
+	while (persistent_mail_process_groups != NULL) {
+		next = persistent_mail_process_groups->next;
+		persistent_mail_process_group_destroy(persistent_mail_process_groups);
+		persistent_mail_process_groups = next;
+	}
+}
+
+static void persistent_mail_process_groups_create(void)
+{
+	struct server_settings *server;
+
+	for (server = settings_root; server != NULL; server = server->next) {
+		if (server->imap != NULL)
+			persistent_mail_process_group_create(server->imap);
+		if (server->pop3 != NULL)
+			persistent_mail_process_group_create(server->pop3);
+	}
+}
+
+static struct persistent_mail_process_group *
+persistent_mail_process_group_find(struct settings *set)
+{
+	struct persistent_mail_process_group *group;
+
+	if (persistent_mail_process_groups == NULL)
+		persistent_mail_process_groups_create();
+
+	for (group = persistent_mail_process_groups; group != NULL;
+	     group = group->next)
+		if (group->set->protocol == set->protocol &&
+		    strcmp(group->set->server->name, set->server->name) == 0)
+			break;
+
+	return group;
+}
+
+static bool
+persistent_mail_process_input_ack(struct persistent_mail_process *process,
+				  const char *args)
+{
+	unsigned int connection_id;
+	struct persistent_mail_pender *pender;
+
+	connection_id = strtoul(args, NULL, 10);
+	if (connection_id == 0) {
+		i_error("BUG: Persistent mail process %s sent ack for "
+			"illegal connection ID 0",
+			dec2str(process->pid));
+		return FALSE;
+	}
+
+	pender = hash_lookup(process->pending, POINTER_CAST(connection_id));
+	if (pender == NULL) {
+		i_error("BUG: Persistent mail process %s sent ack for "
+			"nonexistent connection ID %u",
+			dec2str(process->pid), connection_id);
+		return FALSE;
+	}
+	persistent_mail_pender_destroy(pender);
+	hash_remove(process->pending, POINTER_CAST(connection_id));
+	return TRUE;
+}
+
+static bool
+persistent_mail_process_input_disconnected(struct persistent_mail_process *process,
+					  const char *args)
+{
+	unsigned int connection_id;
+
+	connection_id = strtoul(args, NULL, 10);
+	if (connection_id == 0) {
+		i_error("BUG: Persistent mail process %s sent disconnect for "
+			"illegal connection ID 0",
+			dec2str(process->pid));
+		return FALSE;
+	}
+
+	return persistent_mail_process_disconnect(process, connection_id);
+}
+
+static bool
+persistent_mail_process_input_line(struct persistent_mail_process *process,
+				   const char *line)
+{
+	if (strncmp(line, "ACK\t", 4) == 0)
+		return persistent_mail_process_input_ack(process,
+							 line + 4);
+	else if (strncmp(line, "DISCONNECTED\t", 13) == 0)
+		return persistent_mail_process_input_disconnected(process,
+								  line + 13);
+	else
+		return TRUE;
+}
+
+/* simply copied from auth_process_input() */
+static void
+persistent_mail_process_input(struct persistent_mail_process *process)
+{
+	const char *line;
+	bool ret;
+
+	switch (i_stream_read(process->input)) {
+	case 0:
+		return;
+	case -1:
+		/* disconnected */
+		persistent_mail_process_destroy(process);
+		return;
+	case -2:
+		/* buffer full */
+		i_error("BUG: Persistent mail process %s sent us more than %d "
+			"bytes of data", dec2str(process->pid),
+			(int)MAX_INBUF_SIZE);
+		persistent_mail_process_destroy(process);
+		return;
+	}
+
+	if (!process->version_received) {
+		line = i_stream_next_line(process->input);
+		if (line == NULL)
+			return;
+
+		/* make sure the major version matches */
+		if (strncmp(line, "VERSION\t", 8) != 0 ||
+		    atoi(t_strcut(line + 8, '\t')) !=
+		    PERSISTENT_MAIL_MASTER_PROTOCOL_MAJOR_VERSION) {
+			i_error("Persistent mail process %s not compatible with master "
+				"process (mixed old and new binaries?)",
+				dec2str(process->pid));
+			persistent_mail_process_destroy(process);
+			return;
+		}
+		process->version_received = TRUE;
+	}
+
+	while ((line = i_stream_next_line(process->input)) != NULL) {
+		T_BEGIN {
+			ret = persistent_mail_process_input_line(process, line);
+		} T_END;
+		if (!ret) {
+			persistent_mail_process_destroy(process);
+			break;
+		}
+	}
+}
+
+static struct persistent_mail_process *
+persistent_mail_process_new(pid_t pid, int fd,
+				  struct persistent_mail_process_group *group)
+{
+	struct persistent_mail_process *process;
+	uint32_t len;
+	struct const_iovec iov[2];
+	ssize_t w;
+
+	process = i_new(struct persistent_mail_process, 1);
+	process->group = group;
+	process->pid = pid;
+	process->fd = fd;
+	process->io = io_add(fd, IO_READ, persistent_mail_process_input,
+			     process);
+	process->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
+	/* unlimited buffering to preserve stream integrity, but
+	   persistent_mail_process_request() enforces a limit */
+	process->output = o_stream_create_fd(fd, (size_t) -1, FALSE);
+	i_array_init(&process->connections, 10);
+	process->pending = hash_table_create(default_pool, default_pool, 0, NULL,
+					     NULL);
+
+	process->next = group->processes;
+	group->processes = process;
+
+	/* send handshake.  version string comes second */
+	iov[1].iov_base = t_strdup_printf("VERSION\t%u\t%u\n",
+					  PERSISTENT_MAIL_MASTER_PROTOCOL_MAJOR_VERSION,
+					  PERSISTENT_MAIL_MASTER_PROTOCOL_MINOR_VERSION);
+	iov[1].iov_len = strlen(iov[1].iov_base);
+
+	/* length of version string comes first */
+	len = iov[1].iov_len;
+	len = htonl(len);
+	iov[0].iov_base = &len;
+	iov[0].iov_len = sizeof len;
+	
+	w = o_stream_sendv(process->output, iov, 2);
+	if (w != (ssize_t) (iov[0].iov_len + iov[1].iov_len)) {
+		if (w < 0)
+			i_error("Can't write handshake to "
+				"persistent mail process %s: %m",
+				dec2str(pid));
+		else
+			i_error("New persistent mail process %s transmit "
+				"buffer full",
+				dec2str(pid));
+		persistent_mail_process_destroy(process);
+		process = NULL;
+	}
+
+	return process;
+}
+
+static void persistent_mail_process_destroyed(pid_t pid)
+{
+	struct persistent_mail_process_group *group;
+
+	for (group = persistent_mail_process_groups; group != NULL;
+	     group = group->next) {
+		struct persistent_mail_process *process;
+
+		for (process = group->processes; process != NULL;
+		     process = process->next) {
+			if (process->pid == pid) {
+				persistent_mail_process_destroy(process);
+				return;
+			}
+		}
+	}
+}
+
+/* reduce arg count to mail_process_init_env() and
+   persistent_mail_process_request() */
+struct mail_env_bits {
+	unsigned int connection_id;
+	struct settings *set;
+	const char *system_user;
+	uid_t uid;
+	gid_t gid;
+	const char *chroot_dir;
+	const char *mail;
+	const char *home_dir;
+	const char *user;
+	const char *addr;
+	const char *local_ip;
+	const struct var_expand_table *var_expand_table;
+	const ARRAY_TYPE(const_string) *extra_args;
+	unsigned int dump_capability:1;
+	unsigned int persistent_mail_process:1;
+};
+
+/* "constructor" helps find missing/forgotten initializers */
+static void mail_env_bits_init(struct mail_env_bits *bits,
+			       unsigned int connection_id,
+			       struct settings *set, const char *system_user,
+			       uid_t uid, gid_t gid, const char *chroot_dir,
+			       const char *mail, const char *home_dir,
+			       const char *user, const char *addr, const char *local_ip,
+			       const struct var_expand_table *var_expand_table,
+			       const ARRAY_TYPE(const_string) *extra_args,
+			       bool dump_capability,
+			       bool persistent_mail_process)
+{
+	memset(bits, 0, sizeof *bits);
+	bits->connection_id = connection_id;
+	bits->set = set;
+	bits->system_user = system_user;
+	bits->uid = uid;
+	bits->gid = gid;
+	bits->chroot_dir = chroot_dir;
+	bits->mail = mail;
+	bits->home_dir = home_dir;
+	bits->user = user;
+	bits->addr = addr;
+	bits->local_ip = local_ip;
+	bits->var_expand_table = var_expand_table;
+	bits->extra_args = extra_args;
+	bits->dump_capability = dump_capability;
+	bits->persistent_mail_process = persistent_mail_process;
+}
+
+static void mail_process_set_environment(struct settings *, const char *,
+			     const struct var_expand_table *, bool);
+
+static void
+mail_process_init_env(const struct mail_env_bits *bits, bool private_env)
+{
+	unsigned int i, count;
+	const char *const *args, *p;
+
+	child_process_init_env_private(private_env);
+
+	/* setup environment - set the most important environment first
+	   (paranoia about filling up environment without noticing) */
+	restrict_access_set_env(bits->system_user, bits->uid, bits->gid,
+				bits->set->mail_priv_gid_t, bits->chroot_dir,
+				bits->set->first_valid_gid,
+				bits->set->last_valid_gid,
+				bits->set->mail_access_groups,
+				bits->persistent_mail_process);
+
+	if (bits->dump_capability)
+		env_put("DUMP_CAPABILITY=1");
+
+	mail_process_set_environment(bits->set, bits->mail,
+				     bits->var_expand_table,
+				     bits->dump_capability);
+
+	/* extra args. uppercase key value. */
+	args = array_get(bits->extra_args, &count);
+	for (i = 0; i < count; i++) {
+		if (*args[i] == '=') {
+			/* Should be caught by dovecot-auth already */
+			i_fatal("Userdb returned data with empty key (%s)",
+				args[i]);
+		}
+		p = strchr(args[i], '=');
+		if (p == NULL) {
+			/* boolean */
+			env_put(t_strconcat(t_str_ucase(args[i]), "=1", NULL));
+
+		} else {
+			/* key=value */
+			env_put(t_strconcat(t_str_ucase(
+				t_strdup_until(args[i], p)), p, NULL));
+		}
+	}
+
+	env_put("LOGGED_IN=1");
+	env_put(t_strconcat("HOME=", bits->home_dir, NULL));
+	env_put(t_strconcat("USER=", bits->user, NULL));
+	env_put(t_strconcat("IP=", bits->addr, NULL));
+	env_put(t_strconcat("LOCAL_IP=", bits->local_ip, NULL));
+
+	if (bits->persistent_mail_process) {
+		env_put("PERSISTENT_MAIL_PROCESS=1");
+		env_put(t_strdup_printf("CONNECTION_ID=%u",
+					bits->connection_id));
+	}
+}
+
+static void
+persistent_mail_process_buffer_env(buffer_t *buffer,
+				   const struct mail_env_bits *bits)
+{
+	extern char **environ;
+	char **env;
+
+	/* Allocate all of the environment strings from env-util's private
+	   pool, which is all then released at the env_clean() below.  If
+	   we used the system's putenv() instead, all the strings it mallocs
+	   would be leaked. */
+	mail_process_init_env(bits, TRUE);
+
+	for (env = environ; *env != NULL; env++) {
+		buffer_append(buffer, *env, strlen(*env));
+		if (env[1] != NULL)
+			buffer_append_c(buffer, '\n');
+	}
+
+	env_clean();
+}
+
+static int
+persistent_mail_process_request(struct persistent_mail_process *process,
+				int socket_fd,
+				const struct mail_env_bits *bits)
+{
+	static const char hotpotato[] = HOTPOTATO;
+	uint32_t id;
+	char message[PERSISTENT_MAIL_MASTER_MESSAGE_SIZE];
+	ssize_t sent;
+
+	if (o_stream_get_buffer_used_size(process->output) >=
+	    MAX_OUTBUF_SIZE) {
+		i_warning("Persistent mail process %s transmit buffer full; "
+			  "skipping",
+			  dec2str(process->pid));
+		return 0;
+	}
+
+	/* The login process passed the fd to us, now we hand it to the
+	   persistent mail process.  It's a hot potato.  Here the
+	   connection_id doubles as a receipt for the fd; see below. */
+	id = htonl(bits->connection_id);
+	memcpy(message, hotpotato, sizeof hotpotato - 1);
+	memcpy(&message[sizeof hotpotato - 1], &id, sizeof id);
+	sent = fd_send(process->fd, socket_fd, message, sizeof message);
+	if (sent == sizeof message) {
+		buffer_t *buffer;
+		const void *data;
+		size_t data_len;
+		uint32_t env_len;
+
+		buffer = buffer_create_dynamic(pool_datastack_create(), 2048);
+		env_len = 0;
+		buffer_append(buffer, &env_len, sizeof env_len);
+		persistent_mail_process_buffer_env(buffer, bits);
+
+		data = buffer_get_data(buffer, &data_len);
+		i_assert(data_len >= sizeof env_len);
+		env_len = data_len - sizeof env_len;
+		env_len = htonl(env_len);
+		buffer_write(buffer, 0, &env_len, sizeof env_len);
+
+		sent = o_stream_send(process->output, data, data_len);
+		if (sent == (ssize_t) data_len) {
+			int pending_fd;
+
+			/* can't close socket (on MacOSX) until receiver
+			   has it so stick a copy of it in the pending
+			   list.  could stash without dup but that would
+			   change too much other code. */
+			pending_fd = dup(socket_fd);
+			if (pending_fd >= 0)
+				fd_close_on_exec(pending_fd, TRUE);
+			/* else don't sweat if dup failed.  this is just
+			   a temporary(?) workaround anyway */
+
+			hash_insert(process->pending,
+				    POINTER_CAST(bits->connection_id),
+				    persistent_mail_pender_new(pending_fd));
+			return 1;
+		}
+	}
+
+	if (sent < 0)
+		i_error("Error sending request to "
+			"persistent mail process %s: %m",
+			dec2str(process->pid));
+	else
+		i_error("Persistent mail process %s "
+			"transmit buffer full; detaching",
+			dec2str(process->pid));
+	persistent_mail_process_destroy(process);
+	return -1;
+}
+
+static int persistent_mail_process_cmp(const void *a, const void *b)
+{
+	const struct persistent_mail_process * const *app = a;
+	const struct persistent_mail_process * const *bpp = b;
+	const struct persistent_mail_process *process_a = *app;
+	const struct persistent_mail_process *process_b = *bpp;
+	int count_a = array_count(&process_a->connections);
+	int count_b = array_count(&process_b->connections);
+	return count_a - count_b;
+}
+/* APPLE end */
+
 static bool validate_uid_gid(struct settings *set, uid_t uid, gid_t gid,
 			     const char *user)
 {
@@ -167,11 +1021,12 @@
 	return FALSE;
 }
 
+/* APPLE changed type of pid/uid args*/
 static const struct var_expand_table *
 get_var_expand_table(const char *protocol,
 		     const char *user, const char *home,
 		     const char *local_ip, const char *remote_ip,
-		     pid_t pid, uid_t uid)
+		     const char *pid, const char *uid)
 {
 #define VAR_EXPAND_HOME_IDX 4
 	static struct var_expand_table static_tab[] = {
@@ -199,8 +1054,8 @@
 	tab[VAR_EXPAND_HOME_IDX].value = home;
 	tab[5].value = local_ip;
 	tab[6].value = remote_ip;
-	tab[7].value = dec2str(pid);
-	tab[8].value = dec2str(uid);
+	tab[7].value = pid;
+	tab[8].value = uid;
 
 	return tab;
 }
@@ -467,7 +1322,7 @@
 		get_var_expand_table(protocol, getenv("USER"), getenv("HOME"),
 				     getenv("TCPLOCALIP"),
 				     getenv("TCPREMOTEIP"),
-				     getpid(), geteuid());
+				     dec2str(getpid()), dec2str(geteuid()));
 
 	/* set up logging */
 	env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
@@ -550,15 +1405,24 @@
 	pid_t pid;
 	uid_t uid;
 	gid_t gid;
-	ARRAY_DEFINE(extra_args, const char *);
-	unsigned int i, len, count, left, process_count, throttle;
+	ARRAY_TYPE(const_string) extra_args;	/* APPLE new decl, same type */
+	unsigned int i, len, left, process_count, throttle;
 	int ret, log_fd, nice_value, chdir_errno;
 	bool home_given, nfs_check;
+	/* APPLE begin */
+	unsigned int connection_id;
+	const char *mail_location;
+	struct persistent_mail_process_group *pmp_group;
+	int pmp_fd[2];
+	const struct var_expand_table *pmp_var_expand_table;
+	struct mail_env_bits bits;
+	/* APPLE end */
 
 	i_assert(process_type == PROCESS_TYPE_IMAP ||
 		 process_type == PROCESS_TYPE_POP3);
 
-	if (mail_process_count == set->max_mail_processes) {
+	if (set->mail_process_per_connection &&		/* APPLE */
+	    mail_process_count == set->max_mail_processes) {
 		i_error("Maximum number of mail processes exceeded "
 			"(see max_mail_processes setting)");
 		return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
@@ -605,7 +1469,9 @@
 	process_group = dump_capability ? NULL :
 		mail_process_group_lookup(process_type, user, remote_ip);
 	process_count = process_group == NULL ? 0 :
-		array_count(&process_group->processes);
+		array_count(&process_group->processes) +
+		array_count(&process_group->connections);	/* APPLE */
+	/* APPLE process_count is really count of connections */
 	if (process_count >= set->mail_max_userip_connections &&
 	    set->mail_max_userip_connections != 0 &&
 	    master_user == NULL)
@@ -669,9 +1535,115 @@
 		home_dir += len - 2;
 	}
 
+	/* APPLE begin */
+	do
+		connection_id = ++next_connection_id;
+	while (connection_id == 0 ||
+	       (!dump_capability &&
+		hash_lookup(mail_connections,
+			    POINTER_CAST(connection_id)) != NULL));
+	/* managesieve patches in a new process_type; keep it out of pmp */
+	if ((process_type == PROCESS_TYPE_IMAP ||
+	     process_type == PROCESS_TYPE_POP3) &&
+	    !set->mail_process_per_connection && !dump_capability) {
+		struct persistent_mail_process *process, **process_vec;
+		ARRAY_DEFINE(process_arr, struct persistent_mail_process *);
+
+		if (*chroot_dir != '\0' &&
+		    strcmp(chroot_dir, set->mail_chroot) != 0) {
+			i_error("Can't chroot to directory '%s' (user %s) "
+				"with mail_process_per_connection=no",
+				chroot_dir, user);
+			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
+		}
+#ifdef HAVE_SETPRIORITY
+		if (nice_value != 0)
+			i_warning("Can't adjust nice %+d (user %s) "
+				  "with mail_process_per_connection=no; "
+				  "ignoring nice setting",
+				  nice_value, user);
+#endif
+
+		pmp_group = persistent_mail_process_group_find(set);
+		i_assert(pmp_group != NULL);	/* here's hoping */
+
+		/* use pmp with fewest connections */
+		t_array_init(&process_arr, mail_process_count);
+		for (process = pmp_group->processes; process != NULL;
+		     process = process->next)
+			array_append(&process_arr, &process, 1);
+		process_vec = array_get_modifiable(&process_arr,
+						   &process_count);
+		qsort(process_vec, process_count, sizeof *process_vec,
+		      persistent_mail_process_cmp);
+		for (i = 0; i < process_count; i++) {
+			unsigned int connection_count;
+
+			process = process_vec[i];
+
+			connection_count = array_count(&process->connections);
+			if (connection_count == 0 ||
+			    connection_count >= set->mail_max_connections)
+				continue;
+
+			var_expand_table =
+				get_var_expand_table(process_names[process_type],
+						     user, home_given ? home_dir : NULL,
+						     net_ip2addr(local_ip),
+						     net_ip2addr(remote_ip),
+						     dec2str(process->pid),
+						     dec2str(uid));
+			addr = net_ip2addr(remote_ip);
+			mail_env_bits_init(&bits, connection_id, set,
+					   system_user, uid, gid, chroot_dir,
+					   mail, home_dir, user, addr, net_ip2addr(local_ip),
+					   var_expand_table, &extra_args,
+					   dump_capability, TRUE);
+			ret = persistent_mail_process_request(process,
+							      socket_fd,
+							      &bits);
+			if (ret > 0) {
+				struct mail_connection *conn;
+
+				if (process_group == NULL) {
+					process_group =
+						mail_process_group_create(process_type,
+									  user,
+									  remote_ip);
+				}
+
+				conn = array_append_space(&process_group->connections);
+				conn->connection_id = connection_id;
+				conn->connected_at = ioloop_time;
+
+				array_append(&process->connections,
+					     &connection_id, 1);
+				hash_insert(mail_connections,
+					    POINTER_CAST(connection_id),
+					    process_group);
+
+				return MASTER_LOGIN_STATUS_OK;
+			} else if (ret < 0)
+				return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
+		}
+		/* fall through to make a new persistent mail process */
+	} else
+		pmp_group = NULL;
+	if (mail_process_count == set->max_mail_processes) {
+		i_error("Maximum number of mail processes exceeded "
+			"(see max_mail_processes setting)");
+		return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
+	}
+	/* APPLE end */
+
 	if (!dump_capability) {
 		throttle = set->mail_debug ? 0 :
 			set->mail_log_max_lines_per_sec;
+
+		/* APPLE */
+		if (pmp_group != NULL)
+			throttle *= set->mail_max_connections;
+
 		log_fd = log_create_pipe(&log, throttle);
 		if (log_fd == -1)
 			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
@@ -690,13 +1662,35 @@
 	if (set->nfs_check && !set->mail_nfs_index && !dump_capability) {
 		set->nfs_check = FALSE;
 		nfs_check = TRUE;
+
+		/* APPLE moved this chunk */
+		/* ideally we should check all of the namespaces,
+		   but for now don't bother.  need to read environment
+		   before clobbering it below. */
+		mail_location = getenv("NAMESPACE_1");
+		if (mail_location == NULL)
+			mail_location = getenv("MAIL");
 	} else {
 		nfs_check = FALSE;
+		mail_location = NULL;		/* APPLE hush compiler */
 	}
 
+	/* APPLE */
+	if (pmp_group) {
+		if (socketpair(AF_UNIX, SOCK_STREAM, 0, pmp_fd) < 0) {
+			i_error("socketpair() failed: %m");
+			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
+		}
+	} else
+		pmp_fd[0] = pmp_fd[1] = -1;
+
 	pid = fork();
 	if (pid < 0) {
 		i_error("fork() failed: %m");
+		if (pmp_fd[0] >= 0) {			/* APPLE */
+			(void) close(pmp_fd[0]);
+			(void) close(pmp_fd[1]);
+		}
 		(void)close(log_fd);
 		return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
 	}
@@ -706,12 +1700,22 @@
 				     user, home_given ? home_dir : NULL,
 				     net_ip2addr(local_ip),
 				     net_ip2addr(remote_ip),
-				     pid != 0 ? pid : getpid(), uid);
+				     dec2str(pid != 0 ? pid : getpid()),
+				     dec2str(uid));
+	/* APPLE */
+	pmp_var_expand_table = pmp_group == NULL ? NULL :
+		get_var_expand_table(process_names[process_type],
+				     "*", "*", "*", "*",
+				     dec2str(pid != 0 ? pid : getpid()),
+				     "*");
+				     
 	str = t_str_new(128);
 
 	if (pid != 0) {
 		/* master */
-		var_expand(str, set->mail_log_prefix, var_expand_table);
+		var_expand(str, set->mail_log_prefix,
+			   pmp_group == NULL ?		/* APPLE */
+				var_expand_table : pmp_var_expand_table);
 
 		if (!dump_capability) {
 			log_set_prefix(log, str_c(str));
@@ -722,7 +1726,28 @@
 								  user,
 								  remote_ip);
 			}
-			mail_process_group_add(process_group, pid);
+			mail_process_group_add(process_group, pid,
+					       connection_id,	    /* APPLE */
+					       pmp_group != NULL);  /* APPLE */
+
+			/* APPLE */
+			if (pmp_group != NULL) {
+				struct persistent_mail_process *pmp;
+
+				net_set_nonblock(pmp_fd[0], TRUE);
+				fd_close_on_exec(pmp_fd[0], TRUE);
+				net_set_sndbuf(pmp_fd[0], MAX_OUTBUF_SIZE);
+				pmp = persistent_mail_process_new(pid,
+								  pmp_fd[0],
+								  pmp_group);
+				(void) close(pmp_fd[1]);
+
+				array_append(&pmp->connections,
+					     &connection_id, 1);
+				hash_insert(mail_connections,
+					    POINTER_CAST(connection_id),
+					    process_group);
+			}
 		}
 		(void)close(log_fd);
 		return MASTER_LOGIN_STATUS_OK;
@@ -737,36 +1762,60 @@
 
 	if (!dump_capability) {
 		str_append(str, "master-");
-		var_expand(str, set->mail_log_prefix, var_expand_table);
+		var_expand(str, set->mail_log_prefix,
+			   pmp_group == NULL ?		/* APPLE */
+				var_expand_table : pmp_var_expand_table);
 		log_set_prefix(log, str_c(str));
 	}
 
-	child_process_init_env();
+	if (pmp_group != NULL) {		/* APPLE */
+		/* move master communication handle to 0 */
+		if (dup2(pmp_fd[1], 0) < 0)
+			i_fatal("dup2(stdin) failed: %m");
+
+		(void) close(pmp_fd[0]);
+		(void) close(pmp_fd[1]);
+		pmp_fd[0] = pmp_fd[1] = -1;
+
+		/* set stdout to /dev/null to ignore output */
+		if (dup2(null_fd, 1) < 0)
+			i_fatal("dup2(stdout) failed: %m");
+
+		/* stderr = log, 3 = client socket */
+		if (dup2(log_fd, 2) < 0)
+			i_fatal("dup2(stderr) failed: %m");
+		if (socket_fd != 3) {
+			if (dup2(socket_fd, 3) < 0)
+				i_fatal("dup2(3) failed: %m");
+		}
 
-	/* move the client socket into stdin and stdout fds, log to stderr */
-	if (dup2(dump_capability ? null_fd : socket_fd, 0) < 0)
-		i_fatal("dup2(stdin) failed: %m");
-	if (dup2(socket_fd, 1) < 0)
-		i_fatal("dup2(stdout) failed: %m");
-	if (dup2(log_fd, 2) < 0)
-		i_fatal("dup2(stderr) failed: %m");
+		for (i = 0; i <= 3; i++)
+			fd_close_on_exec(i, FALSE);
+	} else {
+		/* move the client socket into stdin and stdout fds, log to stderr */
+		if (dup2(dump_capability ? null_fd : socket_fd, 0) < 0)
+			i_fatal("dup2(stdin) failed: %m");
+		if (dup2(socket_fd, 1) < 0)
+			i_fatal("dup2(stdout) failed: %m");
+		if (dup2(log_fd, 2) < 0)
+			i_fatal("dup2(stderr) failed: %m");
 
-	for (i = 0; i < 3; i++)
-		fd_close_on_exec(i, FALSE);
+		for (i = 0; i < 3; i++)
+			fd_close_on_exec(i, FALSE);
+	}
 
-	/* setup environment - set the most important environment first
-	   (paranoia about filling up environment without noticing) */
-	restrict_access_set_env(system_user, uid, gid, set->mail_priv_gid_t,
-				chroot_dir,
-				set->first_valid_gid, set->last_valid_gid,
-				set->mail_access_groups);
+	/* APPLE significant reordering/refactoring of environment code */
+	addr = net_ip2addr(remote_ip);
+	mail_env_bits_init(&bits, connection_id, set, system_user, uid, gid,
+			   chroot_dir, mail, home_dir, user, addr, net_ip2addr(local_ip),
+			   var_expand_table, &extra_args, dump_capability,
+			   pmp_group != NULL);
+	mail_process_init_env(&bits, FALSE);
 
 	restrict_process_size(set->mail_process_size, (unsigned int)-1);
 
-	if (dump_capability)
-		env_put("DUMP_CAPABILITY=1");
-
-	if (*home_dir == '\0' && *chroot_dir == '\0') {
+	if ((*home_dir == '\0' && *chroot_dir == '\0') ||
+	    pmp_group != NULL) {		/* APPLE */
 		full_home_dir = "";
 		ret = -1;
 	} else {
@@ -814,48 +1863,11 @@
 			i_fatal("chdir(/tmp) failed: %m");
 	}
 
-	mail_process_set_environment(set, mail, var_expand_table,
-				     dump_capability);
-
-	/* extra args. uppercase key value. */
-	args = array_get(&extra_args, &count);
-	for (i = 0; i < count; i++) {
-		if (*args[i] == '=') {
-			/* Should be caught by dovecot-auth already */
-			i_fatal("Userdb returned data with empty key (%s)",
-				args[i]);
-		}
-		p = strchr(args[i], '=');
-		if (p == NULL) {
-			/* boolean */
-			env_put(t_strconcat(t_str_ucase(args[i]), "=1", NULL));
-
-		} else {
-			/* key=value */
-			env_put(t_strconcat(t_str_ucase(
-				t_strdup_until(args[i], p)), p, NULL));
-		}
-	}
-
-	if (nfs_check) {
-		/* ideally we should check all of the namespaces,
-		   but for now don't bother. */
-		const char *mail_location = getenv("NAMESPACE_1");
-
-		if (mail_location == NULL)
-			mail_location = getenv("MAIL");
+	if (nfs_check)
 		nfs_warn_if_found(mail_location, full_home_dir);
-	}
 
-	env_put("LOGGED_IN=1");
-	env_put(t_strconcat("HOME=", home_dir, NULL));
-	env_put(t_strconcat("USER=", user, NULL));
-
-	addr = net_ip2addr(remote_ip);
-	env_put(t_strconcat("IP=", addr, NULL));
-	env_put(t_strconcat("LOCAL_IP=", net_ip2addr(local_ip), NULL));
-
-	if (!set->verbose_proctitle)
+	if (!set->verbose_proctitle ||
+	    pmp_group != NULL)			/* APPLE */
 		title[0] = '\0';
 	else {
 		if (addr == NULL)
@@ -889,25 +1901,32 @@
 		       pid_t pid, bool abnormal_exit ATTR_UNUSED)
 {
 	struct mail_process_group *group = (struct mail_process_group *)process;
-	const pid_t *pids;
+	const struct mail_process *processes;
 	unsigned int i, count;
 
-	pids = array_get(&group->processes, &count);
-	if (count == 1) {
-		/* last process in this group */
-		i_assert(pids[0] == pid);
-		hash_remove(mail_process_groups, group);
-		mail_process_group_free(group);
-	} else {
-		for (i = 0; i < count; i++) {
-			if (pids[i] == pid)
-				break;
-		}
-		i_assert(i != count);
+	processes = array_get(&group->processes, &count);
+
+	/* APPLE reworked to support processes and connections arrays */
+	i_assert(group->children > 0);
+	for (i = 0; i < count; i++)
+		if (processes[i].pid == pid)
+			break;
+	if (i != count)
 		array_delete(&group->processes, i, 1);
-	}
+	else
+		i_assert(count == 0);
 
 	mail_process_count--;
+
+	persistent_mail_process_destroyed(pid);
+
+	group->children -= 1;
+	if (group->children == 0 &&
+	    array_count(&group->processes) == 0 &&
+	    array_count(&group->connections) == 0) {
+		hash_remove(mail_process_groups, group);
+		mail_process_group_free(group);
+	}
 }
 
 void mail_processes_init(void)
@@ -916,6 +1935,14 @@
 					  mail_process_group_hash,
 					  mail_process_group_cmp);
 
+	/* APPLE */
+	persistent_mail_process_groups = NULL;
+	mail_connections = hash_table_create(default_pool, default_pool, 0, NULL,
+					     NULL);
+#ifdef SIGINFO
+	lib_signals_set_handler(SIGINFO, TRUE, sig_info, NULL);
+#endif
+
 	child_process_set_destroy_callback(PROCESS_TYPE_IMAP,
 					   mail_process_destroyed);
 	child_process_set_destroy_callback(PROCESS_TYPE_POP3,
@@ -927,6 +1954,15 @@
 	struct hash_iterate_context *iter;
 	void *key, *value;
 
+	/* APPLE */
+	while (persistent_mail_process_groups != NULL) {
+		struct persistent_mail_process_group *group = persistent_mail_process_groups;
+
+		persistent_mail_process_groups = group->next;
+		persistent_mail_process_group_destroy(group);
+	}
+	hash_table_destroy(&mail_connections);
+
 	iter = hash_iterate_init(mail_process_groups);
 	while (hash_iterate(iter, &key, &value)) {
 		struct mail_process_group *group = value;
diff -Nur dovecot-1.1.7+patch9/src/master/mail-process.h dovecot-patch/src/master/mail-process.h
--- dovecot-1.1.7+patch9/src/master/mail-process.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/master/mail-process.h	2009-01-06 10:08:19.000000000 -0600
@@ -18,4 +18,6 @@
 void mail_processes_init(void);
 void mail_processes_deinit(void);
 
+void persistent_mail_processes_destroy_all(void);	/* APPLE */
+
 #endif
diff -Nur dovecot-1.1.7+patch9/src/master/main.c dovecot-patch/src/master/main.c
--- dovecot-1.1.7+patch9/src/master/main.c	2009-01-06 10:08:10.000000000 -0600
+++ dovecot-patch/src/master/main.c	2009-01-06 10:08:19.000000000 -0600
@@ -148,6 +148,7 @@
 	i_warning("SIGHUP received - reloading configuration");
 
 	/* restart auth and login processes */
+	persistent_mail_processes_destroy_all();	/* APPLE */
         login_processes_destroy_all();
         auth_processes_destroy_all();
         dict_process_kill();
diff -Nur dovecot-1.1.7+patch9/src/master/master-settings-defs.c dovecot-patch/src/master/master-settings-defs.c
--- dovecot-1.1.7+patch9/src/master/master-settings-defs.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/master/master-settings-defs.c	2009-01-06 10:08:19.000000000 -0600
@@ -103,6 +103,8 @@
 	DEF_BOOL(mail_drop_priv_before_exec),
 
 	DEF_STR(mail_executable),
+	DEF_BOOL(mail_process_per_connection),		/* APPLE */
+	DEF_INT(mail_max_connections),			/* APPLE */
 	DEF_INT(mail_process_size),
 	DEF_STR(mail_plugins),
 	DEF_STR(mail_plugin_dir),
diff -Nur dovecot-1.1.7+patch9/src/master/master-settings.c dovecot-patch/src/master/master-settings.c
--- dovecot-1.1.7+patch9/src/master/master-settings.c	2008-11-15 11:13:18.000000000 -0600
+++ dovecot-patch/src/master/master-settings.c	2009-01-06 11:32:21.000000000 -0600
@@ -269,6 +269,8 @@
 	MEMBER(mail_drop_priv_before_exec) FALSE,
 
 	MEMBER(mail_executable) PKG_LIBEXECDIR"/imap",
+	MEMBER(mail_process_per_connection) TRUE,	/* APPLE */
+	MEMBER(mail_max_connections) 20,		/* APPLE */
 	MEMBER(mail_process_size) 256,
 	MEMBER(mail_plugins) "",
 	MEMBER(mail_plugin_dir) MODULEDIR"/imap",
@@ -812,6 +814,27 @@
 		return FALSE;
 	}
 
+	/* APPLE */
+	if (!set->mail_process_per_connection &&
+	    ((set->protocol == MAIL_PROTOCOL_IMAP &&
+	      strcmp(set->mail_executable, PKG_LIBEXECDIR"/imap") != 0) ||
+	     (set->protocol == MAIL_PROTOCOL_POP3 &&
+	      strcmp(set->mail_executable, PKG_LIBEXECDIR"/pop3") != 0))) {
+		/* mail_process_per_connection=no changes the interface
+		   between the master and the mail processes.  Don't
+		   break unaware scripts.  Force with trailing !.*/
+		size_t len = strlen(set->mail_executable);
+		if (len <= 0 || set->mail_executable[len - 1] != '!') {
+			i_error("mail_executable must be the default when "
+				"mail_process_per_connection=no; force with !");
+			return FALSE;
+		}
+	}
+	if (set->mail_max_connections < 1) {
+		i_error("mail_max_connections must be at least 1");
+		return FALSE;
+	}
+
 	if (set->last_valid_uid != 0 &&
 	    set->first_valid_uid > set->last_valid_uid) {
 		i_error("first_valid_uid can't be larger than last_valid_uid");
diff -Nur dovecot-1.1.7+patch9/src/master/master-settings.h dovecot-patch/src/master/master-settings.h
--- dovecot-1.1.7+patch9/src/master/master-settings.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/master/master-settings.h	2009-01-06 10:08:19.000000000 -0600
@@ -115,6 +115,8 @@
 	bool mail_drop_priv_before_exec;
 
 	const char *mail_executable;
+	bool mail_process_per_connection;		/* APPLE */
+	unsigned int mail_max_connections;		/* APPLE */
 	unsigned int mail_process_size;
 	const char *mail_plugins;
 	const char *mail_plugin_dir;
diff -Nur dovecot-1.1.7+patch9/src/plugins/acl/acl-mailbox-list.c dovecot-patch/src/plugins/acl/acl-mailbox-list.c
--- dovecot-1.1.7+patch9/src/plugins/acl/acl-mailbox-list.c	2008-10-31 10:46:10.000000000 -0500
+++ dovecot-patch/src/plugins/acl/acl-mailbox-list.c	2009-01-06 10:08:19.000000000 -0600
@@ -404,10 +404,7 @@
 	acl_env = getenv("ACL");
 	i_assert(acl_env != NULL);
 
-	owner_username = getenv("USER");
-	if (owner_username == NULL)
-		i_fatal("ACL: USER environment not set");
-
+	owner_username = list->ns->user->username;
 	current_username = getenv("MASTER_USER");
 	if (current_username == NULL)
 		current_username = owner_username;
diff -Nur dovecot-1.1.7+patch9/src/plugins/convert/convert-plugin.c dovecot-patch/src/plugins/convert/convert-plugin.c
--- dovecot-1.1.7+patch9/src/plugins/convert/convert-plugin.c	2008-11-15 11:21:03.000000000 -0600
+++ dovecot-patch/src/plugins/convert/convert-plugin.c	2009-01-06 10:08:19.000000000 -0600
@@ -19,11 +19,7 @@
 	struct convert_settings set;
 
 	memset(&set, 0, sizeof(set));
-	set.user = getenv("USER");
-	if (set.user == NULL)
-		i_fatal("convert plugin: USER unset");
-	set.home = getenv("HOME");
-	if (set.home == NULL)
+	if (namespaces->user->home == NULL)
 		i_fatal("convert plugin: HOME unset");
 
 	set.skip_broken_mailboxes =
diff -Nur dovecot-1.1.7+patch9/src/plugins/convert/convert-storage.c dovecot-patch/src/plugins/convert/convert-storage.c
--- dovecot-1.1.7+patch9/src/plugins/convert/convert-storage.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/convert/convert-storage.c	2009-01-06 10:08:19.000000000 -0600
@@ -390,6 +390,7 @@
 		    struct mail_namespace *dest_namespaces,
 		    const struct convert_settings *set)
 {
+	struct mail_user *user = dest_namespaces->user;
 	struct mail_namespace *source_ns, *dest_inbox_ns;
 	struct dotlock *dotlock;
         enum mail_storage_flags src_flags;
@@ -397,19 +398,19 @@
 	const char *path, *error;
 	int ret;
 
-	source_ns = mail_namespaces_init_empty(pool_datastack_create());
+	source_ns = mail_namespaces_init_empty(user);
 	dest_inbox_ns = mail_namespace_find_inbox(dest_namespaces);
 	src_flags = dest_inbox_ns->storage->flags;
 	lock_method = dest_inbox_ns->storage->lock_method;
 
 	src_flags |= MAIL_STORAGE_FLAG_NO_AUTOCREATE;
-	if (mail_storage_create(source_ns, NULL, source_data, set->user,
+	if (mail_storage_create(source_ns, NULL, source_data,
 				src_flags, lock_method, &error) < 0) {
 		/* No need for conversion. */
 		return 0;
 	}
 
-        path = t_strconcat(set->home, "/"CONVERT_LOCK_FILENAME, NULL);
+        path = t_strconcat(user->home, "/"CONVERT_LOCK_FILENAME, NULL);
 	dotlock_settings.use_excl_lock =
 		(source_ns->storage->flags &
 		 MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0;
@@ -428,7 +429,7 @@
 	/* just in case if another process just had converted the mailbox,
 	   reopen the source storage */
 	mail_storage_destroy(&source_ns->storage);
-	if (mail_storage_create(source_ns, NULL, source_data, set->user,
+	if (mail_storage_create(source_ns, NULL, source_data,
 				src_flags, lock_method, &error) < 0) {
 		/* No need for conversion anymore. */
 		file_dotlock_delete(&dotlock);
diff -Nur dovecot-1.1.7+patch9/src/plugins/convert/convert-storage.h dovecot-patch/src/plugins/convert/convert-storage.h
--- dovecot-1.1.7+patch9/src/plugins/convert/convert-storage.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/convert/convert-storage.h	2009-01-06 10:08:19.000000000 -0600
@@ -4,8 +4,6 @@
 struct mail_namespace;
 
 struct convert_settings {
-	const char *user;
-	const char *home;
 	bool skip_broken_mailboxes;
 	bool skip_dotdirs;
 	char alt_hierarchy_char;
diff -Nur dovecot-1.1.7+patch9/src/plugins/convert/convert-tool.c dovecot-patch/src/plugins/convert/convert-tool.c
--- dovecot-1.1.7+patch9/src/plugins/convert/convert-tool.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/convert/convert-tool.c	2009-01-06 10:08:19.000000000 -0600
@@ -18,6 +18,7 @@
 {
 	struct ioloop *ioloop;
 	struct convert_settings set;
+	struct mail_user *user;
 	struct mail_namespace *dest_ns;
         enum mail_storage_flags dest_flags;
 	enum file_lock_method lock_method;
@@ -37,9 +38,6 @@
 	ioloop = io_loop_create();
 
 	memset(&set, 0, sizeof(set));
-	set.user = argv[1];
-	set.home = argv[2];
-
 	for (i = 5; i < argc; i++) {
 		if (strcmp(argv[i], "skip_broken_mailboxes") != 0)
 			set.skip_broken_mailboxes = TRUE;
@@ -50,8 +48,10 @@
 	}
 
 	mail_storage_parse_env(&dest_flags, &lock_method);
-	dest_ns = mail_namespaces_init_empty(pool_datastack_create());
-	if (mail_storage_create(dest_ns, NULL, argv[4], set.user,
+	user = mail_user_init(argv[1], argv[2]);
+	dest_ns = mail_namespaces_init_empty(user);
+
+	if (mail_storage_create(dest_ns, NULL, argv[4],
 				dest_flags, lock_method, &error) < 0) {
 		i_fatal("Failed to create destination "
 			"mail storage with data '%s': %s", argv[4], error);
@@ -64,7 +64,7 @@
 		i_error("Source storage not found");
 	else
 		i_error("Internal failure");
-	mail_namespaces_deinit(&dest_ns);
+	mail_user_deinit(&user);
 
 	io_loop_destroy(&ioloop);
 	mail_storage_deinit();
diff -Nur dovecot-1.1.7+patch9/src/plugins/expire/expire-plugin.c dovecot-patch/src/plugins/expire/expire-plugin.c
--- dovecot-1.1.7+patch9/src/plugins/expire/expire-plugin.c	2008-11-15 11:21:05.000000000 -0600
+++ dovecot-patch/src/plugins/expire/expire-plugin.c	2009-01-06 11:31:00.000000000 -0600
@@ -289,6 +289,11 @@
 {
 	const char *expunge_env, *altmove_env, *dict_uri;
 
+	/* APPLE XXX teach this plugin about persistent mail processes */
+	if (getenv("PERSISTENT_MAIL_PROCESS") != NULL)
+		i_fatal("expire plugin incompatible with "
+			"mail_process_per_connection=no");
+
 	expunge_env = getenv("EXPIRE");
 	altmove_env = getenv("EXPIRE_ALTMOVE");
 	if (expunge_env != NULL || altmove_env != NULL) {
diff -Nur dovecot-1.1.7+patch9/src/plugins/expire/expire-tool.c dovecot-patch/src/plugins/expire/expire-tool.c
--- dovecot-1.1.7+patch9/src/plugins/expire/expire-tool.c	2008-11-05 12:01:47.000000000 -0600
+++ dovecot-patch/src/plugins/expire/expire-tool.c	2009-01-06 10:08:19.000000000 -0600
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "ioloop.h"
+#include "env-util.h"
 #include "file-lock.h"
 #include "randgen.h"
 #include "lib-signals.h"
@@ -25,8 +26,7 @@
 	struct auth_connection *auth_conn;
 
 	char *user;
-	pool_t namespace_pool;
-	struct mail_namespace *ns;
+	struct mail_user *mail_user;
 	bool testrun;
 };
 
@@ -34,6 +34,7 @@
 {
 	int ret;
 
+	env_clean();
 	if ((ret = auth_client_put_user_env(ctx->auth_conn, user)) <= 0) {
 		if (ret < 0)
 			return ret;
@@ -42,16 +43,16 @@
 		return 0;
 	}
 
-	if (mail_namespaces_init(ctx->namespace_pool, user, &ctx->ns) < 0)
+	ctx->mail_user = mail_user_init(user, getenv("HOME"));
+	if (mail_namespaces_init(ctx->mail_user) < 0)
 		return -1;
 	return 1;
 }
 
 static void user_deinit(struct expire_context *ctx)
 {
-	mail_namespaces_deinit(&ctx->ns);
+	mail_user_deinit(&ctx->mail_user);
 	i_free_and_null(ctx->user);
-	p_clear(ctx->namespace_pool);
 }
 
 static int
@@ -90,7 +91,7 @@
 	search_arg.next = NULL;
 
 	ns_mailbox = mailbox;
-	ns = mail_namespace_find(ctx->ns, &ns_mailbox);
+	ns = mail_namespace_find(ctx->mail_user->namespaces, &ns_mailbox);
 	if (ns == NULL) {
 		/* entire namespace no longer exists, remove the entry */
 		if (ctx->testrun)
@@ -203,7 +204,6 @@
 	memset(&ctx, 0, sizeof(ctx));
 	ctx.testrun = testrun;
 	ctx.auth_conn = auth_connection_init(auth_socket);
-	ctx.namespace_pool = pool_alloconly_create("namespaces", 1024);
 	env = expire_env_init(getenv("EXPIRE"), getenv("EXPIRE_ALTMOVE"));
 	dict = dict_init(getenv("EXPIRE_DICT"), DICT_DATA_TYPE_UINT32, "");
 	if (dict == NULL)
diff -Nur dovecot-1.1.7+patch9/src/plugins/fts-solr/fts-backend-solr.c dovecot-patch/src/plugins/fts-solr/fts-backend-solr.c
--- dovecot-1.1.7+patch9/src/plugins/fts-solr/fts-backend-solr.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/fts-solr/fts-backend-solr.c	2009-01-06 10:08:19.000000000 -0600
@@ -4,6 +4,7 @@
 #include "array.h"
 #include "str.h"
 #include "mail-storage-private.h"
+#include "mail-namespace.h"
 #include "solr-connection.h"
 #include "fts-solr-plugin.h"
 
@@ -93,7 +94,7 @@
 	str_printfa(str, "uidv:%u%%20box:", status.uidvalidity);
 	solr_quote_str(str, backend->box->name);
 	str_append(str, "%20user:");
-	solr_quote_str(str, backend->box->storage->user);
+	solr_quote_str(str, backend->box->storage->ns->user->username);
 
 	t_array_init(&uids, 1);
 	if (solr_connection_select(solr_conn, str_c(str), &uids, NULL) < 0)
@@ -160,11 +161,11 @@
 		str_append(cmd, "<field name=\"box\">");
 		xml_encode(cmd, box->name);
 		str_append(cmd, "</field><field name=\"user\">");
-		xml_encode(cmd, box->storage->user);
+		xml_encode(cmd, box->storage->ns->user->username);
 
 		str_printfa(cmd, "</field><field name=\"id\">%u/%u/",
 			    uid, ctx->uid_validity);
-		xml_encode(cmd, box->storage->user);
+		xml_encode(cmd, box->storage->ns->user->username);
 		str_append_c(cmd, '/');
 		xml_encode(cmd, box->name);
 		str_append(cmd, "</field>");
@@ -228,7 +229,7 @@
 		cmd = t_str_new(256);
 		str_printfa(cmd, "<delete><id>%u/%u/",
 			    mail->uid, status.uidvalidity);
-		xml_encode(cmd, mail->box->storage->user);
+		xml_encode(cmd, mail->box->storage->ns->user->username);
 		str_append_c(cmd, '/');
 		xml_encode(cmd, mail->box->name);
 		str_append(cmd, "</id></delete>");
@@ -300,7 +301,7 @@
 	str_printfa(str, "&fq=uidv:%u%%20box:", status.uidvalidity);
 	solr_quote_str(str, box->name);
 	str_append(str, "%20user:");
-	solr_quote_str(str, box->storage->user);
+	solr_quote_str(str, box->storage->ns->user->username);
 
 	array_clear(maybe_uids);
 	return solr_connection_select(solr_conn, str_c(str),
diff -Nur dovecot-1.1.7+patch9/src/plugins/imap-quota/imap-quota-plugin.c dovecot-patch/src/plugins/imap-quota/imap-quota-plugin.c
--- dovecot-1.1.7+patch9/src/plugins/imap-quota/imap-quota-plugin.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/imap-quota/imap-quota-plugin.c	2009-01-06 10:08:19.000000000 -0600
@@ -49,6 +49,7 @@
 {
 	struct mail_storage *storage;
 	struct mailbox *box;
+	struct quota_settings *quota_set;			/* APPLE */
 	struct quota_root_iter *iter;
         struct quota_root *root;
 	const char *orig_mailbox, *mailbox;
@@ -71,6 +72,7 @@
 		return TRUE;
 	}
 
+	quota_set = quota_settings_find(cmd->client->user);	/* APPLE */
 	if (quota_set == NULL) {
 		mailbox_close(&box);
 		client_send_tagline(cmd, "OK No quota.");
@@ -82,7 +84,7 @@
 	str_append(str, "* QUOTAROOT ");
 	imap_quote_append_string(str, orig_mailbox, FALSE);
 
-	iter = quota_root_iter_init(quota_set, box);
+	iter = quota_root_iter_init(box);
 	while ((root = quota_root_iter_next(iter)) != NULL) {
 		str_append_c(str, ' ');
 		imap_quote_append_string(str, quota_root_get_name(root), FALSE);
@@ -91,7 +93,7 @@
 	client_send_line(cmd->client, str_c(str));
 
 	/* send QUOTA reply for each quotaroot */
-	iter = quota_root_iter_init(quota_set, box);
+	iter = quota_root_iter_init(box);
 	while ((root = quota_root_iter_next(iter)) != NULL)
 		quota_send(cmd, root);
 	quota_root_iter_deinit(&iter);
@@ -104,18 +106,20 @@
 static bool cmd_getquota(struct client_command_context *cmd)
 {
 	const char *root_name;
+	struct quota_settings *quota_set;			/* APPLE */
         struct quota_root *root;
 
 	/* <quota root> */
 	if (!client_read_string_args(cmd, 1, &root_name))
 		return FALSE;
 
+	quota_set = quota_settings_find(cmd->client->user);	/* APPLE */
 	if (quota_set == NULL) {
 		client_send_tagline(cmd, "OK No quota.");
 		return TRUE;
 	}
 
-	root = quota_root_lookup(quota_set, root_name);
+	root = quota_root_lookup(cmd->client->user, root_name);
 	if (root == NULL) {
 		client_send_tagline(cmd, "NO Quota root doesn't exist.");
 		return TRUE;
@@ -132,6 +136,7 @@
         const struct imap_arg *args, *arg;
 	const char *root_name, *name, *error;
 	uint64_t value;
+	struct quota_settings *quota_set;			/* APPLE */
 
 	/* <quota root> <resource limits> */
 	if (!client_read_args(cmd, 2, 0, &args))
@@ -143,12 +148,13 @@
 		return TRUE;
 	}
 
+	quota_set = quota_settings_find(cmd->client->user);	/* APPLE */
 	if (quota_set == NULL) {
 		client_send_tagline(cmd, "OK No quota.");
 		return TRUE;
 	}
 
-	root = quota_root_lookup(quota_set, root_name);
+	root = quota_root_lookup(cmd->client->user, root_name);
 	if (root == NULL) {
 		client_send_tagline(cmd, "NO Quota root doesn't exist.");
 		return TRUE;
diff -Nur dovecot-1.1.7+patch9/src/plugins/lazy-expunge/lazy-expunge-plugin.c dovecot-patch/src/plugins/lazy-expunge/lazy-expunge-plugin.c
--- dovecot-1.1.7+patch9/src/plugins/lazy-expunge/lazy-expunge-plugin.c	2008-11-23 16:03:43.000000000 -0600
+++ dovecot-patch/src/plugins/lazy-expunge/lazy-expunge-plugin.c	2009-01-06 10:08:19.000000000 -0600
@@ -19,6 +19,8 @@
 	MODULE_CONTEXT(obj, lazy_expunge_mail_storage_module)
 #define LAZY_EXPUNGE_LIST_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, lazy_expunge_mailbox_list_module)
+#define LAZY_EXPUNGE_USER_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, lazy_expunge_mail_user_module)
 
 enum lazy_namespace {
 	LAZY_NAMESPACE_EXPUNGE,
@@ -28,6 +30,12 @@
 	LAZY_NAMESPACE_COUNT
 };
 
+struct lazy_expunge_mail_user {
+	union mail_user_module_context module_ctx;
+
+	struct mail_namespace *lazy_ns[LAZY_NAMESPACE_COUNT];
+};
+
 struct lazy_expunge_mailbox_list {
 	union mailbox_list_module_context module_ctx;
 
@@ -57,6 +65,7 @@
 	(struct mail_storage *storage);
 static void (*lazy_expunge_next_hook_mailbox_list_created)
 	(struct mailbox_list *list);
+static void (*lazy_expunge_next_hook_mail_user_created)(struct mail_user *user);
 
 static MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_storage_module,
 				  &mail_storage_module_register);
@@ -64,8 +73,8 @@
 				  &mail_module_register);
 static MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mailbox_list_module,
 				  &mailbox_list_module_register);
-
-static struct mail_namespace *lazy_namespaces[LAZY_NAMESPACE_COUNT];
+static MODULE_CONTEXT_DEFINE_INIT(lazy_expunge_mail_user_module,
+				  &mail_user_module_register);
 
 static struct mailbox *
 mailbox_open_or_create(struct mail_storage *storage, const char *name)
@@ -95,12 +104,14 @@
 
 static void lazy_expunge_mail_expunge(struct mail *_mail)
 {
+	struct lazy_expunge_mail_user *luser =
+		LAZY_EXPUNGE_USER_CONTEXT(_mail->box->storage->ns->user);
 	struct lazy_expunge_transaction *lt =
 		LAZY_EXPUNGE_CONTEXT(_mail->transaction);
 	struct mail_storage *deststorage;
 
 	if (lt->expunge_box == NULL) {
-		deststorage = lazy_namespaces[LAZY_NAMESPACE_EXPUNGE]->storage;
+		deststorage = luser->lazy_ns[LAZY_NAMESPACE_EXPUNGE]->storage;
 		lt->expunge_box = mailbox_open_or_create(deststorage,
 							 _mail->box->name);
 		if (lt->expunge_box == NULL) {
@@ -414,6 +425,8 @@
 static int
 lazy_expunge_mailbox_list_delete(struct mailbox_list *list, const char *name)
 {
+	struct lazy_expunge_mail_user *luser =
+		LAZY_EXPUNGE_USER_CONTEXT(list->ns->user);
 	struct lazy_expunge_mailbox_list *llist =
 		LAZY_EXPUNGE_LIST_CONTEXT(list);
 	struct lazy_expunge_mail_storage *lstorage;
@@ -455,7 +468,7 @@
 	destname = t_strconcat(name, "-", timestamp, NULL);
 
 	/* first move the actual mailbox */
-	dest_list = lazy_namespaces[LAZY_NAMESPACE_DELETE]->storage->list;
+	dest_list = luser->lazy_ns[LAZY_NAMESPACE_DELETE]->storage->list;
 	if ((ret = mailbox_move(list, name, dest_list, &destname)) < 0)
 		return -1;
 	if (ret == 0) {
@@ -465,9 +478,9 @@
 	}
 
 	/* next move the expunged messages mailbox, if it exists */
-	list = lazy_namespaces[LAZY_NAMESPACE_EXPUNGE]->storage->list;
+	list = luser->lazy_ns[LAZY_NAMESPACE_EXPUNGE]->storage->list;
 	dest_list =
-		lazy_namespaces[LAZY_NAMESPACE_DELETE_EXPUNGE]->storage->list;
+		luser->lazy_ns[LAZY_NAMESPACE_DELETE_EXPUNGE]->storage->list;
 	(void)mailbox_move(list, name, dest_list, &destname);
 	return 0;
 }
@@ -526,6 +539,8 @@
 static void
 lazy_expunge_hook_mail_namespaces_created(struct mail_namespace *namespaces)
 {
+	struct lazy_expunge_mail_user *luser =
+		LAZY_EXPUNGE_USER_CONTEXT(namespaces->user);
 	struct lazy_expunge_mail_storage *lstorage;
 	const char *const *p;
 	int i;
@@ -537,18 +552,18 @@
 		if (name == NULL)
 			i_fatal("lazy_expunge: Missing namespace #%d", i + 1);
 
-		lazy_namespaces[i] =
+		luser->lazy_ns[i] =
 			mail_namespace_find_prefix(namespaces, name);
-		if (lazy_namespaces[i] == NULL)
+		if (luser->lazy_ns[i] == NULL)
 			i_fatal("lazy_expunge: Unknown namespace: '%s'", name);
-		if (strcmp(lazy_namespaces[i]->storage->name, "maildir") != 0) {
+		if (strcmp(luser->lazy_ns[i]->storage->name, "maildir") != 0) {
 			i_fatal("lazy_expunge: Namespace must be in maildir "
 				"format: %s", name);
 		}
 
 		/* we don't want to override these namespaces' expunge/delete
 		   operations. */
-		lstorage = LAZY_EXPUNGE_CONTEXT(lazy_namespaces[i]->storage);
+		lstorage = LAZY_EXPUNGE_CONTEXT(luser->lazy_ns[i]->storage);
 		lstorage->internal_namespace = TRUE;
 	}
 
@@ -556,6 +571,19 @@
 		lazy_expunge_next_hook_mail_namespaces_created(namespaces);
 }
 
+static void lazy_expunge_mail_user_created(struct mail_user *user)
+{
+	struct lazy_expunge_mail_user *luser;
+
+	luser = p_new(user->pool, struct lazy_expunge_mail_user, 1);
+	luser->module_ctx.super = user->v;
+
+	MODULE_CONTEXT_SET(user, lazy_expunge_mail_user_module, luser);
+
+	if (lazy_expunge_next_hook_mail_user_created != NULL)
+		lazy_expunge_next_hook_mail_user_created(user);
+}
+
 void lazy_expunge_plugin_init(void)
 {
 	if (getenv("LAZY_EXPUNGE") == NULL) {
@@ -576,6 +604,9 @@
 
 	lazy_expunge_next_hook_mailbox_list_created = hook_mailbox_list_created;
 	hook_mailbox_list_created = lazy_expunge_mailbox_list_created;
+
+	lazy_expunge_next_hook_mail_user_created = hook_mail_user_created;
+	hook_mail_user_created = lazy_expunge_mail_user_created;
 }
 
 void lazy_expunge_plugin_deinit(void)
@@ -587,4 +618,5 @@
 		lazy_expunge_hook_mail_namespaces_created;
 	hook_mail_storage_created = lazy_expunge_next_hook_mail_storage_created;
 	hook_mailbox_list_created = lazy_expunge_next_hook_mailbox_list_created;
+	hook_mail_user_created = lazy_expunge_next_hook_mail_user_created;
 }
diff -Nur dovecot-1.1.7+patch9/src/plugins/mbox-snarf/mbox-snarf-plugin.c dovecot-patch/src/plugins/mbox-snarf/mbox-snarf-plugin.c
--- dovecot-1.1.7+patch9/src/plugins/mbox-snarf/mbox-snarf-plugin.c	2008-11-15 11:21:09.000000000 -0600
+++ dovecot-patch/src/plugins/mbox-snarf/mbox-snarf-plugin.c	2009-01-06 10:08:19.000000000 -0600
@@ -167,10 +167,11 @@
 static void mbox_snarf_mail_storage_created(struct mail_storage *storage)
 {
 	struct mbox_snarf_mail_storage *mstorage;
+	const char *path;
 
+	path = mail_user_home_expand(storage->ns->user, getenv("MBOX_SNARF"));
 	mstorage = p_new(storage->pool, struct mbox_snarf_mail_storage, 1);
-	mstorage->snarf_inbox_path =
-		p_strdup(storage->pool, home_expand(getenv("MBOX_SNARF")));
+	mstorage->snarf_inbox_path = p_strdup(storage->pool, path);
 	mstorage->module_ctx.super = storage->v;
 	storage->v.mailbox_open = mbox_snarf_mailbox_open;
 
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota-count.c dovecot-patch/src/plugins/quota/quota-count.c
--- dovecot-1.1.7+patch9/src/plugins/quota/quota-count.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/quota/quota-count.c	2009-01-06 10:08:19.000000000 -0600
@@ -19,7 +19,7 @@
 	uoff_t size;
 	int ret = 0;
 
-	rule = quota_root_rule_find(root, name);
+	rule = quota_root_rule_find(root->set, name);
 	if (rule != NULL && rule->ignore) {
 		/* mailbox not included in quota */
 		return 0;
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota-dict.c dovecot-patch/src/plugins/quota/quota-dict.c
--- dovecot-1.1.7+patch9/src/plugins/quota/quota-dict.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/quota/quota-dict.c	2009-01-06 10:08:19.000000000 -0600
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "str.h"
 #include "dict.h"
+#include "mail-user.h"
 #include "quota-private.h"
 
 #include <stdlib.h>
@@ -48,9 +49,9 @@
 	}
 
 	if (*username == '\0')
-		username = getenv("USER");
+		username = _root->quota->user->username;
 
-	if (getenv("DEBUG") != NULL) {
+	if (_root->quota->set->debug) {
 		i_info("dict quota: user=%s, uri=%s, enforcing=%d",
 		       username, args, _root->no_enforcing);
 	}
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota-fs.c dovecot-patch/src/plugins/quota/quota-fs.c
--- dovecot-1.1.7+patch9/src/plugins/quota/quota-fs.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/quota/quota-fs.c	2009-01-06 10:08:19.000000000 -0600
@@ -251,7 +251,7 @@
 	dir = mail_storage_get_mailbox_path(storage, "", &is_file);
 	mount = fs_quota_mountpoint_get(dir);
 	if (mount != NULL) {
-		if (getenv("DEBUG") != NULL) {
+		if (quota->set->debug) {
 			i_info("fs quota add storage dir = %s", dir);
 			i_info("fs quota block device = %s", mount->device_path);
 			i_info("fs quota mount point = %s", mount->mount_path);
@@ -310,7 +310,7 @@
 	host = t_strdup_until(mount->device_path, path);
 	path++;
 
-	if (getenv("DEBUG") != NULL) {
+	if (root->root.quota->set->debug) {
 		i_info("quota-fs: host=%s, path=%s, uid=%s",
 			host, path, dec2str(root->uid));
 	}
@@ -366,7 +366,7 @@
 				*limit_r = rq->rq_fsoftlimit;
 			}
 		}
-		if (getenv("DEBUG") != NULL) {
+		if (root->root.quota->set->debug) {
 			i_info("quota-fs: uid=%s, value=%llu, "
 			       "limit=%llu, active=%d", dec2str(root->uid),
 			       (unsigned long long)*value_r,
@@ -375,7 +375,7 @@
 		return 1;
 	}
 	case Q_NOQUOTA:
-		if (getenv("DEBUG") != NULL) {
+		if (root->root.quota->set->debug) {
 			i_info("quota-fs: uid=%s, limit=unlimited",
 			       dec2str(root->uid));
 		}
@@ -632,9 +632,9 @@
 
 	/* update limit */
 	if (bytes)
-		_root->default_rule.bytes_limit = limit;
+		_root->bytes_limit = limit;
 	else
-		_root->default_rule.count_limit = limit;
+		_root->count_limit = limit;
 	return 1;
 }
 
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota-maildir.c dovecot-patch/src/plugins/quota/quota-maildir.c
--- dovecot-1.1.7+patch9/src/plugins/quota/quota-maildir.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/quota/quota-maildir.c	2009-01-06 10:08:19.000000000 -0600
@@ -150,7 +150,7 @@
 			if (ctx->info == NULL)
 				return NULL;
 
-			rule = quota_root_rule_find(&ctx->root->root,
+			rule = quota_root_rule_find(ctx->root->root.set,
 						    ctx->info->name);
 			if (rule != NULL && rule->ignore) {
 				/* mailbox not included in quota */
@@ -218,7 +218,7 @@
 
 static int maildirsize_write(struct maildir_quota_root *root, const char *path)
 {
-	const struct quota_rule *rule = &root->root.default_rule;
+	struct quota_root *_root = &root->root;
 	struct dotlock *dotlock;
 	const char *p, *dir;
 	string_t *str;
@@ -253,15 +253,15 @@
 
 	str = t_str_new(128);
 	/* if we have no limits, write 0S instead of an empty line */
-	if (rule->bytes_limit != 0 || rule->count_limit == 0) {
+	if (_root->bytes_limit != 0 || _root->count_limit == 0) {
 		str_printfa(str, "%lluS",
-			    (unsigned long long)rule->bytes_limit);
+			    (unsigned long long)_root->bytes_limit);
 	}
-	if (rule->count_limit != 0) {
+	if (_root->count_limit != 0) {
 		if (str_len(str) > 0)
 			str_append_c(str, ',');
 		str_printfa(str, "%lluC",
-			    (unsigned long long)rule->count_limit);
+			    (unsigned long long)_root->count_limit);
 	}
 	str_printfa(str, "\n%llu %llu\n",
 		    (unsigned long long)root->total_bytes,
@@ -315,7 +315,7 @@
 
 static void maildirsize_rebuild_later(struct maildir_quota_root *root)
 {
-	if (!root->root.force_default_rule) {
+	if (!root->root.set->force_default_rule) {
 		/* FIXME: can't unlink(), because the limits would be lost. */
 		return;
 	}
@@ -406,7 +406,7 @@
 static int maildirsize_parse(struct maildir_quota_root *root,
 			     int fd, const char *const *lines)
 {
-	struct quota_rule *rule = &root->root.default_rule;
+	struct quota_root *_root = &root->root;
 	uint64_t message_bytes_limit, message_count_limit;
 	long long bytes_diff, total_bytes;
 	int count_diff, total_count;
@@ -425,18 +425,18 @@
 	if (message_count_limit >= (1ULL << 63))
 		message_count_limit = (1ULL << 63) - 1;
 
-	if (rule->bytes_limit == (int64_t)message_bytes_limit &&
-	    rule->count_limit == (int64_t)message_count_limit) {
+	if (root->root.bytes_limit == (int64_t)message_bytes_limit &&
+	    root->root.count_limit == (int64_t)message_count_limit) {
 		/* limits haven't changed */
-	} else if (root->root.force_default_rule) {
+	} else if (root->root.set->force_default_rule) {
 		/* we know the limits and they've changed.
 		   the file must be rewritten. */
 		return 0;
 	} else {
 		/* we're using limits from the file. */
-		rule->bytes_limit = message_bytes_limit;
-		rule->count_limit = message_count_limit;
-		quota_root_recalculate_relative_rules(&root->root);
+		root->root.bytes_limit = message_bytes_limit;
+		root->root.count_limit = message_count_limit;
+		quota_root_recalculate_relative_rules(root->root.set);
 	}
 
 	if (*lines == NULL) {
@@ -459,8 +459,8 @@
 		return -1;
 	}
 
-	if ((total_bytes > rule->bytes_limit && rule->bytes_limit != 0) ||
-	    (total_count > rule->count_limit && rule->count_limit != 0)) {
+	if ((total_bytes > _root->bytes_limit && _root->bytes_limit != 0) ||
+	    (total_count > _root->count_limit && _root->count_limit != 0)) {
 		/* we're over quota. don't trust these values if the file
 		   contains more than the initial summary line, or if the file
 		   is older than 15 minutes. */
@@ -606,10 +606,10 @@
 		ret = maildirsize_read(root);
 	} T_END;
 	if (ret == 0) {
-		if (root->root.default_rule.bytes_limit == 0 &&
-		    root->root.default_rule.count_limit == 0) {
+		if (root->root.set->default_rule.bytes_limit == 0 &&
+		    root->root.set->default_rule.count_limit == 0) {
 			/* no quota */
-			if (!root->root.force_default_rule)
+			if (!root->root.set->force_default_rule)
 				return 0;
 			/* explicitly specified 0 as quota. keep the quota
 			   updated even if it's not enforced. */
@@ -667,7 +667,7 @@
 }
 
 static bool
-maildir_quota_parse_rule(struct quota_root *root ATTR_UNUSED,
+maildir_quota_parse_rule(struct quota_root_settings *root_set ATTR_UNUSED,
 			 struct quota_rule *rule,
 			 const char *str, const char **error_r)
 {
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota-plugin.c dovecot-patch/src/plugins/quota/quota-plugin.c
--- dovecot-1.1.7+patch9/src/plugins/quota/quota-plugin.c	2008-11-15 11:21:11.000000000 -0600
+++ dovecot-patch/src/plugins/quota/quota-plugin.c	2009-01-06 10:08:19.000000000 -0600
@@ -5,20 +5,23 @@
 #include "mailbox-list-private.h"
 #include "quota.h"
 #include "quota-plugin.h"
+#include "quota-private.h"				/* APPLE */
+#include "hash.h"					/* APPLE */
 
 #include <stdlib.h>
 
 /* defined by imap, pop3, lda */
 extern void (*hook_mail_storage_created)(struct mail_storage *storage);
 
+void (*quota_next_hook_mail_user_created)(struct mail_user *user);
 void (*quota_next_hook_mail_storage_created)(struct mail_storage *storage);
 void (*quota_next_hook_mailbox_list_created)(struct mailbox_list *list);
 
 const char *quota_plugin_version = PACKAGE_VERSION;
-struct quota *quota_set;
+struct hash_table *quota_settings;			/* APPLE */
 
 static void quota_root_add_rules(const char *root_name, 
-				 struct quota_root *root)
+				 struct quota_root_settings *root_set)
 {
 	const char *rule_name, *rule, *error;
 	unsigned int i;
@@ -30,7 +33,7 @@
 		if (rule == NULL)
 			break;
 
-		if (quota_root_add_rule(root, rule, &error) < 0) {
+		if (quota_root_add_rule(root_set, rule, &error) < 0) {
 			i_fatal("Quota root %s: Invalid rule %s: %s",
 				root_name, rule, error);
 		}
@@ -39,7 +42,7 @@
 }
 
 static void quota_root_add_warning_rules(const char *root_name,
-					 struct quota_root *root)
+					 struct quota_root_settings *root_set)
 {
 	const char *rule_name, *rule, *error;
 	unsigned int i;
@@ -51,7 +54,7 @@
 		if (rule == NULL)
 			break;
 
-		if (quota_root_add_warning_rule(root, rule, &error) < 0) {
+		if (quota_root_add_warning_rule(root_set, rule, &error) < 0) {
 			i_fatal("Quota root %s: Invalid warning rule: %s",
 				root_name, rule);
 		}
@@ -61,8 +64,6 @@
 
 void quota_plugin_init(void)
 {
-	struct quota_root *root;
-	unsigned int i;
 	const char *env;
 
 	env = getenv("QUOTA");
@@ -72,13 +73,41 @@
 		return;
 	}
 
-	quota_set = quota_init();
+	/* APPLE */
+	quota_settings = hash_table_create(default_pool, default_pool, 5, str_hash,
+					   (hash_cmp_callback_t *) strcmp);
+	if (quota_settings_create() == NULL)
+		i_fatal("Couldn't create quota settings");
 
-	root = quota_root_init(quota_set, env);
-	if (root == NULL)
-		i_fatal("Couldn't create quota root: %s", env);
-	quota_root_add_rules("QUOTA", root);
-	quota_root_add_warning_rules("QUOTA", root);
+	quota_next_hook_mail_user_created = hook_mail_user_created;
+	hook_mail_user_created = quota_mail_user_created;
+
+	quota_next_hook_mail_storage_created = hook_mail_storage_created;
+	hook_mail_storage_created = quota_mail_storage_created;
+
+	quota_next_hook_mailbox_list_created = hook_mailbox_list_created;
+	hook_mailbox_list_created = quota_mailbox_list_created;
+}
+
+/* APPLE */
+struct quota_settings *quota_settings_create()
+{
+	struct quota_settings *quota_set;
+	struct quota_root_settings *root_set;
+	unsigned int i;
+	const char *env;
+
+	quota_set = quota_settings_init();
+
+	env = getenv("QUOTA");
+	root_set = quota_root_settings_init(quota_set, env);
+	if (root_set == NULL) {
+		i_error("Couldn't create quota root: %s", env);
+		quota_settings_deinit(&quota_set);
+		return NULL;
+	}
+	quota_root_add_rules("QUOTA", root_set);
+	quota_root_add_warning_rules("QUOTA", root_set);
 
 	for (i = 2;; i++) {
 		const char *root_name;
@@ -89,27 +118,55 @@
 		if (env == NULL)
 			break;
 
-		root = quota_root_init(quota_set, env);
-		if (root == NULL)
-			i_fatal("Couldn't create quota root: %s", env);
-		quota_root_add_rules(root_name, root);
-		quota_root_add_warning_rules(root_name, root);
+		root_set = quota_root_settings_init(quota_set, env);
+		if (root_set == NULL) {
+			i_error("Couldn't create quota root: %s", env);
+			quota_settings_deinit(&quota_set);
+			return NULL;
+		}
+		quota_root_add_rules(root_name, root_set);
+		quota_root_add_warning_rules(root_name, root_set);
 	}
 
-	quota_next_hook_mail_storage_created = hook_mail_storage_created;
-	hook_mail_storage_created = quota_mail_storage_created;
+	env = getenv("USER");
+	i_assert(env != NULL && *env != '\0');
+	hash_insert(quota_settings, p_strdup(quota_set->pool, env), quota_set);
 
-	quota_next_hook_mailbox_list_created = hook_mailbox_list_created;
-	hook_mailbox_list_created = quota_mailbox_list_created;
+	return quota_set;
+}
+
+/* APPLE */
+struct quota_settings *quota_settings_find(const struct mail_user *user)
+{
+	return hash_lookup(quota_settings, user->username);
+}
+
+/* APPLE */
+void quota_settings_ref(struct quota_settings *quota_set)
+{
+	++quota_set->refcount;
+}
+
+/* APPLE */
+void quota_settings_unref(struct quota_settings **_quota_set)
+{
+	struct quota_settings *quota_set = *_quota_set;
+
+	*_quota_set = NULL;
+	if (--quota_set->refcount > 0)
+		return;
+
+	hash_remove(quota_settings, quota_set->username);
 }
 
 void quota_plugin_deinit(void)
 {
-	if (quota_set != NULL) {
+	if (quota_settings != NULL) {				/* APPLE */
+		hook_mail_user_created = quota_next_hook_mail_user_created;
 		hook_mail_storage_created =
 			quota_next_hook_mail_storage_created;
 		hook_mailbox_list_created =
 			quota_next_hook_mailbox_list_created;
-		quota_deinit(&quota_set);
+		hash_table_destroy(&quota_settings);			/* APPLE */
 	}
 }
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota-plugin.h dovecot-patch/src/plugins/quota/quota-plugin.h
--- dovecot-1.1.7+patch9/src/plugins/quota/quota-plugin.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/quota/quota-plugin.h	2009-01-06 10:08:19.000000000 -0600
@@ -3,14 +3,18 @@
 
 struct mail_storage;
 
+extern void (*quota_next_hook_mail_user_created)(struct mail_user *user);
 extern void (*quota_next_hook_mail_storage_created)
 	(struct mail_storage *storage);
 extern void (*quota_next_hook_mailbox_list_created)(struct mailbox_list *list);
 
-/* "quota" symbol already exists in OSX, so we'll use this slightly uglier
-   name. */
-extern struct quota *quota_set;
+/* APPLE */
+struct quota_settings *quota_settings_create();
+struct quota_settings *quota_settings_find(const struct mail_user *user);
+void quota_settings_ref(struct quota_settings *quota_set);
+void quota_settings_unref(struct quota_settings **_quota_set);
 
+void quota_mail_user_created(struct mail_user *user);
 void quota_mail_storage_created(struct mail_storage *storage);
 void quota_mailbox_list_created(struct mailbox_list *list);
 
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota-private.h dovecot-patch/src/plugins/quota/quota-private.h
--- dovecot-1.1.7+patch9/src/plugins/quota/quota-private.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/quota/quota-private.h	2009-01-06 10:08:19.000000000 -0600
@@ -9,11 +9,20 @@
 extern unsigned int quota_module_id;
 
 struct quota {
+	struct mail_user *user;
+	struct quota_settings *set;
+
 	ARRAY_DEFINE(roots, struct quota_root *);
 	ARRAY_DEFINE(storages, struct mail_storage *);
+};
+
+struct quota_settings {
+	pool_t pool;
+	const char *username;					/* APPLE */
+	int refcount;						/* APPLE */
 
-	int (*test_alloc)(struct quota_transaction_context *ctx,
-			  uoff_t size, bool *too_large_r);
+	ARRAY_DEFINE(root_sets, struct quota_root_settings *);
+	/* APPLE moved test_alloc to global quota_settings_test_alloc */
 
 	const char *quota_exceeded_msg;
 	unsigned int debug:1;
@@ -41,7 +50,8 @@
 	int (*init)(struct quota_root *root, const char *args);
 	void (*deinit)(struct quota_root *root);
 
-	bool (*parse_rule)(struct quota_root *root, struct quota_rule *rule,
+	bool (*parse_rule)(struct quota_root_settings *root_set,
+			   struct quota_rule *rule,
 			   const char *str, const char **error_r);
 
 	/* called once for each backend */
@@ -49,8 +59,8 @@
 			      struct mail_storage *storage);
 
 	const char *const *(*get_resources)(struct quota_root *root);
-	int (*get_resource)(struct quota_root *root, const char *name,
-			    uint64_t *value_r);
+	int (*get_resource)(struct quota_root *root,
+			    const char *name, uint64_t *value_r);
 
 	int (*update)(struct quota_root *root, 
 		      struct quota_transaction_context *ctx);
@@ -62,27 +72,39 @@
 	struct quota_backend_vfuncs v;
 };
 
-struct quota_root {
-	pool_t pool;
-
+struct quota_root_settings {
 	/* Unique quota root name. */
 	const char *name;
 
-	/* pointer to the quota that owns this root */
-	struct quota *quota;
+	struct quota_settings *set;
+	const char *args;
 
-	struct quota_backend backend;
+	const struct quota_backend *backend;
 	struct quota_rule default_rule;
 	ARRAY_DEFINE(rules, struct quota_rule);
 	ARRAY_DEFINE(warning_rules, struct quota_warning_rule);
 
+	/* Limits in default_rule override backend's quota limits */
+	unsigned int force_default_rule:1;
+};
+
+struct quota_root {
+	pool_t pool;
+
+	struct quota_root_settings *set;
+	struct quota *quota;
+	struct quota_backend backend;
+
+	/* initially the same as set->default_rule.*_limit, but some backends
+	   may change these by reading the limits elsewhere (e.g. Maildir++,
+	   FS quota) */
+	int64_t bytes_limit, count_limit;
+
 	/* Module-specific contexts. See quota_module_id. */
 	ARRAY_DEFINE(quota_module_contexts, void);
 
 	/* don't enforce quota when saving */
 	unsigned int no_enforcing:1;
-	/* Limits in default_rule override backend's quota limits */
-	unsigned int force_default_rule:1;
 };
 
 struct quota_transaction_context {
@@ -103,13 +125,18 @@
 
 /* Register storage to all user's quota roots. */
 void quota_add_user_storage(struct quota *quota, struct mail_storage *storage);
-void quota_remove_user_storage(struct quota *quota, 
-			       struct mail_storage *storage);
+void quota_remove_user_storage(struct mail_storage *storage);
+
+struct quota *quota_get_mail_user_quota(struct mail_user *user);
 
 struct quota_rule *
-quota_root_rule_find(struct quota_root *root, const char *name);
+quota_root_rule_find(struct quota_root_settings *root_set, const char *name);
 
-void quota_root_recalculate_relative_rules(struct quota_root *root);
+void quota_root_recalculate_relative_rules(struct quota_root_settings *root_set);
 int quota_count(struct quota_root *root, uint64_t *bytes_r, uint64_t *count_r);
 
+/* APPLE was test_alloc in quota_settings */
+extern int (*quota_settings_test_alloc)(struct quota_transaction_context *ctx,
+					uoff_t size, bool *too_large_r);
+
 #endif
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota-storage.c dovecot-patch/src/plugins/quota/quota-storage.c
--- dovecot-1.1.7+patch9/src/plugins/quota/quota-storage.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/plugins/quota/quota-storage.c	2009-01-06 10:08:19.000000000 -0600
@@ -17,6 +17,14 @@
 	MODULE_CONTEXT(obj, quota_mail_module)
 #define QUOTA_LIST_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, quota_mailbox_list_module)
+#define QUOTA_USER_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, quota_user_module)
+
+struct quota_user {
+	union mail_user_module_context module_ctx;
+
+	struct quota *quota;
+};
 
 struct quota_mailbox_list {
 	union mailbox_list_module_context module_ctx;
@@ -41,6 +49,8 @@
 static MODULE_CONTEXT_DEFINE_INIT(quota_mail_module, &mail_module_register);
 static MODULE_CONTEXT_DEFINE_INIT(quota_mailbox_list_module,
 				  &mailbox_list_module_register);
+static MODULE_CONTEXT_DEFINE_INIT(quota_user_module,
+				  &mail_user_module_register);
 
 static void quota_mail_expunge(struct mail *_mail)
 {
@@ -74,7 +84,7 @@
 	struct quota_transaction_context *qt;
 
 	t = qbox->module_ctx.super.transaction_begin(box, flags);
-	qt = quota_transaction_begin(quota_set, box);
+	qt = quota_transaction_begin(box);
 
 	MODULE_CONTEXT_SET(t, quota_storage_module, qt);
 	return t;
@@ -150,7 +160,7 @@
 		return 0;
 	else if (ret == 0) {
 		mail_storage_set_error(t->box->storage, MAIL_ERROR_NOSPACE,
-				       qt->quota->quota_exceeded_msg);
+				       qt->quota->set->quota_exceeded_msg);
 		return -1;
 	} else {
 		mail_storage_set_critical(t->box->storage,
@@ -215,7 +225,7 @@
 		if (ret == 0) {
 			mail_storage_set_error(t->box->storage,
 				MAIL_ERROR_NOSPACE,
-				qt->quota->quota_exceeded_msg);
+				qt->quota->set->quota_exceeded_msg);
 			return -1;
 		} else if (ret < 0) {
 			mail_storage_set_critical(t->box->storage,
@@ -310,7 +320,7 @@
 	}
 
 	if (qbox->expunge_qt == NULL)
-		qbox->expunge_qt = quota_transaction_begin(quota_set, box);
+		qbox->expunge_qt = quota_transaction_begin(box);
 
 	if (i != count) {
 		/* we already know the size */
@@ -469,16 +479,56 @@
 {
 	union mail_storage_module_context *qstorage = QUOTA_CONTEXT(storage);
 
-	quota_remove_user_storage(quota_set, storage);
+	quota_remove_user_storage(storage);
 
 	if (qstorage->super.destroy != NULL)
 		qstorage->super.destroy(storage);
 }
 
+struct quota *quota_get_mail_user_quota(struct mail_user *user)
+{
+	struct quota_user *quser = QUOTA_USER_CONTEXT(user);
+
+	return quser->quota;
+}
+
+static void quota_user_deinit(struct mail_user *user)
+{
+	struct quota_user *quser = QUOTA_USER_CONTEXT(user);
+
+	quota_deinit(&quser->quota);
+	quser->module_ctx.super.deinit(user);
+}
+
+void quota_mail_user_created(struct mail_user *user)
+{
+	struct quota_settings *quota_set;			/* APPLE */
+	struct quota_user *quser;
+
+	/* APPLE */
+	quota_set = quota_settings_find(user);
+	if (quota_set == NULL) {
+		quota_set = quota_settings_create();
+		if (quota_set == NULL)
+			return;
+	}
+
+	quser = p_new(user->pool, struct quota_user, 1);
+	quser->module_ctx.super = user->v;
+	user->v.deinit = quota_user_deinit;
+	quser->quota = quota_init(quota_set, user);
+
+	MODULE_CONTEXT_SET(user, quota_user_module, quser);
+
+	if (quota_next_hook_mail_user_created != NULL)
+		quota_next_hook_mail_user_created(user);
+}
+
 void quota_mail_storage_created(struct mail_storage *storage)
 {
 	struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(storage->list);
 	union mail_storage_module_context *qstorage;
+	struct quota *quota;
 
 	qlist->storage = storage;
 
@@ -492,7 +542,8 @@
 	if (storage->ns->type == NAMESPACE_PRIVATE &&
 	    (storage->ns->flags & NAMESPACE_FLAG_INTERNAL) == 0) {
 		/* register to user's quota roots */
-		quota_add_user_storage(quota_set, storage);
+		quota = quota_get_mail_user_quota(storage->ns->user);
+		quota_add_user_storage(quota, storage);
 	}
 
 	if (quota_next_hook_mail_storage_created != NULL)
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota.c dovecot-patch/src/plugins/quota/quota.c
--- dovecot-1.1.7+patch9/src/plugins/quota/quota.c	2008-10-29 11:40:46.000000000 -0500
+++ dovecot-patch/src/plugins/quota/quota.c	2009-01-06 10:08:19.000000000 -0600
@@ -42,38 +42,35 @@
 
 static int quota_default_test_alloc(struct quota_transaction_context *ctx,
 				    uoff_t size, bool *too_large_r);
+/* APPLE */
+int (*quota_settings_test_alloc)(struct quota_transaction_context *ctx,
+				 uoff_t size, bool *too_large_r) =
+	quota_default_test_alloc;
 
-struct quota *quota_init(void)
+struct quota_settings *quota_settings_init(void)
 {
-	struct quota *quota;
-
-	quota = i_new(struct quota, 1);
-	quota->test_alloc = quota_default_test_alloc;
-	quota->debug = getenv("DEBUG") != NULL;
-	quota->quota_exceeded_msg = getenv("QUOTA_EXCEEDED_MESSAGE");
-	if (quota->quota_exceeded_msg == NULL)
-		quota->quota_exceeded_msg = DEFAULT_QUOTA_EXCEEDED_MSG;
-	i_array_init(&quota->roots, 4);
-	i_array_init(&quota->storages, 8);
+	struct quota_settings *quota_set;
+	pool_t pool;
 
-	return quota;
+	pool = pool_alloconly_create("quota settings", 256);
+	quota_set = p_new(pool, struct quota_settings, 1);
+	quota_set->pool = pool;
+	quota_set->username = p_strdup(pool, getenv("USER"));	/* APPLE */
+	quota_set->debug = getenv("DEBUG") != NULL;
+	quota_set->quota_exceeded_msg = getenv("QUOTA_EXCEEDED_MESSAGE");
+	if (quota_set->quota_exceeded_msg == NULL)
+		quota_set->quota_exceeded_msg = DEFAULT_QUOTA_EXCEEDED_MSG;
+	p_array_init(&quota_set->root_sets, pool, 4);
+	return quota_set;
 }
 
-void quota_deinit(struct quota **_quota)
+void quota_settings_deinit(struct quota_settings **_quota_set)
 {
-	struct quota *quota = *_quota;
-	struct quota_root **root_p, *root;
+	struct quota_settings *quota_set = *_quota_set;
 
-	*_quota = NULL;
-	while (array_count(&quota->roots) > 0) {
-		root_p = array_idx_modifiable(&quota->roots, 0);
-		root = *root_p;
-		quota_root_deinit(&root);
-	}
+	*_quota_set = NULL;
 
-	array_free(&quota->roots);
-	array_free(&quota->storages);
-	i_free(quota);
+	pool_unref(&quota_set->pool);
 }
 
 static const struct quota_backend *quota_backend_find(const char *name)
@@ -88,11 +85,12 @@
 	return NULL;
 }
 
-struct quota_root *quota_root_init(struct quota *quota, const char *root_def)
+struct quota_root_settings *
+quota_root_settings_init(struct quota_settings *quota_set, const char *root_def)
 {
-	struct quota_root *root;
+	struct quota_root_settings *root_set;
 	const struct quota_backend *backend;
-	const char *p, *args, *backend_name, *const *tmp;
+	const char *p, *args, *backend_name;
 
 	/* <backend>[:<quota root name>[:<backend args>]] */
 	p = strchr(root_def, ':');
@@ -108,44 +106,59 @@
 	if (backend == NULL)
 		i_fatal("Unknown quota backend: %s", backend_name);
 	
-	root = backend->v.alloc();
-	root->quota = quota;
-	root->backend = *backend;
-	root->pool = pool_alloconly_create("quota root", 512);
+	root_set = p_new(quota_set->pool, struct quota_root_settings, 1);
+	root_set->set = quota_set;
+	root_set->backend = backend;
 
 	if (args != NULL) {
 		/* save root's name */
 		p = strchr(args, ':');
 		if (p == NULL) {
-			root->name = p_strdup(root->pool, args);
+			root_set->name = p_strdup(quota_set->pool, args);
 			args = NULL;
 		} else {
-			root->name = p_strdup_until(root->pool, args, p);
+			root_set->name =
+				p_strdup_until(quota_set->pool, args, p);
 			args = p + 1;
 		}
 	} else {
-		root->name = "";
+		root_set->name = "";
 	}
+	root_set->args = p_strdup(quota_set->pool, args);
 
-	if (quota->debug) {
+	if (quota_set->debug) {
 		i_info("Quota root: name=%s backend=%s args=%s",
-		       root->name, backend_name, args == NULL ? "" : args);
+		       root_set->name, backend_name, args == NULL ? "" : args);
 	}
 
-	i_array_init(&root->rules, 4);
-	i_array_init(&root->warning_rules, 4);
-	array_create(&root->quota_module_contexts, default_pool,
-		     sizeof(void *), 5);
+	p_array_init(&root_set->rules, quota_set->pool, 4);
+	p_array_init(&root_set->warning_rules, quota_set->pool, 4);
+	array_append(&quota_set->root_sets, &root_set, 1);
+	return root_set;
+}
 
-	array_append(&quota->roots, &root, 1);
+static struct quota_root *
+quota_root_init(struct quota_root_settings *root_set, struct quota *quota)
+{
+	struct quota_root *root;
+	const char *const *tmp;
 
-	if (backend->v.init != NULL) {
-		if (backend->v.init(root, args) < 0) {
-			quota_root_deinit(&root);
-			return NULL;
-		}
-	} else if (args != NULL) {
-		tmp = t_strsplit_spaces(args, " ");
+	root = root_set->backend->v.alloc();
+	root->pool = pool_alloconly_create("quota root", 512);
+	root->set = root_set;
+	root->quota = quota;
+	root->backend = *root_set->backend;
+	root->bytes_limit = root_set->default_rule.bytes_limit;
+	root->count_limit = root_set->default_rule.count_limit;
+
+	array_create(&root->quota_module_contexts, root->pool,
+		     sizeof(void *), 10);
+
+	if (root->backend.v.init != NULL) {
+		if (root->backend.v.init(root, root_set->args) < 0)
+			i_fatal("Quota root %s init() failed", root_set->name);
+	} else if (root_set->args != NULL) {
+		tmp = t_strsplit_spaces(root_set->args, " ");
 		for (; *tmp != NULL; tmp++) {
 			if (strcmp(*tmp, "noenforcing") == 0)
 				root->no_enforcing = TRUE;
@@ -155,49 +168,70 @@
 		if (*tmp != NULL) {
 			i_fatal("Quota root %s backend %s: "
 				"Unknown parameter: %s",
-				root->name, backend_name, *tmp);
+				root_set->name, root->backend.name, *tmp);
 		}
 	}
 	return root;
 }
 
-void quota_root_deinit(struct quota_root **_root)
+static void quota_root_deinit(struct quota_root *root)
 {
-	struct quota_root *root = *_root;
 	pool_t pool = root->pool;
-	struct quota_root *const *roots;
-	struct quota_warning_rule *warnings;
+
+	root->backend.v.deinit(root);
+	pool_unref(&pool);
+}
+
+struct quota *quota_init(struct quota_settings *quota_set,
+			 struct mail_user *user)
+{
+	struct quota *quota;
+	struct quota_root *root;
+	struct quota_root_settings *const *root_sets;
 	unsigned int i, count;
 
-	*_root = NULL;
+	quota_settings_ref(quota_set);				/* APPLE */
 
-	roots = array_get(&root->quota->roots, &count);
+	quota = i_new(struct quota, 1);
+	quota->user = user;
+	quota->set = quota_set;
+	i_array_init(&quota->roots, 8);
+
+	root_sets = array_get(&quota_set->root_sets, &count);
+	i_array_init(&quota->storages, count);
 	for (i = 0; i < count; i++) {
-		if (roots[i] == root) {
-			array_delete(&root->quota->roots, i, 1);
-			break;
-		}
+		root = quota_root_init(root_sets[i], quota);
+		array_append(&quota->roots, &root, 1);
 	}
+	return quota;
+}
 
-	warnings = array_get_modifiable(&root->warning_rules, &count);
+void quota_deinit(struct quota **_quota)
+{
+	struct quota *quota = *_quota;
+	struct quota_root *const *roots;
+	unsigned int i, count;
+
+	*_quota = NULL;
+
+	roots = array_get(&quota->roots, &count);
 	for (i = 0; i < count; i++)
-		i_free(warnings[i].command);
-	array_free(&root->warning_rules);
+		quota_root_deinit(roots[i]);
+	array_free(&quota->roots);
+	array_free(&quota->storages);
 
-	array_free(&root->rules);
-	array_free(&root->quota_module_contexts);
+	quota_settings_unref(&quota->set);			/* APPLE */
 
-	root->backend.v.deinit(root);
-	pool_unref(&pool);
+	i_free(quota);
 }
 
 struct quota_rule *
-quota_root_rule_find(struct quota_root *root, const char *name)
+quota_root_rule_find(struct quota_root_settings *root_set, const char *name)
 {
 	struct quota_rule *rules;
 	unsigned int i, count;
 
-	rules = array_get_modifiable(&root->rules, &count);
+	rules = array_get_modifiable(&root_set->rules, &count);
 	for (i = 0; i < count; i++) {
 		if (strcmp(rules[i].mailbox_name, name) == 0)
 			return &rules[i];
@@ -206,18 +240,19 @@
 }
 
 static int
-quota_rule_parse_percentage(struct quota_root *root, struct quota_rule *rule,
+quota_rule_parse_percentage(struct quota_root_settings *root_set,
+			    struct quota_rule *rule,
 			    int64_t *limit, const char **error_r)
 {
 	int64_t percentage = *limit;
 
 	if (percentage <= 0 || percentage >= -1U) {
-		*error_r = p_strdup_printf(root->pool,
+		*error_r = p_strdup_printf(root_set->set->pool,
 			"Invalid rule percentage: %lld", (long long)percentage);
 		return -1;
 	}
 
-	if (rule == &root->default_rule) {
+	if (rule == &root_set->default_rule) {
 		*error_r = "Default rule can't be a percentage";
 		return -1;
 	}
@@ -245,29 +280,29 @@
 	}
 }
 
-void quota_root_recalculate_relative_rules(struct quota_root *root)
+void quota_root_recalculate_relative_rules(struct quota_root_settings *root_set)
 {
 	struct quota_rule *rules;
 	struct quota_warning_rule *warning_rules;
 	unsigned int i, count;
 
-	rules = array_get_modifiable(&root->rules, &count);
+	rules = array_get_modifiable(&root_set->rules, &count);
 	for (i = 0; i < count; i++) {
 		quota_rule_recalculate_relative_rules(&rules[i],
-						      &root->default_rule);
+						      &root_set->default_rule);
 	}
 
-	warning_rules = array_get_modifiable(&root->warning_rules, &count);
+	warning_rules = array_get_modifiable(&root_set->warning_rules, &count);
 	for (i = 0; i < count; i++) {
 		quota_rule_recalculate_relative_rules(&warning_rules[i].rule,
-						      &root->default_rule);
+						      &root_set->default_rule);
 	}
 }
 
 static int
-quota_rule_parse_limits(struct quota_root *root, struct quota_rule *rule,
-			const char *limits, bool allow_negative,
-			const char **error_r)
+quota_rule_parse_limits(struct quota_root_settings *root_set,
+			struct quota_rule *rule, const char *limits,
+			bool allow_negative, const char **error_r)
 {
 	const char **args;
 	char *p;
@@ -289,7 +324,7 @@
 			limit = &rule->count_limit;
 			*limit = strtoll(*args + 9, &p, 10);
 		} else {
-			*error_r = p_strdup_printf(root->pool,
+			*error_r = p_strdup_printf(root_set->set->pool,
 					"Unknown rule limit name: %s", *args);
 			return -1;
 		}
@@ -315,12 +350,12 @@
 			break;
 		case '%':
 			multiply = 0;
-			if (quota_rule_parse_percentage(root, rule, limit,
+			if (quota_rule_parse_percentage(root_set, rule, limit,
 							error_r) < 0)
 				return -1;
 			break;
 		default:
-			*error_r = p_strdup_printf(root->pool,
+			*error_r = p_strdup_printf(root_set->set->pool,
 					"Invalid rule limit value: %s", *args);
 			return -1;
 		}
@@ -339,8 +374,8 @@
 	return 0;
 }
 
-int quota_root_add_rule(struct quota_root *root, const char *rule_def,
-			const char **error_r)
+int quota_root_add_rule(struct quota_root_settings *root_set,
+			const char *rule_def, const char **error_r)
 {
 	struct quota_rule *rule;
 	const char *p, *mailbox_name;
@@ -355,43 +390,45 @@
 	/* <mailbox name>:<quota limits> */
 	mailbox_name = t_strdup_until(rule_def, p++);
 
-	rule = quota_root_rule_find(root, mailbox_name);
+	rule = quota_root_rule_find(root_set, mailbox_name);
 	if (rule == NULL) {
 		if (strcmp(mailbox_name, RULE_NAME_DEFAULT_NONFORCE) == 0)
-			rule = &root->default_rule;
+			rule = &root_set->default_rule;
 		else if (strcmp(mailbox_name, RULE_NAME_DEFAULT_FORCE) == 0) {
-			rule = &root->default_rule;
-			root->force_default_rule = TRUE;
+			rule = &root_set->default_rule;
+			root_set->force_default_rule = TRUE;
 		} else {
-			rule = array_append_space(&root->rules);
-			rule->mailbox_name = p_strdup(root->pool, mailbox_name);
+			rule = array_append_space(&root_set->rules);
+			rule->mailbox_name =
+				p_strdup(root_set->set->pool, mailbox_name);
 		}
 	}
 
 	if (strcmp(p, "ignore") == 0) {
 		rule->ignore = TRUE;
-		if (root->quota->debug) {
+		if (root_set->set->debug) {
 			i_info("Quota rule: root=%s mailbox=%s ignored",
-			       root->name, mailbox_name);
+			       root_set->name, mailbox_name);
 		}
 		return 0;
 	}
 
 	if (strncmp(p, "backend=", 8) == 0) {
-		if (!root->backend.v.parse_rule(root, rule, p + 8, error_r))
+		if (!root_set->backend->v.parse_rule(root_set, rule,
+						     p + 8, error_r))
 			ret = -1;
 	} else {
-		bool allow_negative = rule != &root->default_rule;
+		bool allow_negative = rule != &root_set->default_rule;
 
-		if (quota_rule_parse_limits(root, rule, p,
+		if (quota_rule_parse_limits(root_set, rule, p,
 					    allow_negative, error_r) < 0)
 			ret = -1;
 	}
 
-	quota_root_recalculate_relative_rules(root);
-	if (root->quota->debug) {
+	quota_root_recalculate_relative_rules(root_set);
+	if (root_set->set->debug) {
 		i_info("Quota rule: root=%s mailbox=%s "
-		       "bytes=%lld (%u%%) messages=%lld (%u%%)", root->name,
+		       "bytes=%lld (%u%%) messages=%lld (%u%%)", root_set->name,
 		       mailbox_name,
 		       (long long)rule->bytes_limit, rule->bytes_percent,
 		       (long long)rule->count_limit, rule->count_percent);
@@ -408,14 +445,14 @@
 	int64_t bytes_limit, count_limit;
 	bool found;
 
-	bytes_limit = root->default_rule.bytes_limit;
-	count_limit = root->default_rule.count_limit;
+	bytes_limit = root->bytes_limit;
+	count_limit = root->count_limit;
 
 	/* if default rule limits are 0, this rule applies only to specific
 	   mailboxes */
 	found = bytes_limit != 0 || count_limit != 0;
 
-	rule = quota_root_rule_find(root, mailbox_name);
+	rule = quota_root_rule_find(root->set, mailbox_name);
 	if (rule != NULL) {
 		if (!rule->ignore) {
 			bytes_limit += rule->bytes_limit;
@@ -490,12 +527,18 @@
 	}
 }
 
-void quota_remove_user_storage(struct quota *quota,
-			       struct mail_storage *storage)
+void quota_remove_user_storage(struct mail_storage *storage)
 {
+	struct quota *quota;
 	struct mail_storage *const *storages;
 	unsigned int i, count;
-	
+
+	quota = quota_get_mail_user_quota(storage->ns->user);
+	if (quota == NULL) {
+		/* no quota for this storage */
+		return;
+	}
+
 	storages = array_get(&quota->storages, &count);
 	for (i = 0; i < count; i++) {
 		if (storages[i] == storage) {
@@ -505,8 +548,8 @@
 	}
 }
 
-int quota_root_add_warning_rule(struct quota_root *root, const char *rule_def,
-				const char **error_r)
+int quota_root_add_warning_rule(struct quota_root_settings *root_set,
+				const char *rule_def, const char **error_r)
 {
 	struct quota_warning_rule *warning;
 	struct quota_rule rule;
@@ -520,17 +563,18 @@
 	}
 
 	memset(&rule, 0, sizeof(rule));
-	ret = quota_rule_parse_limits(root, &rule, t_strdup_until(rule_def, p),
+	ret = quota_rule_parse_limits(root_set, &rule,
+				      t_strdup_until(rule_def, p),
 				      TRUE, error_r);
 	if (ret < 0)
 		return -1;
 
-	warning = array_append_space(&root->warning_rules);
+	warning = array_append_space(&root_set->warning_rules);
 	warning->command = i_strdup(p+1);
 	warning->rule = rule;
 
-	quota_root_recalculate_relative_rules(root);
-	if (root->quota->debug) {
+	quota_root_recalculate_relative_rules(root_set);
+	if (root_set->set->debug) {
 		i_info("Quota warning: bytes=%llu (%u%%) "
 		       "messages=%llu (%u%%) command=%s",
 		       (unsigned long long)warning->rule.bytes_limit,
@@ -542,12 +586,13 @@
 }
 
 struct quota_root_iter *
-quota_root_iter_init(struct quota *quota, struct mailbox *box)
+quota_root_iter_init(struct mailbox *box)
 {
+	struct mail_user *user = box->storage->ns->user;
 	struct quota_root_iter *iter;
 
 	iter = i_new(struct quota_root_iter, 1);
-	iter->quota = quota;
+	iter->quota = quota_get_mail_user_quota(user);
 	iter->box = box;
 	return iter;
 }
@@ -590,14 +635,16 @@
 	i_free(iter);
 }
 
-struct quota_root *quota_root_lookup(struct quota *quota, const char *name)
+struct quota_root *quota_root_lookup(struct mail_user *user, const char *name)
 {
+	struct quota *quota;
 	struct quota_root *const *roots;
 	unsigned int i, count;
 
+	quota = quota_get_mail_user_quota(user);
 	roots = array_get(&quota->roots, &count);
 	for (i = 0; i < count; i++) {
-		if (strcmp(roots[i]->name, name) == 0)
+		if (strcmp(roots[i]->set->name, name) == 0)
 			return roots[i];
 	}
 	return NULL;
@@ -605,7 +652,7 @@
 
 const char *quota_root_get_name(struct quota_root *root)
 {
-	return root->name;
+	return root->set->name;
 }
 
 const char *const *quota_root_get_resources(struct quota_root *root)
@@ -658,13 +705,13 @@
 	return -1;
 }
 
-struct quota_transaction_context *quota_transaction_begin(struct quota *quota,
-							  struct mailbox *box)
+struct quota_transaction_context *quota_transaction_begin(struct mailbox *box)
 {
+	struct mail_user *user = box->storage->ns->user;
 	struct quota_transaction_context *ctx;
 
 	ctx = i_new(struct quota_transaction_context, 1);
-	ctx->quota = quota;
+	ctx->quota = quota_get_mail_user_quota(user);
 	ctx->box = box;
 	ctx->bytes_left = (uint64_t)-1;
 	ctx->count_left = (uint64_t)-1;
@@ -718,14 +765,10 @@
 	return 0;
 }
 
-static void quota_warning_execute(struct quota_root *root, const char *cmd)
+static void quota_warning_execute(const char *cmd)
 {
-	int ret;
-
-	if (root->quota->debug)
-		i_info("quota: Executing warning: %s", cmd);
+	int ret = system(cmd);
 
-	ret = system(cmd);
 	if (ret < 0) {
 		i_error("system(%s) failed: %m", cmd);
 	} else if (WIFSIGNALED(ret)) {
@@ -744,7 +787,7 @@
 	uint64_t bytes_current, bytes_before, bytes_limit;
 	uint64_t count_current, count_before, count_limit;
 
-	warnings = array_get_modifiable(&root->warning_rules, &count);
+	warnings = array_get_modifiable(&root->set->warning_rules, &count);
 	if (count == 0)
 		return;
 
@@ -762,7 +805,7 @@
 		     bytes_current >= (uint64_t)warnings[i].rule.bytes_limit) ||
 		    (count_before < (uint64_t)warnings[i].rule.count_limit &&
 		     count_current >= (uint64_t)warnings[i].rule.count_limit)) {
-			quota_warning_execute(root, warnings[i].command);
+			quota_warning_execute(warnings[i].command);
 			break;
 		}
 	}
@@ -786,7 +829,8 @@
 		mailbox_name = mailbox_get_name(ctx->box);
 		roots = array_get(&ctx->quota->roots, &count);
 		for (i = 0; i < count; i++) {
-			rule = quota_root_rule_find(roots[i], mailbox_name);
+			rule = quota_root_rule_find(roots[i]->set,
+						    mailbox_name);
 			if (rule != NULL && rule->ignore) {
 				/* mailbox not included in quota */
 				continue;
@@ -841,7 +885,7 @@
 		if (quota_transaction_set_limits(ctx) < 0)
 			return -1;
 	}
-	return ctx->quota->test_alloc(ctx, size, too_large_r);
+	return quota_settings_test_alloc(ctx, size, too_large_r);	/* APPLE */
 }
 
 static int quota_default_test_alloc(struct quota_transaction_context *ctx,
diff -Nur dovecot-1.1.7+patch9/src/plugins/quota/quota.h dovecot-patch/src/plugins/quota/quota.h
--- dovecot-1.1.7+patch9/src/plugins/quota/quota.h	2008-11-18 10:12:38.000000000 -0600
+++ dovecot-patch/src/plugins/quota/quota.h	2009-01-06 10:08:19.000000000 -0600
@@ -3,6 +3,7 @@
 
 struct mail;
 struct mailbox;
+struct mail_user;
 
 /* Message storage size kilobytes. */
 #define QUOTA_NAME_STORAGE_KILOBYTES "STORAGE"
@@ -16,29 +17,35 @@
 struct quota_root_iter;
 struct quota_transaction_context;
 
-struct quota *quota_init(void);
-void quota_deinit(struct quota **quota);
+struct quota_settings *quota_settings_init(void);
+void quota_settings_deinit(struct quota_settings **quota_set);
 
-/* Create a new quota root. */
-struct quota_root *quota_root_init(struct quota *quota, const char *root_def);
-void quota_root_deinit(struct quota_root **root);
+/* Set up a new quota root. */
+struct quota_root_settings *
+quota_root_settings_init(struct quota_settings *quota_set,
+			 const char *root_def);
+void quota_root_settings_deinit(struct quota_root_settings **root_set);
 
 /* Add a new rule too the quota root. Returns 0 if ok, -1 if rule is invalid. */
-int quota_root_add_rule(struct quota_root *root, const char *rule_def,
-			const char **error_r);
+int quota_root_add_rule(struct quota_root_settings *root_set,
+			const char *rule_def, const char **error_r);
 /* Add a new warning rule for the quota root. Returns 0 if ok, -1 if rule is
    invalid. */
-int quota_root_add_warning_rule(struct quota_root *root, const char *rule_def,
-				const char **error_r);
+int quota_root_add_warning_rule(struct quota_root_settings *root_set,
+				const char *rule_def, const char **error_r);
+
+/* Initialize quota for the given user. */
+struct quota *quota_init(struct quota_settings *quota_set,
+			 struct mail_user *user);
+void quota_deinit(struct quota **quota);
 
 /* List all quota roots. Returned quota roots are freed by quota_deinit(). */
-struct quota_root_iter *
-quota_root_iter_init(struct quota *quota, struct mailbox *box);
+struct quota_root_iter *quota_root_iter_init(struct mailbox *box);
 struct quota_root *quota_root_iter_next(struct quota_root_iter *iter);
 void quota_root_iter_deinit(struct quota_root_iter **iter);
 
 /* Return quota root or NULL. */
-struct quota_root *quota_root_lookup(struct quota *quota, const char *name);
+struct quota_root *quota_root_lookup(struct mail_user *user, const char *name);
 
 /* Returns name of the quota root. */
 const char *quota_root_get_name(struct quota_root *root);
@@ -53,8 +60,7 @@
 		       uint64_t value, const char **error_r);
 
 /* Start a new quota transaction. */
-struct quota_transaction_context *quota_transaction_begin(struct quota *quota, 
-							  struct mailbox *box);
+struct quota_transaction_context *quota_transaction_begin(struct mailbox *box);
 /* Commit quota transaction. Returns 0 if ok, -1 if failed. */
 int quota_transaction_commit(struct quota_transaction_context **ctx);
 /* Rollback quota transaction changes. */
diff -Nur dovecot-1.1.7+patch9/src/plugins/trash/trash-plugin.c dovecot-patch/src/plugins/trash/trash-plugin.c
--- dovecot-1.1.7+patch9/src/plugins/trash/trash-plugin.c	2008-11-23 16:04:30.000000000 -0600
+++ dovecot-patch/src/plugins/trash/trash-plugin.c	2009-01-06 10:08:19.000000000 -0600
@@ -3,7 +3,6 @@
 #include "lib.h"
 #include "array.h"
 #include "istream.h"
-#include "home-expand.h"
 #include "mail-namespace.h"
 #include "mail-search.h"
 #include "quota-private.h"
@@ -17,6 +16,9 @@
 #define INIT_TRASH_MAILBOX_COUNT 4
 #define MAX_RETRY_COUNT 3
 
+#define TRASH_USER_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, trash_user_module)
+
 struct trash_mailbox {
 	const char *name;
 	int priority; /* lower number = higher priority */
@@ -33,17 +35,22 @@
 	unsigned int mail_set:1;
 };
 
+struct trash_user {
+	union mail_user_module_context module_ctx;
+
+	/* ordered by priority, highest first */
+	ARRAY_DEFINE(trash_boxes, struct trash_mailbox);
+};
+
 const char *trash_plugin_version = PACKAGE_VERSION;
 
+static MODULE_CONTEXT_DEFINE_INIT(trash_user_module,
+				  &mail_user_module_register);
 static void (*trash_next_hook_mail_namespaces_created)
 	(struct mail_namespace *namespaces);
 static int (*trash_next_quota_test_alloc)(struct quota_transaction_context *,
 					  uoff_t, bool *);
 
-static pool_t config_pool;
-/* trash_boxes ordered by priority, highest first */
-static ARRAY_DEFINE(trash_boxes, struct trash_mailbox);
-
 static int trash_clean_mailbox_open(struct trash_mailbox *trash)
 {
 	trash->box = mailbox_open(trash->storage, trash->name, NULL,
@@ -90,13 +97,14 @@
 static int trash_try_clean_mails(struct quota_transaction_context *ctx,
 				 uint64_t size_needed)
 {
+	struct trash_user *tuser = TRASH_USER_CONTEXT(ctx->quota->user);
 	struct trash_mailbox *trashes;
 	unsigned int i, j, count, oldest_idx;
 	time_t oldest, received = 0;
 	uint64_t size, size_expunged = 0, expunged_count = 0;
 	int ret = 0;
 
-	trashes = array_get_modifiable(&trash_boxes, &count);
+	trashes = array_get_modifiable(&tuser->trash_boxes, &count);
 	for (i = 0; i < count; ) {
 		/* expunge oldest mails first in all trash boxes with
 		   same priority */
@@ -209,19 +217,18 @@
 	return 0;
 }
 
-static bool trash_find_storage(struct trash_mailbox *trash)
+static bool trash_find_storage(struct mail_user *user,
+			       struct trash_mailbox *trash)
 {
-	struct mail_storage *const *storages;
-	unsigned int i, count;
+	struct mail_namespace *ns;
 	const char *name;
 
-	storages = array_get(&quota_set->storages, &count);
-	for (i = 0; i < count; i++) {
+	for (ns = user->namespaces; ns != NULL; ns = ns->next) {
 		name = trash->name;
-		if (mail_namespace_update_name(storages[i]->ns, &name)) {
+		if (mail_namespace_update_name(ns, &name)) {
 			if (name != trash->name)
-				trash->name = p_strdup(config_pool, name);
-			trash->storage = storages[i];
+				trash->name = p_strdup(user->pool, name);
+			trash->storage = ns->storage;
 			return TRUE;
 		}
 	}
@@ -235,8 +242,9 @@
 	return t1->priority - t2->priority;
 }
 
-static int read_configuration(const char *path)
+static int read_configuration(struct mail_user *user, const char *path)
 {
+	struct trash_user *tuser = TRASH_USER_CONTEXT(user);
 	struct istream *input;
 	const char *line, *name;
 	struct trash_mailbox *trash;
@@ -249,8 +257,7 @@
 		return -1;
 	}
 
-	p_clear(config_pool);
-	p_array_init(&trash_boxes, config_pool, INIT_TRASH_MAILBOX_COUNT);
+	p_array_init(&tuser->trash_boxes, user->pool, INIT_TRASH_MAILBOX_COUNT);
 
 	input = i_stream_create_fd(fd, (size_t)-1, FALSE);
 	i_stream_set_return_partial_line(input, TRUE);
@@ -260,12 +267,12 @@
 		if (name == NULL || name[1] == '\0' || *line == '#')
 			continue;
 
-		trash = array_append_space(&trash_boxes);
-		trash->name = p_strdup(config_pool, name+1);
+		trash = array_append_space(&tuser->trash_boxes);
+		trash->name = p_strdup(user->pool, name+1);
 		trash->priority = atoi(t_strdup_until(line, name));
 		trash->search_arg.type = SEARCH_ALL;
 
-		if (!trash_find_storage(trash)) {
+		if (!trash_find_storage(user, trash)) {
 			i_error("trash: Namespace not found for mailbox '%s'",
 				trash->name);
 			ret = -1;
@@ -279,7 +286,7 @@
 	i_stream_destroy(&input);
 	(void)close(fd);
 
-	trash = array_get_modifiable(&trash_boxes, &count);
+	trash = array_get_modifiable(&tuser->trash_boxes, &count);
 	qsort(trash, count, sizeof(*trash), trash_mailbox_priority_cmp);
 	return ret;
 }
@@ -287,23 +294,26 @@
 static void
 trash_hook_mail_namespaces_created(struct mail_namespace *namespaces)
 {
+	struct mail_user *user = namespaces->user;
+	struct trash_user *tuser;
 	const char *env;
+	struct quota_settings *quota_set;			/* APPLE */
 
 	env = getenv("TRASH");
 	if (env == NULL) {
 		if (getenv("DEBUG") != NULL)
 			i_info("trash: No trash setting - plugin disabled");
-	} else if (quota_set == NULL) {
+	} else if ((quota_set = quota_settings_find(user)) == NULL) { /* APPLE */
 		i_error("trash plugin: quota plugin not initialized");
 	} else {
-		config_pool = pool_alloconly_create("trash config",
-					sizeof(trash_boxes) +
-					BUFFER_APPROX_SIZE +
-					INIT_TRASH_MAILBOX_COUNT *
-					(sizeof(struct trash_mailbox) + 32));
-		if (read_configuration(env) == 0) {
-			trash_next_quota_test_alloc = quota_set->test_alloc;
-			quota_set->test_alloc = trash_quota_test_alloc;
+		tuser = p_new(user->pool, struct trash_user, 1);
+		tuser->module_ctx.super = user->v;
+		MODULE_CONTEXT_SET(user, trash_user_module, tuser);
+
+		if (read_configuration(user, env) == 0) {
+			/* APPLE */
+			trash_next_quota_test_alloc = quota_settings_test_alloc;
+			quota_settings_test_alloc = trash_quota_test_alloc;
 		}
 	}
 
@@ -320,8 +330,6 @@
 void trash_plugin_deinit(void)
 {
 	hook_mail_namespaces_created = trash_hook_mail_namespaces_created;
-	quota_set->test_alloc = trash_next_quota_test_alloc;
-
-	if (config_pool != NULL)
-		pool_unref(&config_pool);
+	/* APPLE */
+	quota_settings_test_alloc = trash_next_quota_test_alloc;
 }
diff -Nur dovecot-1.1.7+patch9/src/pop3/Makefile.am dovecot-patch/src/pop3/Makefile.am
--- dovecot-1.1.7+patch9/src/pop3/Makefile.am	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/pop3/Makefile.am	2009-01-06 10:08:19.000000000 -0600
@@ -7,6 +7,7 @@
 	-I$(top_srcdir)/src/lib-dict \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-storage \
+	-I$(top_srcdir)/src/mail-common \
 	-DMODULEDIR=\""$(moduledir)"\"
 
 pop3_LDFLAGS = -export-dynamic
@@ -21,6 +22,7 @@
 	../lib-storage/list/libstorage_list.a \
 	$(STORAGE_LIBS) \
 	../lib-imap/libimap.a \
+	../mail-common/libmail-common.a \
 	../lib-mail/libmail.a \
 	../lib-dict/libdict.a \
 	../lib-charset/libcharset.a \
diff -Nur dovecot-1.1.7+patch9/src/pop3/client.c dovecot-patch/src/pop3/client.c
--- dovecot-1.1.7+patch9/src/pop3/client.c	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/pop3/client.c	2009-01-06 10:08:19.000000000 -0600
@@ -12,6 +12,8 @@
 #include "commands.h"
 #include "mail-search.h"
 #include "mail-namespace.h"
+#include "master.h"		/* APPLE */
+#include "ioloop.h"		/* APPLE */
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -29,7 +31,8 @@
 /* Disconnect client after idling this many milliseconds */
 #define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
 
-static struct client *my_client; /* we don't need more than one currently */
+static struct client *clients;		/* APPLE was my_client, now list */
+static bool process_per_connection = TRUE;	/* APPLE */
 
 static void client_input(struct client *client);
 static int client_output(struct client *client);
@@ -136,8 +139,10 @@
 	return FALSE;
 }
 
+static /* APPLE */
 struct client *client_create(int fd_in, int fd_out,
-			     struct mail_namespace *namespaces)
+			     struct mail_user *user,
+			     unsigned int connection_id)	/* APPLE */
 {
 	struct mail_storage *storage;
 	const char *inbox;
@@ -148,7 +153,8 @@
 
 	/* always use nonblocking I/O */
 	net_set_nonblock(fd_in, TRUE);
-	net_set_nonblock(fd_out, TRUE);
+	if (fd_out != fd_in)			/* APPLE */
+		net_set_nonblock(fd_out, TRUE);
 
 	client = i_new(struct client, 1);
 	client->fd_in = fd_in;
@@ -162,10 +168,15 @@
 	client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
 				      client_idle_timeout, client);
 
-	client->namespaces = namespaces;
+	client->user = user;
+
+	/* APPLE */
+	client->connection_id = connection_id;
+	client->next = clients;
+	clients = client;
 
 	inbox = "INBOX";
-	client->inbox_ns = mail_namespace_find(namespaces, &inbox);
+	client->inbox_ns = mail_namespace_find(user->namespaces, &inbox);
 	if (client->inbox_ns == NULL) {
 		client_send_line(client, "-ERR No INBOX namespace for user.");
 		client_destroy(client, "No INBOX namespace for user.");
@@ -196,14 +207,49 @@
 		return NULL;
 	}
 
-	i_assert(my_client == NULL);
-	my_client = client;
-
 	if (hook_client_created != NULL)
 		hook_client_created(&client);
 	return client;
 }
 
+/* APPLE */
+bool client_attach(int fd_in, int fd_out, bool is_standalone ATTR_UNUSED)
+{
+	const char *username, *home;
+	struct mail_user *user;
+	unsigned int connection_id;
+
+	username = getenv("USER");
+	if (username == NULL || *username == '\0') {
+		i_error("USER environment missing from client request");
+		return FALSE;
+	}
+
+	home = getenv("HOME");
+
+	user = mail_user_init(username, home);
+	if (mail_namespaces_init(user) < 0) {
+		i_error("Namespace initialization failed for user %s",
+			username);
+		mail_user_deinit(&user);
+		return FALSE;
+	}
+
+	if (process_per_connection)
+		connection_id = 0;
+	else {
+		const char *env = getenv("CONNECTION_ID");
+		if (env == NULL || *env == '\0') {
+			i_error("CONNECTION_ID environment missing from client request");
+			mail_user_deinit(&user);
+			return FALSE;
+		}
+		connection_id = strtoul(env, NULL, 10);
+	}
+
+	return client_create(fd_in, fd_out, user, connection_id) != NULL;
+}
+
 static const char *client_stats(struct client *client)
 {
 	static struct var_expand_table static_tab[] = {
@@ -250,10 +296,18 @@
 
 void client_destroy(struct client *client, const char *reason)
 {
+	struct client **pos;			/* APPLE */
+	unsigned int connection_id;		/* APPLE */
+
 	if (!client->disconnected) {
+		const char *prefix;			/* APPLE */
+
 		if (reason == NULL)
 			reason = client_get_disconnect_reason(client);
-		i_info("%s %s", reason, client_stats(client));
+		/* APPLE */
+		prefix = process_per_connection ? "" :
+			t_strconcat("User ", client->user->username, ": ", NULL);
+		i_info("%s%s %s", prefix, reason, client_stats(client));
 	}
 
 	if (client->cmd != NULL) {
@@ -271,7 +325,7 @@
 	}
 	if (client->mailbox != NULL)
 		mailbox_close(&client->mailbox);
-	mail_namespaces_deinit(&client->namespaces);
+	mail_user_deinit(&client->user);
 
 	i_free(client->message_sizes);
 	i_free(client->deleted_bitmask);
@@ -290,21 +344,45 @@
 			i_error("close(client out) failed: %m");
 	}
 
+	/* APPLE */
+	for (pos = &clients; *pos != NULL; pos = &(*pos)->next) {
+		if (*pos == client) {
+			*pos = client->next;
+			break;
+		}
+	}
+	io_env_clean();
+	connection_id = client->connection_id;
+
 	i_free(client);
 
-	/* quit the program */
-	my_client = NULL;
-	io_loop_stop(ioloop);
+	if (process_per_connection) {			/* APPLE */
+		/* quit the program */
+		i_assert(clients == NULL);
+		io_loop_stop(ioloop);
+	} else {
+		/* (APPLE) run until master says stop, or can't say stop
+		   and there are no more clients.  otherwise there's a
+		   race if there's an incoming request from the master. */
+		if (!master_send_disconnect(connection_id) &&
+		    clients == NULL)
+			io_loop_stop(ioloop);
+	}
 }
 
 void client_disconnect(struct client *client, const char *reason)
 {
+	const char *prefix;			/* APPLE */
+
 	if (client->disconnected)
 		return;
 
-	client->disconnected = TRUE;
-	i_info("Disconnected: %s %s", reason, client_stats(client));
+	/* APPLE */
+	prefix = process_per_connection ? "" :
+		t_strconcat("User ", client->user->username, ": ", NULL);
+	i_info("%sDisconnected: %s %s", prefix, reason, client_stats(client));
 
+	client->disconnected = TRUE;
 	(void)o_stream_flush(client->output);
 
 	i_stream_close(client->input);
@@ -464,15 +542,51 @@
 	return client->cmd == NULL;
 }
 
-void clients_init(void)
+void clients_init(bool persistent_mail_process)		/* APPLE */
 {
-	my_client = NULL;
+	clients = NULL;
+	process_per_connection = !persistent_mail_process;	/* APPLE */
 }
 
 void clients_deinit(void)
 {
-	if (my_client != NULL) {
-		client_send_line(my_client, "-ERR Server shutting down.");
-		client_destroy(my_client, "Server shutting down");
+	struct client *next;
+
+	/* APPLE */
+	while (clients != NULL) {
+		next = clients->next;
+		client_send_line(clients, "-ERR Server shutting down.");
+		client_destroy(clients, "Server shutting down");
+		clients = next;
+	}
+}
+
+/* APPLE */
+bool clients_connected(void)
+{
+	return clients != NULL;
+}
+
+/* APPLE */
+void clients_info(string_t *str)
+{
+	struct client *client;
+	int n;
+
+	n = 0;
+	for (client = clients; client != NULL; client = client->next)
+		++n;
+
+	str_printfa(str, "%d connected user%s", n, n == 1 ? "" : "s");
+
+	if (n > 0) {
+		str_append(str, " (");
+		n = 0;
+		for (client = clients; client != NULL; client = client->next) {
+			if (n++ > 0)
+				str_append(str, ", ");
+			str_append(str, client->user->username);
+		}
+		str_append_c(str, ')');
 	}
 }
diff -Nur dovecot-1.1.7+patch9/src/pop3/client.h dovecot-patch/src/pop3/client.h
--- dovecot-1.1.7+patch9/src/pop3/client.h	2008-10-26 10:03:45.000000000 -0500
+++ dovecot-patch/src/pop3/client.h	2009-01-06 10:08:19.000000000 -0600
@@ -7,6 +7,9 @@
 typedef void command_func_t(struct client *client);
 
 struct client {
+	struct client *next;			/* APPLE */
+	unsigned int connection_id;		/* APPLE */
+
 	int fd_in, fd_out;
 	struct io *io;
 	struct istream *input;
@@ -16,7 +19,8 @@
 	command_func_t *cmd;
 	void *cmd_context;
 
-	struct mail_namespace *namespaces, *inbox_ns;
+	struct mail_user *user;
+	struct mail_namespace *inbox_ns;
 	struct mailbox *mailbox;
 	struct mailbox_transaction_context *trans;
 
@@ -47,10 +51,9 @@
 	unsigned int waiting_input:1;
 };
 
-/* Create new client with specified input/output handles. socket specifies
-   if the handle is a socket. */
-struct client *client_create(int fd_in, int fd_out,
-			     struct mail_namespace *namespaces);
+/* Create new client with specified input/output handles.
+   APPLE was client_create() */
+bool client_attach(int fd_in, int fd_out, bool is_standalone);
 void client_destroy(struct client *client, const char *reason);
 
 /* Disconnect client connection */
@@ -61,7 +64,9 @@
 	ATTR_FORMAT(2, 3);
 void client_send_storage_error(struct client *client);
 
-void clients_init(void);
+void clients_init(bool persistent_mail_process);	/* APPLE */
 void clients_deinit(void);
+bool clients_connected(void);				/* APPLE */
+void clients_info(string_t *str);			/* APPLE */
 
 #endif
diff -Nur dovecot-1.1.7+patch9/src/pop3/main.c dovecot-patch/src/pop3/main.c
--- dovecot-1.1.7+patch9/src/pop3/main.c	2008-11-05 12:01:47.000000000 -0600
+++ dovecot-patch/src/pop3/main.c	2009-01-06 10:08:19.000000000 -0600
@@ -14,6 +14,9 @@
 #include "dict-client.h"
 #include "mail-storage.h"
 #include "mail-namespace.h"
+#include "master.h"		/* APPLE */
+#include "env-util.h"		/* APPLE */
+#include "str.h"		/* APPLE */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -23,6 +26,9 @@
 #define IS_STANDALONE() \
         (getenv("LOGGED_IN") == NULL)
 
+/* APPLE */
+#define IS_PERSISTENT()	(getenv("PERSISTENT_MAIL_PROCESS") != NULL)
+
 struct client_workaround_list {
 	const char *name;
 	enum client_workarounds num;
@@ -39,7 +45,6 @@
 void (*hook_client_created)(struct client **client) = NULL;
 
 static struct module *modules = NULL;
-static pool_t namespace_pool;
 static char log_prefix[128]; /* syslog() needs this to be permanent */
 static struct io *log_io = NULL;
 
@@ -60,6 +65,21 @@
 	io_loop_stop(ioloop);
 }
 
+/* APPLE */
+#ifdef SIGINFO
+static void sig_info(int signo ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+	string_t *msg;
+
+	msg = t_str_new(512);
+	clients_info(msg);
+	str_append(msg, "; ");
+	master_info(msg);
+
+	i_info("POP3 process %d: %s", getpid(), str_c(msg));
+}
+#endif
+
 static void log_error_callback(void *context ATTR_UNUSED)
 {
 	io_loop_stop(ioloop);
@@ -182,7 +202,7 @@
 
 static int main_init(void)
 {
-	struct mail_namespace *ns;
+	int fd_in, fd_out;			/* APPLE */
 
 	lib_signals_init();
         lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
@@ -208,7 +228,15 @@
         mail_storage_init();
 	mail_storage_register_all();
 	mailbox_list_register_all();
-	clients_init();
+	clients_init(IS_PERSISTENT());			/* APPLE */
+
+	/* APPLE */
+	master_init(IS_PERSISTENT() ? 0 : -1,
+	    ioloop, client_attach, clients_connected);
+#ifdef SIGINFO
+	if (IS_PERSISTENT())
+		lib_signals_set_handler(SIGINFO, TRUE, sig_info, NULL);
+#endif
 
 	module_dir_init(modules);
 
@@ -229,14 +257,21 @@
 		i_fatal("pop3_uidl_format setting doesn't contain any "
 			"%% variables.");
 
-	namespace_pool = pool_alloconly_create("namespaces", 1024);
-	if (mail_namespaces_init(namespace_pool, getenv("USER"), &ns) < 0)
-		i_fatal("Namespace initialization failed");
-	return client_create(0, 1, ns) != NULL;
+	/* APPLE */
+	if (IS_PERSISTENT())
+		fd_in = fd_out = 3;
+	else {
+		fd_in = 0;
+		fd_out = 1;
+	}
+
+	return client_attach(fd_in, fd_out, IS_STANDALONE());
 }
 
 static void main_deinit(void)
 {
+	master_deinit();		/* APPLE */
+
 	if (log_io != NULL)
 		io_remove(&log_io);
 	clients_deinit();
@@ -253,8 +288,10 @@
 int main(int argc ATTR_UNUSED, char *argv[], char *envp[])
 {
 #ifdef DEBUG
-	if (getenv("LOGGED_IN") != NULL && getenv("GDB") == NULL)
-		fd_debug_verify_leaks(3, 1024);
+	if (getenv("LOGGED_IN") != NULL && getenv("GDB") == NULL) {
+		int base = IS_PERSISTENT() ? 4 : 3;		/* APPLE */
+		fd_debug_verify_leaks(base, 1024);
+	}
 #endif
 	if (IS_STANDALONE() && getuid() == 0 &&
 	    net_getpeername(1, NULL, NULL) == 0) {
@@ -268,6 +305,10 @@
 	lib_init();
 	drop_privileges();
 
+	/* APPLE */
+	if (IS_PERSISTENT())
+		io_env_enable();
+
         process_title_init(argv, envp);
 	ioloop = io_loop_create();