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 #include 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 #include @@ -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 +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 @@ -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 #include -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 @@ /* */ 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 +#include +#include + +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, "\n\tusersArray\n\t\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\n\t\t\ttype\n\t\t\timap\n" ); + } + else if ( group->process.type == PROCESS_TYPE_POP3 ) + { + cnt = &pop3_cnt; + str_append( msg_str, "\t\t\n\t\t\ttype\n\t\t\tpop3\n" ); + } + else + { + continue; + } + + str_printfa( msg_str, "\t\t\tname\n\t\t\t%s\n", group->user ); + str_printfa( msg_str, "\t\t\tipAddress\n\t\t\t%s\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\tconnections\n\t\t\t%d\n", + process_count + connection_count); + str_printfa(msg_str, "\t\t\tconnectionElapsedTime\n\t\t\t%d\n", + (int) (ioloop_time - earliest_connected_at)); + + str_append( msg_str, "\t\t\n" ); + } + hash_iterate_deinit( &iter ); + + str_printfa( msg_str, "\t\n\timapRequests\n\t%d\n", imap_cnt ); + str_printfa( msg_str, "\tpopRequests\n\t%d\n", pop3_cnt ); + str_printfa( msg_str, "\ttotalRequests\n\t%d\n", pop3_cnt + imap_cnt ); + str_append( msg_str, "\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, ""); xml_encode(cmd, box->name); str_append(cmd, ""); - xml_encode(cmd, box->storage->user); + xml_encode(cmd, box->storage->ns->user->username); str_printfa(cmd, "%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, ""); @@ -228,7 +229,7 @@ cmd = t_str_new(256); str_printfa(cmd, "%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, ""); @@ -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; /* */ 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 */ /* */ 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 @@ -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 /* 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("a_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("a_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("a_set); + hash_table_destroy("a_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("a->roots, 4); - i_array_init("a->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("a_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("a->roots) > 0) { - root_p = array_idx_modifiable("a->roots, 0); - root = *root_p; - quota_root_deinit(&root); - } + *_quota_set = NULL; - array_free("a->roots); - array_free("a->storages); - i_free(quota); + pool_unref("a_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; /* [:[:]] */ 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("a_set->root_sets, &root_set, 1); + return root_set; +} - array_append("a->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("a->roots, 8); + + root_sets = array_get("a_set->root_sets, &count); + i_array_init("a->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("a->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("a->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("a->roots); + array_free("a->storages); - array_free(&root->rules); - array_free(&root->quota_module_contexts); + quota_settings_unref("a->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 = 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("a->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("a->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("a_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 #include @@ -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 #include @@ -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();