Mercurial > dovecot > core-2.2
changeset 12755:baa0314ed610
Merged changes from v2.0 tree.
line wrap: on
line diff
--- a/.hgsigs Sat Feb 19 16:56:21 2011 +0200 +++ b/.hgsigs Tue Feb 22 15:31:31 2011 +0200 @@ -22,3 +22,4 @@ 8a838dcf8e761690806c8df2caabf828fa8028ff 0 iEYEABECAAYFAkzAgVMACgkQyUhSUUBVislqlACgptrkApZDOZOARdi0UtefD/EWVagAn3GEcvGADNkeos2laWvQxGURptCY d0d3aca1c9587887a32a890548ec7db76c3bbbe2 0 iEYEABECAAYFAkzYU0oACgkQyUhSUUBVismeNwCaAua5XzOT/BgfeOVrBpscYz4M/jYAnRAc11iVkEeXr32o4YVL37DCPOv/ 51e41fcc78560b1eb5e3e1d026151e2cbe007486 0 iEYEABECAAYFAkz5RxcACgkQyUhSUUBViskcyQCcDetyEXnLHc1nCSFQ5LdlxgoNDE4AoKS8ZtsrUFdeT3/dfnJT1K7b5ski +440fcf8cb33815e86e5cb9701965000230338ac8 0 iEYEABECAAYFAk0u33kACgkQyUhSUUBVislxBwCeNfszo3Ivr5182ugeAXheEX33qDgAni6UFG9soLT2P5p1wpL2p/Bq4ACG
--- a/.hgtags Sat Feb 19 16:56:21 2011 +0200 +++ b/.hgtags Tue Feb 22 15:31:31 2011 +0200 @@ -59,3 +59,4 @@ 8a838dcf8e761690806c8df2caabf828fa8028ff 2.0.6 d0d3aca1c9587887a32a890548ec7db76c3bbbe2 2.0.7 51e41fcc78560b1eb5e3e1d026151e2cbe007486 2.0.8 +440fcf8cb33815e86e5cb9701965000230338ac8 2.0.9
--- a/NEWS Sat Feb 19 16:56:21 2011 +0200 +++ b/NEWS Tue Feb 22 15:31:31 2011 +0200 @@ -1,3 +1,21 @@ +v2.0.9 2011-01-13 Timo Sirainen <tss@iki.fi> + + - Linux: Fixed a high system CPU usage / high context switch count + performance problem + - Maildir: Avoid unnecessarily reading dovecot-uidlist while opening + mailbox. + - Maildir: Fixed renaming child mailboxes when namespace had a prefix. + - mdbox: Don't leave partially written messages to mdbox files when + aborting saving. + - Fixed master user logins when using userdb prefetch + - lda: Fixed a crash when trying to send "out of quota" reply + - lmtp: If delivering duplicate messages to same user's INBOX, + create different GUIDs for them. This helps to avoid duplicate + POP3 UIDLs when pop3_uidl_format=%g. + - virtual storage: Fixed saving multiple mails in a transaction + (e.g. copy multiple messages). + - dsync: Saved messages' save-date was set to 1970-01-01. + v2.0.8 2010-12-03 Timo Sirainen <tss@iki.fi> * Services' default vsz_limits weren't being enforced correctly in
--- a/configure.in Sat Feb 19 16:56:21 2011 +0200 +++ b/configure.in Tue Feb 22 15:31:31 2011 +0200 @@ -2624,7 +2624,7 @@ dnl IDLE doesn't really belong to banner. It's there just to make Blackberries dnl happy, because otherwise BIS server disables push email. capability_banner="IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE" -capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT IDLE CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS" +capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS" AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities) AC_DEFINE_UNQUOTED(CAPABILITY_BANNER_STRING, "$capability_banner", IMAP capabilities advertised in banner)
--- a/doc/example-config/conf.d/15-lda.conf Sat Feb 19 16:56:21 2011 +0200 +++ b/doc/example-config/conf.d/15-lda.conf Tue Feb 22 15:31:31 2011 +0200 @@ -17,6 +17,9 @@ # Binary to use for sending mails. #sendmail_path = /usr/sbin/sendmail +# If non-empty, send mails via this SMTP host[:port] instead of sendmail. +#submission_host = + # Subject: header to use for rejection mails. You can use the same variables # as for rejection_reason below. #rejection_subject = Rejected: %s
--- a/doc/example-config/conf.d/20-pop3.conf Sat Feb 19 16:56:21 2011 +0200 +++ b/doc/example-config/conf.d/20-pop3.conf Tue Feb 22 15:31:31 2011 +0200 @@ -69,7 +69,7 @@ # Maximum number of POP3 connections allowed for a user from each IP address. # NOTE: The username is compared case-sensitively. - #mail_max_userip_connections = 3 + #mail_max_userip_connections = 10 # Space separated list of plugins to load (default is global mail_plugins). #mail_plugins = $mail_plugins
--- a/doc/man/doveadm-quota.1.in Sat Feb 19 16:56:21 2011 +0200 +++ b/doc/man/doveadm-quota.1.in Tue Feb 22 15:31:31 2011 +0200 @@ -1,5 +1,5 @@ -.\" Copyright (c) 2010 Dovecot authors, see the included COPYING file -.TH DOVEADM\-QUOTA 1 "2010-11-25" "Dovecot v2.0" "Dovecot" +.\" Copyright (c) 2010-2011 Dovecot authors, see the included COPYING file +.TH DOVEADM\-QUOTA 1 "2011-02-17" "Dovecot v2.0" "Dovecot" .SH NAME doveadm\-quota \- Initialize/recalculate or show current quota usage .\"------------------------------------------------------------------------ @@ -71,6 +71,7 @@ The .B quota get command is used to display the current quota usage. +The storage values are reported in kilobytes. .PP This command uses by default the output formatter .BR table .
--- a/doc/man/dovecot-lda.1.in Sat Feb 19 16:56:21 2011 +0200 +++ b/doc/man/dovecot-lda.1.in Tue Feb 22 15:31:31 2011 +0200 @@ -1,5 +1,5 @@ .\" Copyright (c) 2010 Dovecot authors, see the included COPYING file -.TH DOVECOT\-LDA 1 "2010-06-22" "Dovecot v2.0" "Dovecot" +.TH DOVECOT\-LDA 1 "2011-01-16" "Dovecot v2.0" "Dovecot" .SH NAME dovecot\-lda \- Dovecot\(aqs local mail delivery agent .\"------------------------------------------------------------------------ @@ -11,6 +11,7 @@ [\fB\-d\fP \fIusername\fP] [\fB\-f\fP \fIenvelope_sender\fP] [\fB\-m\fP \fImailbox\fP] +[\fB\-o\fP \fIsetting=value\fP] [\fB\-p\fP \fIpath\fP] .\"------------------------------------------------------------------------ .SH DESCRIPTION @@ -80,6 +81,18 @@ for any reason, it\(aqs delivered to .B INBOX instead. +.\"--------------------------------- +.TP +.BI \-o\ setting = value +Overrides the configuration +.I setting +from +.I @pkgsysconfdir@/dovecot.conf +and from the userdb with the given +.IR value . +In order to override multiple settings, the +.B \-o +option may be specified multiple times. .\"------------------------------------- .TP .BI \-p\ path
--- a/doc/man/dsync.1.in Sat Feb 19 16:56:21 2011 +0200 +++ b/doc/man/dsync.1.in Tue Feb 22 15:31:31 2011 +0200 @@ -1,5 +1,5 @@ .\" Copyright (c) 2010 Dovecot authors, see the included COPYING file -.TH DSYNC 1 "2010-07-02" "Dovecot v2.0" "Dovecot" +.TH DSYNC 1 "2011-01-16" "Dovecot v2.0" "Dovecot" .SH NAME dsync \- Dovecot\(aqs mailbox synchronization utility .\"------------------------------------------------------------------------ @@ -111,7 +111,7 @@ .I @pkgsysconfdir@/dovecot.conf and from the userdb with the given .IR value . -In order to override multiple settings to +In order to override multiple settings, the .B \-o option may be specified multiple times. .\"---------------------------------
--- a/dovecot-config.in.in Sat Feb 19 16:56:21 2011 +0200 +++ b/dovecot-config.in.in Tue Feb 22 15:31:31 2011 +0200 @@ -1,6 +1,7 @@ DOVECOT_CFLAGS="@CFLAGS@" DOVECOT_LIBS="@LIBS@" DOVECOT_SSL_LIBS="@SSL_LIBS@" +DOVECOT_SQL_LIBS="@SQL_LIBS@" LIBDOVECOT="@LIBDOVECOT@ @MODULE_LIBS@" LIBDOVECOT_LOGIN="@LIBDOVECOT_LOGIN@ @SSL_LIBS@"
--- a/dovecot.m4 Sat Feb 19 16:56:21 2011 +0200 +++ b/dovecot.m4 Tue Feb 22 15:31:31 2011 +0200 @@ -6,7 +6,7 @@ # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. -# serial 3 +# serial 4 AC_DEFUN([DC_DOVECOT_MODULEDIR],[ AC_ARG_WITH(moduledir, @@ -56,10 +56,10 @@ AC_MSG_ERROR([dovecot-config not found]) fi - eval `grep -i '^dovecot_[[a-z]]*=' "$dovecotdir"/dovecot-config` + eval `grep -i '^dovecot_[[a-z_]]*=' "$dovecotdir"/dovecot-config` eval `grep '^LIBDOVECOT[[A-Z_]]*=' "$dovecotdir"/dovecot-config` AX_SUBST_L([dovecotdir], [dovecot_moduledir], [dovecot_pkgincludedir], [dovecot_pkglibexecdir], [dovecot_pkglibdir], [dovecot_docdir]) - AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS]) + AX_SUBST_L([DOVECOT_CFLAGS], [DOVECOT_LIBS], [DOVECOT_SSL_LIBS], [DOVECOT_SQL_LIBS]) AX_SUBST_L([LIBDOVECOT], [LIBDOVECOT_LOGIN], [LIBDOVECOT_SQL], [LIBDOVECOT_LDA], [LIBDOVECOT_STORAGE]) AX_SUBST_L([LIBDOVECOT_DEPS], [LIBDOVECOT_LOGIN_DEPS], [LIBDOVECOT_SQL_DEPS], [LIBDOVECOT_LDA_DEPS], [LIBDOVECOT_STORAGE_DEPS]) AX_SUBST_L([LIBDOVECOT_INCLUDE], [LIBDOVECOT_LDA_INCLUDE], [LIBDOVECOT_SERVICE_INCLUDE], [LIBDOVECOT_STORAGE_INCLUDE], [LIBDOVECOT_LOGIN_INCLUDE], [LIBDOVECOT_CONFIG_INCLUDE])
--- a/dovecot.service.in Sat Feb 19 16:56:21 2011 +0200 +++ b/dovecot.service.in Tue Feb 22 15:31:31 2011 +0200 @@ -6,3 +6,6 @@ Type=simple ExecStart=@sbindir@/dovecot -F NonBlocking=yes + +[Install] +WantedBy=multi-user.target
--- a/src/auth/auth-request.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/auth/auth-request.c Tue Feb 22 15:31:31 2011 +0200 @@ -894,7 +894,8 @@ if (set->username_chars_map[*p & 0xff] == 0) { *error_r = t_strdup_printf( "Username contains disallowed character: " - "0x%02x", *p); + "0x%02x (username: %s)", *p, + str_sanitize(username, 128)); return NULL; } } @@ -963,11 +964,8 @@ } request->user = auth_request_fix_username(request, username, error_r); - if (request->user == NULL) { - auth_request_log_debug(request, "auth", - "Invalid username: %s", str_sanitize(username, 128)); + if (request->user == NULL) return FALSE; - } if (request->translated_username == NULL) { /* similar to original_username, but after translations */ request->translated_username = request->user; @@ -1474,7 +1472,8 @@ } if (request->no_password) { - auth_request_log_info(request, subsystem, "No password"); + auth_request_log_debug(request, subsystem, + "Allowing any password"); return 1; }
--- a/src/auth/mech-winbind.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/auth/mech-winbind.c Tue Feb 22 15:31:31 2011 +0200 @@ -335,7 +335,7 @@ .passdb_need = MECH_PASSDB_NEED_NOTHING, mech_winbind_ntlm_auth_new, - mech_generic_auth_initial, + mech_winbind_auth_initial, mech_winbind_auth_continue, mech_generic_auth_free };
--- a/src/auth/passdb-cache.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/auth/passdb-cache.c Tue Feb 22 15:31:31 2011 +0200 @@ -18,7 +18,7 @@ *value != '\0' && *value != '\t') { /* hide the password */ p = strchr(value, '\t'); - value = t_strconcat("<hidden>", p, NULL); + value = t_strconcat(PASSWORD_HIDDEN_STR, p, NULL); } auth_request_log_debug(request, "cache", "hit: %s", value); }
--- a/src/auth/passdb.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/auth/passdb.c Tue Feb 22 15:31:31 2011 +0200 @@ -146,14 +146,18 @@ return; } - if (password == NULL) { + if (password != NULL) { + if (!passdb_get_credentials(auth_request, password, scheme, + &credentials, &size)) + result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE; + } else if (*auth_request->credentials_scheme == '\0') { + /* We're doing a passdb lookup (not authenticating). + Pass through a NULL password without an error. */ + } else { auth_request_log_info(auth_request, "password", "Requested %s scheme, but we have a NULL password", auth_request->credentials_scheme); result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE; - } else if (!passdb_get_credentials(auth_request, password, scheme, - &credentials, &size)) { - result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE; } callback(result, credentials, size, auth_request);
--- a/src/config/config-parser.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/config/config-parser.c Tue Feb 22 15:31:31 2011 +0200 @@ -74,6 +74,15 @@ i_assert(ret > 0); } +static bool +config_parser_is_in_localremote(struct config_section_stack *section) +{ + const struct config_filter *filter = §ion->filter; + + return filter->local_name != NULL || filter->local_bits > 0 || + filter->remote_bits > 0; +} + int config_apply_line(struct config_parser_context *ctx, const char *key, const char *line, const char *section_name) { @@ -85,6 +94,14 @@ ret = settings_parse_line(l->parser, line); if (ret > 0) { found = TRUE; + /* FIXME: remove once auth does support these. */ + if (strcmp(l->root->module_name, "auth") == 0 && + config_parser_is_in_localremote(ctx->cur_section)) { + ctx->error = p_strconcat(ctx->pool, + "Auth settings not supported inside local/remote blocks: ", + key, NULL); + return -1; + } if (section_name != NULL) config_add_type(l->parser, line, section_name); } else if (ret < 0) {
--- a/src/config/config-request.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/config/config-request.c Tue Feb 22 15:31:31 2011 +0200 @@ -286,16 +286,14 @@ break; } hash_table_insert(ctx->keys, key, key); - ctx->callback(key, "0", CONFIG_KEY_LIST, ctx->context); strings = array_get(val, &count); i_assert(count % 2 == 0); for (i = 0; i < count; i += 2) { - str = p_strdup_printf(ctx->pool, "%s%s%c0%c%s", + str = p_strdup_printf(ctx->pool, "%s%s%c%s", str_c(ctx->prefix), def->key, SETTINGS_SEPARATOR, - SETTINGS_SEPARATOR, strings[i]); ctx->callback(str, strings[i+1], CONFIG_KEY_NORMAL, ctx->context);
--- a/src/doveadm/doveadm-mail-import.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/doveadm/doveadm-mail-import.c Tue Feb 22 15:31:31 2011 +0200 @@ -167,7 +167,7 @@ /* create a user for accessing the source storage */ memset(&input, 0, sizeof(input)); - input.module = "module"; + input.module = "mail"; input.username = "doveadm"; input.no_userdb_lookup = TRUE; input.userdb_fields = userdb_fields;
--- a/src/doveadm/doveadm-mail.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/doveadm/doveadm-mail.c Tue Feb 22 15:31:31 2011 +0200 @@ -372,7 +372,7 @@ ctx = doveadm_mail_cmd_init(cmd); - getopt_args = t_strconcat("As:u:", ctx->getopt_args, NULL); + getopt_args = t_strconcat("AS:u:", ctx->getopt_args, NULL); username = getenv("USER"); wildcard_user = NULL; while ((c = getopt(argc, argv, getopt_args)) > 0) {
--- a/src/doveadm/doveadm.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/doveadm/doveadm.c Tue Feb 22 15:31:31 2011 +0200 @@ -234,6 +234,7 @@ memset(&input, 0, sizeof(input)); input.roots = set_roots; input.module = "doveadm"; + input.preserve_user = TRUE; input.preserve_home = TRUE; if (master_service_settings_read(master_service, &input, &output, &error) < 0)
--- a/src/dsync/dsync-brain-msgs-new.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/dsync/dsync-brain-msgs-new.c Tue Feb 22 15:31:31 2011 +0200 @@ -182,6 +182,11 @@ dest_iter->adding_msgs = FALSE; if (dsync_worker_output_flush(src_iter->worker) < 0) return -1; + if (dsync_worker_is_output_full(dest_iter->worker)) { + /* see if the output becomes less full by flushing */ + if (dsync_worker_output_flush(dest_iter->worker) < 0) + return -1; + } } return dsync_worker_is_output_full(dest_iter->worker) ? 0 : 1; }
--- a/src/dsync/dsync-brain-msgs.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/dsync/dsync-brain-msgs.c Tue Feb 22 15:31:31 2011 +0200 @@ -112,17 +112,23 @@ static int dsync_brain_msg_iter_next_pair(struct dsync_brain_mailbox_sync *sync) { - int ret; + int ret1, ret2; if (sync->skip_mailbox) { if (dsync_brain_msg_iter_skip_mailbox(sync) == 0) return 0; } - if ((ret = dsync_brain_msg_iter_next(sync->src_msg_iter)) <= 0) - return ret; - if ((ret = dsync_brain_msg_iter_next(sync->dest_msg_iter)) <= 0) - return ret; + ret1 = dsync_brain_msg_iter_next(sync->src_msg_iter); + ret2 = dsync_brain_msg_iter_next(sync->dest_msg_iter); + if (ret1 == 0 || ret2 == 0) { + /* make sure we iterate through everything in both iterators + (even if it might not seem necessary, because proxy + requires it) */ + return 0; + } + if (ret1 < 0 || ret2 < 0) + return -1; return 1; }
--- a/src/dsync/dsync-data.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/dsync/dsync-data.c Tue Feb 22 15:31:31 2011 +0200 @@ -19,7 +19,9 @@ if (array_is_created(&box->cache_fields)) cache_fields = array_get(&box->cache_fields, &count); - if (count > 0) { + if (count == 0) + memset(&dest->cache_fields, 0, sizeof(dest->cache_fields)); + else { p_array_init(&dest->cache_fields, pool, count); for (i = 0; i < count; i++) { dup = p_strdup(pool, cache_fields[i]);
--- a/src/dsync/dsync-proxy-client.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/dsync/dsync-proxy-client.c Tue Feb 22 15:31:31 2011 +0200 @@ -382,10 +382,11 @@ return ret; } -static void proxy_client_worker_timeout(void *context ATTR_UNUSED) +static void +proxy_client_worker_timeout(struct proxy_client_dsync_worker *worker) { i_error("proxy client timed out"); - master_service_stop(master_service); + proxy_client_fail(worker); } struct dsync_worker *dsync_worker_init_proxy_client(int fd_in, int fd_out) @@ -397,7 +398,7 @@ worker->fd_in = fd_in; worker->fd_out = fd_out; worker->to = timeout_add(DSYNC_PROXY_TIMEOUT_MSECS, - proxy_client_worker_timeout, NULL); + proxy_client_worker_timeout, worker); worker->io = io_add(fd_in, IO_READ, proxy_client_worker_input, worker); worker->input = i_stream_create_fd(fd_in, (size_t)-1, FALSE); worker->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE); @@ -977,6 +978,8 @@ proxy_client_send_stream_real(struct proxy_client_dsync_worker *worker) { dsync_worker_save_callback_t *callback; + void *context; + struct istream *input; const unsigned char *data; size_t size; int ret; @@ -1019,11 +1022,20 @@ } callback = worker->save_callback; + context = worker->save_context; worker->save_callback = NULL; - i_stream_unref(&worker->save_input); + worker->save_context = NULL; + + /* a bit ugly way to free the stream. the problem is that local worker + has set a destroy callback, which in turn can call our msg_save() + again before the i_stream_unref() is finished. */ + input = worker->save_input; + worker->save_input = NULL; + i_stream_unref(&input); + (void)proxy_client_worker_output_flush(&worker->worker); - callback(worker->save_context); + callback(context); } static void proxy_client_send_stream(struct proxy_client_dsync_worker *worker) @@ -1053,7 +1065,7 @@ proxy_client_worker_cmd(worker, str); } T_END; - i_assert(worker->save_io == NULL); + i_assert(worker->save_input == NULL); worker->save_callback = callback; worker->save_context = context; worker->save_input = data->input;
--- a/src/dsync/dsync-worker-local.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/dsync/dsync-worker-local.c Tue Feb 22 15:31:31 2011 +0200 @@ -964,6 +964,8 @@ msg_r->flags = mail_get_flags(iter->mail); msg_r->keywords = mail_get_keywords(iter->mail); msg_r->modseq = mail_get_modseq(iter->mail); + if (mail_get_save_date(iter->mail, &msg_r->save_date) < 0) + msg_r->save_date = (time_t)-1; return 1; } @@ -1671,7 +1673,6 @@ i_error("Can't save message to mailbox %s: %s", mailbox_get_vname(dest_box), mailbox_get_last_error(dest_box, NULL)); - mailbox_save_cancel(&save_ctx); dsync_worker_set_failure(_worker); callback(context); return;
--- a/src/imap/cmd-idle.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/imap/cmd-idle.c Tue Feb 22 15:31:31 2011 +0200 @@ -55,10 +55,26 @@ client_command_free(&ctx->cmd); } +static bool +idle_client_handle_input(struct cmd_idle_context *ctx, bool free_cmd) +{ + const char *line; + + while ((line = i_stream_next_line(ctx->client->input)) != NULL) { + if (ctx->client->input_skip_line) + ctx->client->input_skip_line = FALSE; + else { + idle_finish(ctx, strcasecmp(line, "DONE") == 0, + free_cmd); + return TRUE; + } + } + return FALSE; +} + static void idle_client_input_more(struct cmd_idle_context *ctx) { struct client *client = ctx->client; - char *line; client->last_input = ioloop_time; timeout_reset(client->to_idle); @@ -82,15 +98,9 @@ return; } - while ((line = i_stream_next_line(client->input)) != NULL) { - if (client->input_skip_line) - client->input_skip_line = FALSE; - else { - idle_finish(ctx, strcasecmp(line, "DONE") == 0, TRUE); - if (!client->disconnected) - client_continue_pending_input(client); - break; - } + if (idle_client_handle_input(ctx, TRUE)) { + if (!client->disconnected) + client_continue_pending_input(client); } } @@ -264,6 +274,5 @@ added mailbox-notifier, we wouldn't see them otherwise. */ if (client->mailbox != NULL) idle_sync_now(client->mailbox, ctx); - idle_client_input_more(ctx); - return FALSE; + return idle_client_handle_input(ctx, FALSE); }
--- a/src/imap/imap-client.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/imap/imap-client.c Tue Feb 22 15:31:31 2011 +0200 @@ -432,7 +432,7 @@ return NULL; } -static bool client_command_check_ambiguity(struct client_command_context *cmd) +static bool client_command_is_ambiguous(struct client_command_context *cmd) { enum command_flags flags; enum client_command_state max_state = @@ -443,6 +443,17 @@ !imap_sync_is_allowed(cmd->client)) return TRUE; + if (cmd->search_save_result_used) { + /* if there are pending commands that update the search + save result, wait */ + struct client_command_context *old_cmd = cmd->next; + + for (; old_cmd != NULL; old_cmd = old_cmd->next) { + if (old_cmd->search_save_result) + return TRUE; + } + } + if ((cmd->cmd_flags & COMMAND_FLAG_BREAKS_MAILBOX) == COMMAND_FLAG_BREAKS_MAILBOX) { /* there must be no other command running that uses the @@ -578,11 +589,11 @@ /* the command is waiting for existing ambiguity causing commands to finish. */ - if (client_command_check_ambiguity(cmd)) { + if (client_command_is_ambiguous(cmd)) { /* we could be waiting for existing sync to finish */ if (!cmd_sync_delayed(client)) return; - if (client_command_check_ambiguity(cmd)) + if (client_command_is_ambiguous(cmd)) return; } cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; @@ -690,7 +701,7 @@ } else if ((command = command_find(cmd->name)) != NULL) { cmd->func = command->func; cmd->cmd_flags = command->flags; - if (client_command_check_ambiguity(cmd)) { + if (client_command_is_ambiguous(cmd)) { /* do nothing until existing commands are finished */ i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT); cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY; @@ -907,6 +918,7 @@ i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT); cmd->client->input_lock = cmd; cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY; + cmd->search_save_result_used = TRUE; io_remove(&cmd->client->io); return TRUE; }
--- a/src/imap/imap-client.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/imap/imap-client.h Tue Feb 22 15:31:31 2011 +0200 @@ -82,6 +82,7 @@ unsigned int cancel:1; /* command is wanted to be cancelled */ unsigned int param_error:1; unsigned int search_save_result:1; /* search result is being updated */ + unsigned int search_save_result_used:1; /* command uses search save */ unsigned int temp_executed:1; /* temporary execution state tracking */ };
--- a/src/lda/main.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lda/main.c Tue Feb 22 15:31:31 2011 +0200 @@ -254,7 +254,8 @@ &argc, &argv, "a:d:ef:km:p:r:"); memset(&ctx, 0, sizeof(ctx)); - ctx.pool = pool_alloconly_create("mail deliver context", 256); + ctx.session = mail_deliver_session_init(); + ctx.pool = ctx.session->pool; ctx.dest_mailbox_name = "INBOX"; path = NULL; @@ -467,7 +468,7 @@ mail_user_unref(&ctx.dest_user); mail_user_unref(&raw_mail_user); - pool_unref(&ctx.pool); + mail_deliver_session_deinit(&ctx.session); mail_storage_service_user_free(&service_user); mail_storage_service_deinit(&storage_service);
--- a/src/lib-index/mail-index.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-index/mail-index.c Tue Feb 22 15:31:31 2011 +0200 @@ -423,8 +423,19 @@ path = *path_r = t_strconcat(index->filepath, ".tmp", NULL); old_mask = umask(0); - fd = open(path, O_RDWR|O_CREAT|O_TRUNC, index->mode); + fd = open(path, O_RDWR|O_CREAT|O_EXCL, index->mode); umask(old_mask); + if (fd == -1 && errno == EEXIST) { + /* stale temp file. unlink and recreate rather than overwriting, + just to make sure locking problems won't cause corruption */ + if (unlink(path) < 0) { + i_error("unlink(%s) failed: %m", path); + return -1; + } + old_mask = umask(0); + fd = open(path, O_RDWR|O_CREAT|O_EXCL, index->mode); + umask(old_mask); + } if (fd == -1) { mail_index_file_set_syscall_error(index, path, "creat()"); return -1;
--- a/src/lib-index/mail-transaction-log-view.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-index/mail-transaction-log-view.c Tue Feb 22 15:31:31 2011 +0200 @@ -495,13 +495,25 @@ array_create_from_buffer(&uids, &uid_buf, sizeof(struct mail_transaction_expunge)); break; - case MAIL_TRANSACTION_EXPUNGE_GUID: - if ((rec_size % sizeof(struct mail_transaction_expunge_guid)) != 0) { + case MAIL_TRANSACTION_EXPUNGE_GUID: { + const struct mail_transaction_expunge_guid *recs = data; + unsigned int i, count; + + if ((rec_size % sizeof(*recs)) != 0) { mail_transaction_log_file_set_corrupted(file, "Invalid expunge guid record size"); return FALSE; } + count = rec_size / sizeof(*recs); + for (i = 0; i < count; i++) { + if (recs[i].uid == 0) { + mail_transaction_log_file_set_corrupted(file, + "Expunge guid record with uid=0"); + return FALSE; + } + } break; + } case MAIL_TRANSACTION_FLAG_UPDATE: buffer_create_const_data(&uid_buf, data, rec_size); array_create_from_buffer(&uids, &uid_buf,
--- a/src/lib-lda/lda-settings.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-lda/lda-settings.c Tue Feb 22 15:31:31 2011 +0200 @@ -20,6 +20,7 @@ static const struct setting_define lda_setting_defines[] = { DEF(SET_STR, postmaster_address), DEF(SET_STR, hostname), + DEF(SET_STR, submission_host), DEF(SET_STR, sendmail_path), DEF(SET_STR, rejection_subject), DEF(SET_STR, rejection_reason), @@ -36,6 +37,7 @@ static const struct lda_settings lda_default_settings = { .postmaster_address = "", .hostname = "", + .submission_host = "", .sendmail_path = "/usr/sbin/sendmail", .rejection_subject = "Rejected: %s", .rejection_reason =
--- a/src/lib-lda/lda-settings.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-lda/lda-settings.h Tue Feb 22 15:31:31 2011 +0200 @@ -6,12 +6,14 @@ struct lda_settings { const char *postmaster_address; const char *hostname; + const char *submission_host; const char *sendmail_path; const char *rejection_subject; const char *rejection_reason; const char *deliver_log_format; const char *recipient_delimiter; const char *lda_original_recipient_header; + bool quota_full_tempfail; bool lda_mailbox_autocreate; bool lda_mailbox_autosubscribe;
--- a/src/lib-lda/mail-deliver.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-lda/mail-deliver.c Tue Feb 22 15:31:31 2011 +0200 @@ -116,6 +116,25 @@ va_end(args); } +struct mail_deliver_session *mail_deliver_session_init(void) +{ + struct mail_deliver_session *session; + pool_t pool; + + pool = pool_alloconly_create("mail deliver session", 1024); + session = p_new(pool, struct mail_deliver_session, 1); + session->pool = pool; + return session; +} + +void mail_deliver_session_deinit(struct mail_deliver_session **_session) +{ + struct mail_deliver_session *session = *_session; + + *_session = NULL; + pool_unref(&session->pool); +} + int mail_deliver_save_open(struct mail_deliver_save_open_context *ctx, const char *name, struct mailbox **box_r, enum mail_error *error_r, const char **error_str_r) @@ -185,6 +204,47 @@ return 0; } +static bool mail_deliver_check_duplicate(struct mail_deliver_session *session, + struct mail_user *user) +{ + const char *const *usernamep, *username; + + /* there shouldn't be all that many recipients, + so just do a linear search */ + if (!array_is_created(&session->inbox_users)) + p_array_init(&session->inbox_users, session->pool, 8); + array_foreach(&session->inbox_users, usernamep) { + if (strcmp(*usernamep, user->username) == 0) + return TRUE; + } + username = p_strdup(session->pool, user->username); + array_append(&session->inbox_users, &username, 1); + return FALSE; +} + +void mail_deliver_deduplicate_guid_if_needed(struct mail_deliver_session *session, + struct mail_save_context *save_ctx) +{ + struct mailbox_transaction_context *trans = + mailbox_save_get_transaction(save_ctx); + struct mailbox *box = mailbox_transaction_get_mailbox(trans); + struct mail_storage *storage = mailbox_get_storage(box); + struct mail_user *user = mail_storage_get_user(storage); + uint8_t guid[MAIL_GUID_128_SIZE]; + + if (strcmp(mailbox_get_name(box), "INBOX") != 0) + return; + + /* avoid storing duplicate GUIDs to delivered mails to INBOX. this + happens if mail is delivered to same user multiple times within a + session. the problem with this is that if GUIDs are used as POP3 + UIDLs, some clients can't handle the duplicates well. */ + if (mail_deliver_check_duplicate(session, user)) { + mail_generate_guid_128(guid); + mailbox_save_set_guid(save_ctx, mail_guid_128_to_string(guid)); + } +} + int mail_deliver_save(struct mail_deliver_context *ctx, const char *mailbox, enum mail_flags flags, const char *const *keywords, struct mail_storage **storage_r) @@ -241,6 +301,7 @@ ctx->dest_mail = mail_alloc(t, lda_log_wanted_fetch_fields, NULL); mailbox_header_lookup_unref(&headers_ctx); mailbox_save_set_dest_mail(save_ctx, ctx->dest_mail); + mail_deliver_deduplicate_guid_if_needed(ctx->session, save_ctx); if (mailbox_copy(&save_ctx, ctx->src_mail) < 0) ret = -1;
--- a/src/lib-lda/mail-deliver.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-lda/mail-deliver.h Tue Feb 22 15:31:31 2011 +0200 @@ -4,11 +4,20 @@ enum mail_flags; enum mail_error; struct mail_storage; +struct mail_save_context; struct mailbox; +struct mail_deliver_session { + pool_t pool; + + /* List of users who have already saved this mail to their INBOX */ + ARRAY_TYPE(const_string) inbox_users; +}; + struct mail_deliver_context { pool_t pool; const struct lda_settings *set; + struct mail_deliver_session *session; struct duplicate_context *dup_ctx; @@ -62,6 +71,9 @@ const char *mail_deliver_get_return_address(struct mail_deliver_context *ctx); const char *mail_deliver_get_new_message_id(struct mail_deliver_context *ctx); +struct mail_deliver_session *mail_deliver_session_init(void); +void mail_deliver_session_deinit(struct mail_deliver_session **session); + /* Try to open mailbox for saving. Returns 0 if ok, -1 if error. The box may be returned even with -1, and the caller must free it then. */ int mail_deliver_save_open(struct mail_deliver_save_open_context *ctx, @@ -70,6 +82,8 @@ int mail_deliver_save(struct mail_deliver_context *ctx, const char *mailbox, enum mail_flags flags, const char *const *keywords, struct mail_storage **storage_r); +void mail_deliver_deduplicate_guid_if_needed(struct mail_deliver_session *session, + struct mail_save_context *save_ctx); int mail_deliver(struct mail_deliver_context *ctx, struct mail_storage **storage_r);
--- a/src/lib-lda/smtp-client.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-lda/smtp-client.c Tue Feb 22 15:31:31 2011 +0200 @@ -1,8 +1,14 @@ /* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "close-keep-errno.h" +#include "safe-mkstemp.h" #include "execv-const.h" +#include "istream.h" #include "master-service.h" +#include "lmtp-client.h" #include "lda-settings.h" #include "mail-deliver.h" #include "smtp-client.h" @@ -11,9 +17,20 @@ #include <sys/wait.h> #include <sysexits.h> +#define DEFAULT_SUBMISSION_PORT 25 + struct smtp_client { FILE *f; pid_t pid; + + bool use_smtp; + bool success; + bool finished; + + const struct lda_settings *set; + char *temp_path; + char *destination; + char *return_path; }; static struct smtp_client *smtp_client_devnull(FILE **file_r) @@ -51,14 +68,15 @@ if (dup2(fd, STDIN_FILENO) < 0) i_fatal("dup2() failed: %m"); - master_service_env_clean(TRUE); + master_service_env_clean(); execv_const(sendmail_path, argv); } -struct smtp_client * -smtp_client_open(const struct lda_settings *set, const char *destination, - const char *return_path, FILE **file_r) +static struct smtp_client * +smtp_client_open_sendmail(const struct lda_settings *set, + const char *destination, const char *return_path, + FILE **file_r) { struct smtp_client *client; int fd[2]; @@ -88,11 +106,68 @@ return client; } -int smtp_client_close(struct smtp_client *client) +static int create_temp_file(const char **path_r) +{ + string_t *path; + int fd; + + path = t_str_new(128); + str_append(path, "/tmp/dovecot."); + str_append(path, master_service_get_name(master_service)); + str_append_c(path, '.'); + + fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); + if (fd == -1) { + i_error("safe_mkstemp(%s) failed: %m", str_c(path)); + return -1; + } + + /* we just want the fd, unlink it */ + if (unlink(str_c(path)) < 0) { + /* shouldn't happen.. */ + i_error("unlink(%s) failed: %m", str_c(path)); + close_keep_errno(fd); + return -1; + } + + *path_r = str_c(path); + return fd; +} + +struct smtp_client * +smtp_client_open(const struct lda_settings *set, const char *destination, + const char *return_path, FILE **file_r) +{ + struct smtp_client *client; + const char *path; + int fd; + + if (*set->submission_host == '\0') { + return smtp_client_open_sendmail(set, destination, + return_path, file_r); + } + + if ((fd = create_temp_file(&path)) == -1) + return smtp_client_devnull(file_r); + + client = i_new(struct smtp_client, 1); + client->set = set; + client->temp_path = i_strdup(path); + client->destination = i_strdup(destination); + client->return_path = i_strdup(return_path); + client->f = *file_r = fdopen(fd, "w"); + if (client->f == NULL) + i_fatal("fdopen() failed: %m"); + client->use_smtp = TRUE; + return client; +} + +static int smtp_client_close_sendmail(struct smtp_client *client) { int ret = EX_TEMPFAIL, status; fclose(client->f); + if (client->pid == (pid_t)-1) { /* smtp_client_open() failed already */ } else if (waitpid(client->pid, &status, 0) < 0) @@ -113,7 +188,118 @@ i_error("Sendmail process terminated abnormally, " "return status %d", status); } - i_free(client); return ret; } + +static void smtp_client_send_finished(void *context) +{ + struct smtp_client *smtp_client = context; + + smtp_client->finished = TRUE; + io_loop_stop(current_ioloop); +} + +static void +rcpt_to_callback(bool success, const char *reply, void *context) +{ + struct smtp_client *smtp_client = context; + + if (!success) { + i_error("smtp(%s): RCPT TO failed: %s", + smtp_client->set->submission_host, reply); + smtp_client_send_finished(smtp_client); + } +} + +static void +data_callback(bool success, const char *reply, void *context) +{ + struct smtp_client *smtp_client = context; + + if (!success) { + i_error("smtp(%s): DATA failed: %s", + smtp_client->set->submission_host, reply); + smtp_client_send_finished(smtp_client); + } else { + smtp_client->success = TRUE; + } +} + +static int smtp_client_send(struct smtp_client *smtp_client) +{ + struct lmtp_client_settings client_set; + struct lmtp_client *client; + struct ioloop *ioloop; + struct istream *input; + const char *host, *p; + unsigned int port = DEFAULT_SUBMISSION_PORT; + + host = smtp_client->set->submission_host; + p = strchr(host, ':'); + if (p != NULL) { + host = t_strdup_until(host, p); + if (str_to_uint(p + 1, &port) < 0 || + port == 0 || port > 65535) { + i_error("Invalid port in submission_host: %s", p+1); + return -1; + } + } + + if (fflush(smtp_client->f) != 0) { + i_error("fflush(%s) failed: %m", smtp_client->temp_path); + return -1; + } + + if (lseek(fileno(smtp_client->f), 0, SEEK_SET) < 0) { + i_error("lseek(%s) failed: %m", smtp_client->temp_path); + return -1; + } + + memset(&client_set, 0, sizeof(client_set)); + client_set.mail_from = smtp_client->return_path == NULL ? "<>" : + t_strconcat("<", smtp_client->return_path, ">", NULL); + client_set.my_hostname = smtp_client->set->hostname; + client_set.dns_client_socket_path = "dns-client"; + + ioloop = io_loop_create(); + client = lmtp_client_init(&client_set, smtp_client_send_finished, + smtp_client); + + if (lmtp_client_connect_tcp(client, LMTP_CLIENT_PROTOCOL_SMTP, + host, port) < 0) { + lmtp_client_deinit(&client); + io_loop_destroy(&ioloop); + return -1; + } + + lmtp_client_add_rcpt(client, smtp_client->destination, + rcpt_to_callback, data_callback, smtp_client); + + input = i_stream_create_fd(fileno(smtp_client->f), (size_t)-1, FALSE); + lmtp_client_send(client, input); + i_stream_unref(&input); + + if (!smtp_client->finished) + io_loop_run(ioloop); + io_loop_destroy(&ioloop); + return smtp_client->success ? 0 : -1; +} + +int smtp_client_close(struct smtp_client *client) +{ + int ret; + + if (!client->use_smtp) + return smtp_client_close_sendmail(client); + + /* the mail has been written to a file. now actually send it. */ + ret = smtp_client_send(client); + + fclose(client->f); + i_free(client->return_path); + i_free(client->destination); + i_free(client->temp_path); + i_free(client); + return ret < 0 ? EX_TEMPFAIL : 0; +}
--- a/src/lib-mail/istream-header-filter.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-mail/istream-header-filter.c Tue Feb 22 15:31:31 2011 +0200 @@ -318,7 +318,9 @@ if (ret == -1 && stream->parent->eof && !last_lf) { /* missing LF, need to add it */ + i_assert(!mstream->last_lf_added); i_assert(size == 0 || data[size-1] != '\n'); + buffer_reset(mstream->hdr_buf); buffer_append(mstream->hdr_buf, data, size); if (mstream->crlf) @@ -328,7 +330,7 @@ mstream->last_lf_added = TRUE; stream->skip = 0; - stream->pos = size + 1; + stream->pos = mstream->hdr_buf->used; stream->buffer = mstream->hdr_buf->data; return mstream->crlf ? 2 : 1; } else { @@ -425,8 +427,6 @@ struct header_filter_istream *mstream = (struct header_filter_istream *)stream; - mstream->last_lf_added = FALSE; - if (stream->istream.v_offset == v_offset) { /* just reset the input buffer */ stream_reset_to(mstream, v_offset); @@ -434,6 +434,9 @@ mstream->istream.parent_expected_offset); return; } + /* if last_lf_added=TRUE, we're currently at EOF. So reset it only if + we're seeking backwards, otherwise we would just add a duplicate */ + mstream->last_lf_added = FALSE; if (v_offset == 0) { /* seeking to beginning of headers. */
--- a/src/lib-master/master-auth.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-master/master-auth.c Tue Feb 22 15:31:31 2011 +0200 @@ -116,7 +116,8 @@ return; i_error("read(%s) failed: %m", conn->auth->path); } else { - i_error("read(%s) failed: Remote closed connection", + i_error("read(%s) failed: Remote closed connection " + "(process_limit reached?)", conn->auth->path); } master_auth_connection_deinit(&conn);
--- a/src/lib-master/master-interface.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-master/master-interface.h Tue Feb 22 15:31:31 2011 +0200 @@ -59,6 +59,10 @@ if dovecot was started with -p parameter. */ #define MASTER_SSL_KEY_PASSWORD_ENV "SSL_KEY_PASSWORD" +/* getenv(DOVECOT_PRESERVE_ENVS_ENV) returns a space separated list of + environments that should be preserved. */ +#define DOVECOT_PRESERVE_ENVS_ENV "DOVECOT_PRESERVE_ENVS" + /* Write pipe to anvil. */ #define MASTER_ANVIL_FD 3 /* Anvil reads new log fds from this fd */
--- a/src/lib-master/master-service-settings.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-master/master-service-settings.c Tue Feb 22 15:31:31 2011 +0200 @@ -90,12 +90,22 @@ const struct master_service_settings_input *input) { const char **conf_argv, *binary_path = service->argv[0]; + const char *home = NULL, *user = NULL; unsigned int i, argv_max_count; (void)t_binary_abspath(&binary_path); - if (!service->keep_environment) - master_service_env_clean(input->preserve_home); + if (!service->keep_environment && !input->preserve_environment) { + if (input->preserve_home) + home = getenv("HOME"); + if (input->preserve_user) + user = getenv("USER"); + master_service_env_clean(); + if (home != NULL) + env_put(t_strconcat("HOME=", home, NULL)); + if (user != NULL) + env_put(t_strconcat("USER=", user, NULL)); + } if (input->use_sysexits) env_put("USE_SYSEXITS=1");
--- a/src/lib-master/master-service-settings.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-master/master-service-settings.h Tue Feb 22 15:31:31 2011 +0200 @@ -21,6 +21,8 @@ struct master_service_settings_input { const struct setting_parser_info *const *roots; const char *config_path; + bool preserve_environment; + bool preserve_user; bool preserve_home; bool never_exec; bool use_sysexits;
--- a/src/lib-master/master-service.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-master/master-service.c Tue Feb 22 15:31:31 2011 +0200 @@ -391,22 +391,16 @@ master_status_update(service); } -void master_service_env_clean(bool preserve_home) +void master_service_env_clean(void) { - static const char *preserve_envs[] = { - "HOME", /* keep as the first element */ - "USER", - "TZ", -#ifdef DEBUG - "GDB", -#endif -#ifdef HAVE_SYSTEMD - "LISTEN_PID", - "LISTEN_FDS", -#endif - NULL - }; - env_clean_except(preserve_envs + (preserve_home ? 0 : 1)); + const char *value = getenv(DOVECOT_PRESERVE_ENVS_ENV); + + if (value == NULL || *value == '\0') + env_clean(); + else T_BEGIN { + value = t_strconcat(value, " "DOVECOT_PRESERVE_ENVS_ENV, NULL); + env_clean_except(t_strsplit_spaces(value, " ")); + } T_END; } void master_service_set_client_limit(struct master_service *service,
--- a/src/lib-master/master-service.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-master/master-service.h Tue Feb 22 15:31:31 2011 +0200 @@ -59,8 +59,9 @@ before calling this. */ void master_service_init_finish(struct master_service *service); -/* Clean environment from everything except TZ, USER and optionally HOME. */ -void master_service_env_clean(bool preserve_home); +/* Clean environment from everything except the ones listed in + DOVECOT_PRESERVE_ENVS environment. */ +void master_service_env_clean(void); /* Initialize logging. */ void master_service_init_log(struct master_service *service,
--- a/src/lib-settings/settings-parser.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-settings/settings-parser.c Tue Feb 22 15:31:31 2011 +0200 @@ -66,6 +66,9 @@ .parent_offset = (size_t)-1 }; +static int settings_parse_keyvalue(struct setting_parser_context *ctx, + const char *key, const char *value); + static void setting_parser_copy_defaults(struct setting_parser_context *ctx, const struct setting_parser_info *info, @@ -657,7 +660,7 @@ { const struct setting_define *def; struct setting_link *link; - const char *end; + const char *end, *parent_key; unsigned int i; /* try to find from roots */ @@ -679,9 +682,27 @@ if (end == NULL) return FALSE; - link = hash_table_lookup(ctx->links, t_strdup_until(key, end)); - if (link == NULL) - return FALSE; + parent_key = t_strdup_until(key, end); + link = hash_table_lookup(ctx->links, parent_key); + if (link == NULL) { + /* maybe this is the first strlist value */ + unsigned int parent_n = 0; + const struct setting_define *parent_def; + struct setting_link *parent_link; + + if (!settings_find_key_nth(ctx, parent_key, &parent_n, + &parent_def, &parent_link)) + return FALSE; + if (parent_def->type != SET_STRLIST) + return FALSE; + + /* setting parent_key=0 adds it to links list */ + if (settings_parse_keyvalue(ctx, parent_key, "0") <= 0) + return FALSE; + + link = hash_table_lookup(ctx->links, parent_key); + i_assert(link != NULL); + } *link_r = link; if (link->info == &strlist_info) {
--- a/src/lib-sql/Makefile.am Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-sql/Makefile.am Tue Feb 22 15:31:31 2011 +0200 @@ -64,6 +64,12 @@ libdriver_sqlite_la_SOURCES = driver-sqlite.c endif +pkglib_LTLIBRARIES = libdovecot-sql.la +libdovecot_sql_la_SOURCES = +libdovecot_sql_la_LIBADD = libsql.la $(MODULE_LIBS) +libdovecot_sql_la_DEPENDENCIES = libsql.la +libdovecot_sql_la_LDFLAGS = -export-dynamic + headers = \ sql-api.h \ sql-api-private.h \
--- a/src/lib-sql/driver-mysql.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-sql/driver-mysql.c Tue Feb 22 15:31:31 2011 +0200 @@ -31,7 +31,7 @@ struct sql_result api; MYSQL_RES *result; - MYSQL_ROW row; + MYSQL_ROW row; MYSQL_FIELD *fields; unsigned int fields_count; @@ -406,12 +406,16 @@ } static const unsigned char * -driver_mysql_result_get_field_value_binary(struct sql_result *_result ATTR_UNUSED, - unsigned int idx ATTR_UNUSED, - size_t *size_r ATTR_UNUSED) +driver_mysql_result_get_field_value_binary(struct sql_result *_result, + unsigned int idx, size_t *size_r) { - /* FIXME */ - return NULL; + struct mysql_result *result = (struct mysql_result *)_result; + unsigned long *lengths; + + lengths = mysql_fetch_lengths(result->result); + + *size_r = lengths[idx]; + return (const void *)result->row[idx]; } static const char * @@ -591,6 +595,8 @@ .failed_try_retry = TRUE }; +const char *driver_mysql_version = DOVECOT_VERSION; + void driver_mysql_init(void); void driver_mysql_deinit(void);
--- a/src/lib-sql/driver-pgsql.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-sql/driver-pgsql.c Tue Feb 22 15:31:31 2011 +0200 @@ -75,6 +75,8 @@ static void driver_pgsql_set_state(struct pgsql_db *db, enum sql_db_state state) { + i_assert(state == SQL_DB_STATE_BUSY || db->cur_result == NULL); + /* switch back to original ioloop in case the caller wants to add/remove timeouts */ if (db->ioloop != NULL) @@ -422,6 +424,10 @@ static void query_timeout(struct pgsql_result *result) { + struct pgsql_db *db = (struct pgsql_db *)result->api.db; + + driver_pgsql_stop_io(db); + i_error("pgsql: Query timed out, aborting"); result->timeout = TRUE; result_finish(result); @@ -526,6 +532,8 @@ static void driver_pgsql_sync_init(struct pgsql_db *db) { + bool add_to_connect; + db->orig_ioloop = current_ioloop; if (db->io == NULL) { db->ioloop = io_loop_create(); @@ -536,12 +544,18 @@ /* have to move our existing I/O and timeout handlers to new I/O loop */ io_remove(&db->io); - if (db->to_connect != NULL) + if (db->to_connect != NULL) { timeout_remove(&db->to_connect); + add_to_connect = TRUE; + } else { + add_to_connect = FALSE; + } db->ioloop = io_loop_create(); - db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000, - driver_pgsql_connect_timeout, db); + if (add_to_connect) { + db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000, + driver_pgsql_connect_timeout, db); + } db->io = io_add(PQsocket(db->pg), db->io_dir, connect_callback, db); /* wait for connecting to finish */ io_loop_run(db->ioloop); @@ -1041,6 +1055,8 @@ } }; +const char *driver_pgsql_version = DOVECOT_VERSION; + void driver_pgsql_init(void); void driver_pgsql_deinit(void);
--- a/src/lib-sql/driver-sqlite.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-sql/driver-sqlite.c Tue Feb 22 15:31:31 2011 +0200 @@ -444,6 +444,8 @@ } }; +const char *driver_sqlite_version = DOVECOT_VERSION; + void driver_sqlite_init(void); void driver_sqlite_deinit(void);
--- a/src/lib-storage/index/dbox-common/dbox-attachment.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-attachment.c Tue Feb 22 15:31:31 2011 +0200 @@ -148,11 +148,13 @@ const char *path, *path_suffix; uoff_t psize, last_voffset = 0; unsigned int i; + int ret = 1; t_array_init(&extrefs_arr, 16); if (!dbox_attachment_parse_extref_real(ext_refs, pool_datastack_create(), &extrefs_arr)) return 0; + psize = dbox_file_get_plaintext_size(file); t_array_init(&streams, 8); array_foreach(&extrefs_arr, extref) { @@ -166,6 +168,10 @@ input = i_stream_create_limit(*stream, part_size); array_append(&streams, &input, 1); i_stream_seek(*stream, (*stream)->v_offset + part_size); + if ((*stream)->v_offset + part_size > psize) { + /* extrefs point outside message */ + ret = 0; + } last_voffset += part_size; } @@ -185,13 +191,17 @@ array_append(&streams, &input, 1); } - psize = dbox_file_get_plaintext_size(file); if (psize != (*stream)->v_offset) { - uoff_t trailer_size = psize - (*stream)->v_offset; + if (psize < (*stream)->v_offset) { + /* extrefs point outside message */ + ret = 0; + } else { + uoff_t trailer_size = psize - (*stream)->v_offset; - input = i_stream_create_limit(*stream, trailer_size); - array_append(&streams, &input, 1); - (void)array_append_space(&streams); + input = i_stream_create_limit(*stream, trailer_size); + array_append(&streams, &input, 1); + (void)array_append_space(&streams); + } } inputs = array_idx_modifiable(&streams, 0); @@ -199,7 +209,7 @@ *stream = i_stream_create_concat(inputs); for (i = 0; inputs[i] != NULL; i++) i_stream_unref(&inputs[i]); - return 1; + return ret; } int dbox_attachment_file_get_stream(struct dbox_file *file,
--- a/src/lib-storage/index/dbox-common/dbox-file.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-file.c Tue Feb 22 15:31:31 2011 +0200 @@ -479,6 +479,13 @@ *_ctx = NULL; ret = dbox_file_append_flush(ctx); + if (ctx->last_checkpoint_offset != ctx->output->offset) { + o_stream_close(ctx->output); + if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) { + dbox_file_set_syscall_error(ctx->file, "ftruncate()"); + return -1; + } + } o_stream_unref(&ctx->output); ctx->file->appending = FALSE; i_free(ctx); @@ -538,6 +545,11 @@ return 0; } +void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx) +{ + ctx->last_checkpoint_offset = ctx->output->offset; +} + int dbox_file_get_append_stream(struct dbox_file_append_context *ctx, struct ostream **output_r) { @@ -548,6 +560,11 @@ /* file creation had failed */ return -1; } + if (ctx->last_checkpoint_offset != ctx->output->offset) { + /* a message was aborted. don't try appending to this + file anymore. */ + return -1; + } if (file->file_version == 0) { /* newly created file, write the file header */
--- a/src/lib-storage/index/dbox-common/dbox-file.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-file.h Tue Feb 22 15:31:31 2011 +0200 @@ -126,7 +126,7 @@ struct dbox_file_append_context { struct dbox_file *file; - uoff_t first_append_offset, last_flush_offset; + uoff_t first_append_offset, last_checkpoint_offset, last_flush_offset; struct ostream *output; }; @@ -173,6 +173,9 @@ can't be appended to (old file version or corruption) or -1 if error. */ int dbox_file_get_append_stream(struct dbox_file_append_context *ctx, struct ostream **output_r); +/* Call after message has been fully saved. If this isn't done, the writes + since the last checkpoint are truncated. */ +void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx); /* Flush output buffer. */ int dbox_file_append_flush(struct dbox_file_append_context *ctx);
--- a/src/lib-storage/index/dbox-common/dbox-sync-rebuild.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-sync-rebuild.c Tue Feb 22 15:31:31 2011 +0200 @@ -3,6 +3,7 @@ #include "lib.h" #include "array.h" #include "mail-index-modseq.h" +#include "mailbox-list-private.h" #include "index-storage.h" #include "dbox-storage.h" #include "dbox-sync-rebuild.h" @@ -190,3 +191,29 @@ } i_free(ctx); } + +int dbox_sync_rebuild_verify_alt_storage(struct mailbox_list *list) +{ + const char *alt_path; + struct stat st; + + alt_path = mailbox_list_get_path(list, NULL, + MAILBOX_LIST_PATH_TYPE_ALT_DIR); + if (alt_path == NULL) + return 0; + + /* make sure alt storage is mounted. if it's not, abort the rebuild. */ + if (stat(alt_path, &st) == 0) + return 0; + if (errno != ENOENT) { + i_error("stat(%s) failed: %m", alt_path); + return -1; + } + + /* try to create the alt directory. if it fails, it means alt + storage isn't mounted. */ + if (mailbox_list_mkdir(list, alt_path, + MAILBOX_LIST_PATH_TYPE_ALT_DIR) < 0) + return -1; + return 0; +}
--- a/src/lib-storage/index/dbox-common/dbox-sync-rebuild.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-sync-rebuild.h Tue Feb 22 15:31:31 2011 +0200 @@ -1,6 +1,8 @@ #ifndef DBOX_SYNC_REBUILD_H #define DBOX_SYNC_REBUILD_H +struct mailbox_list; + struct dbox_sync_rebuild_context { struct mailbox *box; @@ -23,5 +25,6 @@ void dbox_sync_rebuild_index_metadata(struct dbox_sync_rebuild_context *ctx, uint32_t new_seq, uint32_t uid); +int dbox_sync_rebuild_verify_alt_storage(struct mailbox_list *list); #endif
--- a/src/lib-storage/index/dbox-multi/mdbox-map.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-multi/mdbox-map.c Tue Feb 22 15:31:31 2011 +0200 @@ -1100,15 +1100,19 @@ void mdbox_map_append_finish(struct mdbox_map_append_context *ctx) { - struct mdbox_map_append *appends; + struct mdbox_map_append *appends, *last; unsigned int count; uoff_t cur_offset; appends = array_get_modifiable(&ctx->appends, &count); - i_assert(count > 0 && appends[count-1].size == (uint32_t)-1); - cur_offset = appends[count-1].file_append->output->offset; - i_assert(cur_offset >= appends[count-1].offset); - appends[count-1].size = cur_offset - appends[count-1].offset; + i_assert(count > 0); + last = &appends[count-1]; + i_assert(last->size == (uint32_t)-1); + + cur_offset = last->file_append->output->offset; + i_assert(cur_offset >= last->offset); + last->size = cur_offset - last->offset; + dbox_file_append_checkpoint(last->file_append); } void mdbox_map_append_abort(struct mdbox_map_append_context *ctx)
--- a/src/lib-storage/index/dbox-multi/mdbox-save.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-multi/mdbox-save.c Tue Feb 22 15:31:31 2011 +0200 @@ -410,7 +410,8 @@ ctx->ctx.finished = TRUE; if (mail->box->storage != _ctx->transaction->box->storage || - _ctx->transaction->box->disable_reflink_copy_to) + _ctx->transaction->box->disable_reflink_copy_to || + _ctx->guid != NULL) return mail_storage_copy(_ctx, mail); src_mbox = (struct mdbox_mailbox *)mail->box;
--- a/src/lib-storage/index/dbox-multi/mdbox-storage-rebuild.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-multi/mdbox-storage-rebuild.c Tue Feb 22 15:31:31 2011 +0200 @@ -9,6 +9,7 @@ #include "mail-cache.h" #include "dbox-sync-rebuild.h" #include "mail-namespace.h" +#include "mailbox-list-private.h" #include "mdbox-storage.h" #include "mdbox-file.h" #include "mdbox-map-private.h" @@ -890,6 +891,14 @@ struct mdbox_storage_rebuild_context *ctx; int ret; + if (dbox_sync_rebuild_verify_alt_storage(storage->map->root_list) < 0) { + mail_storage_set_critical(&storage->storage.storage, + "mdbox rebuild: Alt storage %s not mounted, aborting", + storage->alt_storage_dir); + mdbox_map_atomic_set_failed(atomic); + return -1; + } + ctx = mdbox_storage_rebuild_init(storage, atomic); ret = mdbox_storage_rebuild_scan(ctx); mdbox_storage_rebuild_deinit(ctx);
--- a/src/lib-storage/index/dbox-single/sdbox-copy.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-single/sdbox-copy.c Tue Feb 22 15:31:31 2011 +0200 @@ -148,7 +148,8 @@ i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); ctx->finished = TRUE; - if (mail_storage_copy_can_use_hardlink(mail->box, &mbox->box)) { + if (mail_storage_copy_can_use_hardlink(mail->box, &mbox->box) && + _ctx->guid == NULL) { T_BEGIN { ret = sdbox_copy_hardlink(_ctx, mail); } T_END;
--- a/src/lib-storage/index/dbox-single/sdbox-save.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-single/sdbox-save.c Tue Feb 22 15:31:31 2011 +0200 @@ -186,8 +186,11 @@ if (ctx->ctx.failed) dbox_file_append_rollback(&ctx->append_ctx); - else if (dbox_file_append_commit(&ctx->append_ctx) < 0) - ctx->ctx.failed = TRUE; + else { + dbox_file_append_checkpoint(ctx->append_ctx); + if (dbox_file_append_commit(&ctx->append_ctx) < 0) + ctx->ctx.failed = TRUE; + } i_stream_unref(&ctx->ctx.input); dbox_file_close(*files);
--- a/src/lib-storage/index/dbox-single/sdbox-sync-rebuild.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/dbox-single/sdbox-sync-rebuild.c Tue Feb 22 15:31:31 2011 +0200 @@ -178,6 +178,13 @@ } } + if (dbox_sync_rebuild_verify_alt_storage(mbox->box.list) < 0) { + mail_storage_set_critical(mbox->box.storage, + "sdbox %s: Alt storage not mounted, " + "aborting index rebuild", mailbox_get_path(&mbox->box)); + return -1; + } + mail_cache_reset(mbox->box.cache); view = mail_index_view_open(mbox->box.index);
--- a/src/lib-storage/index/maildir/maildir-uidlist.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/index/maildir/maildir-uidlist.c Tue Feb 22 15:31:31 2011 +0200 @@ -1074,6 +1074,10 @@ int maildir_uidlist_get_mailbox_guid(struct maildir_uidlist *uidlist, uint8_t mailbox_guid[MAIL_GUID_128_SIZE]) { + if (!uidlist->initial_hdr_read) { + if (maildir_uidlist_refresh(uidlist) < 0) + return -1; + } if (!uidlist->have_mailbox_guid) { uidlist->recreate = TRUE; if (maildir_uidlist_update(uidlist) < 0)
--- a/src/lib-storage/list/mailbox-list-maildir.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-maildir.c Tue Feb 22 15:31:31 2011 +0200 @@ -129,16 +129,6 @@ return TRUE; } -static bool ATTR_NORETURN -maildir_is_valid_pattern(struct mailbox_list *list ATTR_UNUSED, - const char *pattern ATTR_UNUSED) -{ - i_unreached(); -#ifndef ATTRS_DEFINED - return FALSE; -#endif -} - static bool maildir_is_valid_existing_name(struct mailbox_list *_list, const char *name) { @@ -156,6 +146,15 @@ } static bool +maildir_is_valid_pattern(struct mailbox_list *list, const char *pattern) +{ + /* maildir code itself doesn't care about this, but we may get here + from listing subscriptions to LAYOUT=fs namespace containing + entries for a subscriptions=no LAYOUT=maildir++ namespace */ + return maildir_is_valid_existing_name(list, pattern); +} + +static bool maildir_is_valid_create_name(struct mailbox_list *_list, const char *name) { struct maildir_mailbox_list *list = @@ -477,7 +476,7 @@ i_array_init(&names_arr, 64); old_vname = mailbox_list_get_vname(oldlist, oldname); - old_vnamelen = strlen(oldname); + old_vnamelen = strlen(old_vname); new_vname = mailbox_list_get_vname(newlist, newname);
--- a/src/lib-storage/list/mailbox-list-none.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-none.c Tue Feb 22 15:31:31 2011 +0200 @@ -8,6 +8,12 @@ #define MAILBOX_LIST_NAME_NONE "none" #define GLOBAL_TEMP_PREFIX ".temp." +struct noop_list_iterate_context { + struct mailbox_list_iterate_context ctx; + struct mailbox_info inbox_info; + unsigned int list_inbox:1; +}; + extern struct mailbox_list none_mailbox_list; static struct mailbox_list *none_list_alloc(void) @@ -126,15 +132,20 @@ const char *const *patterns, enum mailbox_list_iter_flags flags) { - struct mailbox_list_iterate_context *ctx; + struct noop_list_iterate_context *ctx; - ctx = i_new(struct mailbox_list_iterate_context, 1); - ctx->list = list; - ctx->flags = flags; - ctx->glob = imap_match_init_multiple(default_pool, patterns, TRUE, - mail_namespace_get_sep(list->ns)); - array_create(&ctx->module_contexts, default_pool, sizeof(void *), 5); - return ctx; + ctx = i_new(struct noop_list_iterate_context, 1); + ctx->ctx.list = list; + ctx->ctx.flags = flags; + ctx->ctx.glob = imap_match_init_multiple(default_pool, patterns, TRUE, + mail_namespace_get_sep(list->ns)); + array_create(&ctx->ctx.module_contexts, default_pool, sizeof(void *), 5); + if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { + ctx->list_inbox = TRUE; + ctx->inbox_info.ns = list->ns; + ctx->inbox_info.name = "INBOX"; + } + return &ctx->ctx; } static int @@ -147,8 +158,15 @@ } static const struct mailbox_info * -none_list_iter_next(struct mailbox_list_iterate_context *ctx ATTR_UNUSED) +none_list_iter_next(struct mailbox_list_iterate_context *_ctx) { + struct noop_list_iterate_context *ctx = + (struct noop_list_iterate_context *)_ctx; + + if (ctx->list_inbox) { + ctx->list_inbox = FALSE; + return &ctx->inbox_info; + } return NULL; }
--- a/src/lib-storage/mail-storage-service.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/mail-storage-service.c Tue Feb 22 15:31:31 2011 +0200 @@ -58,7 +58,7 @@ pool_t pool; struct mail_storage_service_input input; - const char *system_groups_user; + const char *system_groups_user, *uid_source, *gid_source; const struct mail_user_settings *user_set; const struct setting_parser_info *user_info; struct setting_parser_context *set_parser; @@ -178,10 +178,13 @@ *error_r = "userdb returned 0 as uid"; return -1; } + user->uid_source = "userdb lookup"; set_keyval(ctx, user, "mail_uid", dec2str(reply->uid)); } - if (reply->gid != (uid_t)-1) + if (reply->gid != (uid_t)-1) { + user->gid_source = "userdb lookup"; set_keyval(ctx, user, "mail_gid", dec2str(reply->gid)); + } if (home != NULL && chroot == NULL && *user->user_set->valid_chroot_dirs != '\0' && @@ -299,8 +302,8 @@ } static int -service_drop_privileges(const struct mail_user_settings *set, - const char *system_groups_user, +service_drop_privileges(struct mail_storage_service_user *user, + const struct mail_user_settings *set, const char *home, const char *chroot, bool disallow_root, bool keep_setuid_root, bool setenv_only, const char **error_r) @@ -327,6 +330,7 @@ dec2str(rset.uid)); return -1; } + rset.uid_source = user->uid_source; } else if (rset.uid == (uid_t)-1 && disallow_root && current_euid == 0) { *error_r = "User is missing UID (see mail_uid setting)"; @@ -347,6 +351,7 @@ dec2str(rset.gid)); return -1; } + rset.gid_source = user->gid_source; } else if (rset.gid == (gid_t)-1 && disallow_root && set->first_valid_gid > 0 && getegid() == 0) { *error_r = "User is missing GID (see mail_gid setting)"; @@ -370,7 +375,7 @@ /* we can't chroot if we want to switch between users. there's not much point either (from security point of view) */ rset.chroot_dir = *chroot == '\0' || keep_setuid_root ? NULL : chroot; - rset.system_groups_user = system_groups_user; + rset.system_groups_user = user->system_groups_user; cur_chroot = restrict_access_get_current_chroot(); if (cur_chroot != NULL) { @@ -647,6 +652,7 @@ memset(&set_input, 0, sizeof(set_input)); set_input.roots = ctx->set_roots; + set_input.preserve_user = TRUE; /* settings reader may exec doveconf, which is going to clear environment, and if we're not doing a userdb lookup we want to use $HOME */ @@ -820,6 +826,8 @@ i_panic("settings_parser_check() failed: %s", error); user->user_set = settings_parser_get_list(user->set_parser)[1]; + user->gid_source = "mail_gid setting"; + user->uid_source = "mail_uid setting"; if (!userdb_lookup) { const char *home = getenv("HOME"); @@ -891,17 +899,21 @@ } else if (len > 0 && temp_priv_drop) { /* we're dropping privileges only temporarily, so we can't chroot. fix home directory so we can access it. */ - set_keyval(ctx, user, "mail_home", - t_strconcat(chroot, "/", home, NULL)); + if (*home == '\0' || strcmp(home, "/") == 0) + home = chroot; + else + home = t_strconcat(chroot, home, NULL); + chroot = ""; + set_keyval(ctx, user, "mail_home", home); } if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) mail_storage_service_init_log(ctx, user); if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) { - if (service_drop_privileges(user_set, user->system_groups_user, - home, chroot, disallow_root, - temp_priv_drop, FALSE, &error) < 0) { + if (service_drop_privileges(user, user_set, home, chroot, + disallow_root, temp_priv_drop, + FALSE, &error) < 0) { i_error("user %s: Couldn't drop privileges: %s", user->input.username, error); return -1; @@ -935,8 +947,8 @@ chroot = user_expand_varstr(ctx->service, &user->input, user_set->mail_chroot); - if (service_drop_privileges(user_set, user->system_groups_user, - home, chroot, FALSE, FALSE, TRUE, + if (service_drop_privileges(user, user_set, home, chroot, + FALSE, FALSE, TRUE, &error) < 0) i_fatal("%s", error); }
--- a/src/lib-storage/mail-storage.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/mail-storage.c Tue Feb 22 15:31:31 2011 +0200 @@ -948,8 +948,9 @@ "Can't rename mailboxes across specified storages."); return -1; } - if (src->list->ns->type != NAMESPACE_PRIVATE || - dest->list->ns->type != NAMESPACE_PRIVATE) { + if (src->list != dest->list && + (src->list->ns->type != NAMESPACE_PRIVATE || + dest->list->ns->type != NAMESPACE_PRIVATE)) { mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, "Renaming not supported across non-private namespaces."); return -1; @@ -1445,6 +1446,12 @@ mailbox_keywords_unref(&keywords); } +struct mailbox_transaction_context * +mailbox_save_get_transaction(struct mail_save_context *ctx) +{ + return ctx->transaction; +} + int mailbox_copy(struct mail_save_context **_ctx, struct mail *mail) { struct mail_save_context *ctx = *_ctx;
--- a/src/lib-storage/mail-storage.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/mail-storage.h Tue Feb 22 15:31:31 2011 +0200 @@ -606,6 +606,9 @@ int mailbox_save_finish(struct mail_save_context **ctx); void mailbox_save_cancel(struct mail_save_context **ctx); +struct mailbox_transaction_context * +mailbox_save_get_transaction(struct mail_save_context *ctx); + /* Copy the given message. You'll need to specify the flags etc. using the mailbox_save_*() functions. */ int mailbox_copy(struct mail_save_context **ctx, struct mail *mail);
--- a/src/lib-storage/mailbox-list.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib-storage/mailbox-list.c Tue Feb 22 15:31:31 2011 +0200 @@ -264,6 +264,12 @@ *error_r = t_strconcat(error, "mail root dir in: ", data, NULL); return -1; } + if (strncmp(set_r->root_dir, "INBOX=", 6) == 0) { + /* probably mbox user trying to avoid root_dir */ + *error_r = t_strconcat("Mail root directory not given: ", + data, NULL); + return -1; + } while (*tmp != NULL) { str = split_next_arg(&tmp);
--- a/src/lib/file-dotlock.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/file-dotlock.c Tue Feb 22 15:31:31 2011 +0200 @@ -316,11 +316,12 @@ } static int try_create_lock_hardlink(struct lock_info *lock_info, bool write_pid, - string_t *tmp_path) + string_t *tmp_path, time_t now) { const char *temp_prefix = lock_info->set->temp_prefix; const char *p; mode_t old_mask; + struct stat st; if (lock_info->temp_path == NULL) { /* we'll need our temp file first. */ @@ -366,6 +367,14 @@ } lock_info->temp_path = str_c(tmp_path); + } else if (fstat(lock_info->fd, &st) < 0) { + i_error("fstat(%s) failed: %m", lock_info->temp_path); + return -1; + } else if (st.st_ctime < now) { + /* we've been waiting for a while. + refresh the file's timestamp. */ + if (utime(lock_info->temp_path, NULL) < 0) + i_error("utime(%s) failed: %m", lock_info->temp_path); } if (nfs_safe_link(lock_info->temp_path, @@ -506,6 +515,7 @@ lock_info.wait_usecs += lock_info.wait_usecs/2; } dotlock_wait(&lock_info); + now = time(NULL); } ret = check_lock(now, &lock_info); @@ -519,7 +529,7 @@ ret = set->use_excl_lock ? try_create_lock_excl(&lock_info, write_pid) : try_create_lock_hardlink(&lock_info, write_pid, - tmp_path); + tmp_path, now); if (ret != 0) break; }
--- a/src/lib/istream-limit.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/istream-limit.c Tue Feb 22 15:31:31 2011 +0200 @@ -74,10 +74,6 @@ static void i_stream_limit_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { - struct limit_istream *lstream = (struct limit_istream *) stream; - - i_assert(v_offset <= lstream->v_size); - stream->istream.v_offset = v_offset; stream->skip = stream->pos = 0; }
--- a/src/lib/istream-seekable.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/istream-seekable.c Tue Feb 22 15:31:31 2011 +0200 @@ -132,7 +132,7 @@ /* full / error */ sstream->istream.istream.stream_errno = sstream->cur_input->stream_errno; - return ret; + return -1; } /* go to next stream */ @@ -157,8 +157,9 @@ const unsigned char *data; size_t size, pos, offset; - if (stream->istream.v_offset + - (stream->pos - stream->skip) >= sstream->buffer->used) { + i_assert(stream->skip == 0); + + if (stream->istream.v_offset + stream->pos >= sstream->buffer->used) { /* need to read more */ if (sstream->buffer->used >= stream->max_buffer_size) return FALSE; @@ -176,6 +177,7 @@ /* we should have more now. */ data = i_stream_get_data(sstream->cur_input, &size); + i_assert(size > 0); buffer_append(sstream->buffer, data, size); i_stream_skip(sstream->cur_input, size); }
--- a/src/lib/istream.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/istream.c Tue Feb 22 15:31:31 2011 +0200 @@ -136,6 +136,7 @@ break; default: i_assert(ret > 0); + i_assert(_stream->skip < _stream->pos); i_assert((size_t)ret+old_size == _stream->pos - _stream->skip); break; }
--- a/src/lib/mkdir-parents.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/mkdir-parents.c Tue Feb 22 15:31:31 2011 +0200 @@ -33,12 +33,16 @@ return -1; } if (chown(path, uid, gid) < 0) { + orig_errno = errno; + if (rmdir(path) < 0) + i_error("rmdir(%s) failed: %m", path); + errno = orig_errno; + if (errno == EPERM && uid == (uid_t)-1) { i_error("%s", eperm_error_get_chgrp("chown", path, gid, gid_origin)); return -1; } - orig_errno = errno; str = t_str_new(256); str_printfa(str, "chown(%s, %ld", path,
--- a/src/lib/module-dir.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/module-dir.c Tue Feb 22 15:31:31 2011 +0200 @@ -432,7 +432,7 @@ /* make sure all modules were found */ for (; *module_names != NULL; module_names++) { if (**module_names != '\0') { - i_fatal("Plugin %s not found from directory %s", + i_fatal("Plugin '%s' not found from directory %s", *module_names, dir); } }
--- a/src/lib/restrict-access.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/restrict-access.c Tue Feb 22 15:31:31 2011 +0200 @@ -6,6 +6,7 @@ #include <unistd.h> #include "lib.h" +#include "str.h" #include "restrict-access.h" #include "env-util.h" @@ -61,24 +62,31 @@ return ret; } -static void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid) +static void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid, + const char *gid_source) { + string_t *str; + if (privileged_gid == (gid_t)-1) { if (primary_gid == getgid() && primary_gid == getegid()) { /* everything is already set */ return; } - if (setgid(primary_gid) != 0) { - i_fatal("setgid(%s) failed with " - "euid=%s, gid=%s, egid=%s: %m " - "(This binary should probably be called with " - "process group set to %s instead of %s)", - get_gid_str(primary_gid), get_uid_str(geteuid()), - get_gid_str(getgid()), get_gid_str(getegid()), - get_gid_str(primary_gid), get_gid_str(getegid())); - } - return; + if (setgid(primary_gid) == 0) + return; + + str = t_str_new(128); + str_printfa(str, "setgid(%s", get_gid_str(primary_gid)); + if (gid_source != NULL) + str_printfa(str, " from %s", gid_source); + str_printfa(str, ") failed with euid=%s, gid=%s, egid=%s: %m " + "(This binary should probably be called with " + "process group set to %s instead of %s)", + get_uid_str(geteuid()), + get_gid_str(getgid()), get_gid_str(getegid()), + get_gid_str(primary_gid), get_gid_str(getegid())); + i_fatal("%s", str_c(str)); } if (getegid() != 0 && primary_gid == getgid() && @@ -245,7 +253,7 @@ if (process_primary_gid == (gid_t)-1) process_primary_gid = getegid(); restrict_init_groups(process_primary_gid, - process_privileged_gid); + process_privileged_gid, set->gid_source); } else { if (process_primary_gid == (gid_t)-1) process_primary_gid = getegid(); @@ -295,11 +303,17 @@ /* uid last */ if (set->uid != (uid_t)-1) { if (setuid(set->uid) != 0) { - i_fatal("setuid(%s) failed with euid=%s: %m " + string_t *str = t_str_new(128); + + str_printfa(str, "setuid(%s", get_uid_str(set->uid)); + if (set->uid_source != NULL) + str_printfa(str, " from %s", set->uid_source); + str_printfa(str, ") failed with euid=%s: %m " "(This binary should probably be called with " "process user set to %s instead of %s)", - get_uid_str(set->uid), get_uid_str(geteuid()), + get_uid_str(geteuid()), get_uid_str(set->uid), get_uid_str(geteuid())); + i_fatal("%s", str_c(str)); } }
--- a/src/lib/restrict-access.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/restrict-access.h Tue Feb 22 15:31:31 2011 +0200 @@ -19,6 +19,10 @@ group user contains other GIDs, they're silently dropped. */ gid_t first_valid_gid, last_valid_gid; + /* Human readable "source" of UID and GID values. If non-NULL, + displayed on error messages about failing to change uid/gid. */ + const char *uid_source, *gid_source; + /* Chroot directory */ const char *chroot_dir; };
--- a/src/lib/unichar.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/unichar.c Tue Feb 22 15:31:31 2011 +0200 @@ -388,3 +388,10 @@ return uni_utf8_find_invalid_pos((const unsigned char *)str, strlen(str), &i) == 0; } + +bool uni_utf8_data_is_valid(const unsigned char *data, size_t size) +{ + size_t i; + + return uni_utf8_find_invalid_pos(data, size, &i) == 0; +}
--- a/src/lib/unichar.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lib/unichar.h Tue Feb 22 15:31:31 2011 +0200 @@ -75,5 +75,7 @@ buffer_t *buf); /* Returns TRUE if string is valid UTF-8 input. */ bool uni_utf8_str_is_valid(const char *str); +/* Returns TRUE if data contains only valid UTF-8 input. */ +bool uni_utf8_data_is_valid(const unsigned char *data, size_t size); #endif
--- a/src/lmtp/client.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lmtp/client.c Tue Feb 22 15:31:31 2011 +0200 @@ -231,7 +231,7 @@ client_raw_user_create(client); client_generate_session_id(client); client->my_domain = client->set->hostname; - client->state.lhlo = "missing"; + client->lhlo = i_strdup("missing"); DLLIST_PREPEND(&clients, client); clients_count++; @@ -265,6 +265,7 @@ if (client->fd_in != client->fd_out) net_disconnect(client->fd_out); client_state_reset(client); + i_free(client->lhlo); pool_unref(&client->state_pool); pool_unref(&client->pool);
--- a/src/lmtp/client.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lmtp/client.h Tue Feb 22 15:31:31 2011 +0200 @@ -12,7 +12,6 @@ }; struct client_state { - const char *lhlo; const char *session_id; const char *mail_from; ARRAY_DEFINE(rcpt_to, struct mail_recipient); @@ -58,6 +57,7 @@ struct mail_user *raw_mail_user; const char *my_domain; + char *lhlo; pool_t state_pool; struct client_state state;
--- a/src/lmtp/commands.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/lmtp/commands.c Tue Feb 22 15:31:31 2011 +0200 @@ -71,7 +71,8 @@ client_send_line(client, "250-ENHANCEDSTATUSCODES"); client_send_line(client, "250 PIPELINING"); - client->state.lhlo = p_strdup(client->state_pool, str_c(domain)); + i_free(client->lhlo); + client->lhlo = i_strdup(str_c(domain)); return 0; } @@ -323,7 +324,7 @@ static void rcpt_address_parse(struct client *client, const char *address, const char **username_r, const char **detail_r) { - const char *p, *p2; + const char *p, *domain; *username_r = address; *detail_r = ""; @@ -331,16 +332,16 @@ if (*client->set->recipient_delimiter == '\0') return; + domain = strchr(address, '@'); p = strstr(address, client->set->recipient_delimiter); - if (p != NULL) { + if (p != NULL && (domain == NULL || p < domain)) { /* user+detail@domain */ *username_r = t_strdup_until(*username_r, p); - p2 = strchr(p, '@'); - if (p2 == NULL) + if (domain == NULL) *detail_r = p+1; else { - *detail_r = t_strdup_until(p+1, p2); - *username_r = t_strconcat(*username_r, p2, NULL); + *detail_r = t_strdup_until(p+1, domain); + *username_r = t_strconcat(*username_r, domain, NULL); } } } @@ -446,7 +447,7 @@ static int client_deliver(struct client *client, const struct mail_recipient *rcpt, - struct mail *src_mail) + struct mail *src_mail, struct mail_deliver_session *session) { struct mail_deliver_context dctx; struct mail_storage *storage; @@ -470,7 +471,8 @@ sets = mail_storage_service_user_get_set(rcpt->service_user); memset(&dctx, 0, sizeof(dctx)); - dctx.pool = pool_alloconly_create("mail delivery", 1024); + dctx.session = session; + dctx.pool = session->pool; dctx.set = sets[1]; dctx.session_id = client->state.session_id; dctx.src_mail = src_mail; @@ -516,11 +518,11 @@ } ret = -1; } - pool_unref(&dctx.pool); return ret; } -static bool client_deliver_next(struct client *client, struct mail *src_mail) +static bool client_deliver_next(struct client *client, struct mail *src_mail, + struct mail_deliver_session *session) { const struct mail_recipient *rcpts; unsigned int count; @@ -529,7 +531,7 @@ rcpts = array_get(&client->state.rcpt_to, &count); while (client->state.rcpt_idx < count) { ret = client_deliver(client, &rcpts[client->state.rcpt_idx], - src_mail); + src_mail, session); i_set_failure_prefix(t_strdup_printf("lmtp(%s): ", my_pid)); client->state.rcpt_idx++; @@ -618,15 +620,17 @@ static void client_input_data_write_local(struct client *client, struct istream *input) { + struct mail_deliver_session *session; struct mail *src_mail; uid_t old_uid, first_uid = (uid_t)-1; if (client_open_raw_mail(client, input) < 0) return; + session = mail_deliver_session_init(); old_uid = geteuid(); src_mail = client->state.raw_mail; - while (client_deliver_next(client, src_mail)) { + while (client_deliver_next(client, src_mail, session)) { if (client->state.first_saved_mail == NULL || client->state.first_saved_mail == src_mail) mail_user_unref(&client->state.dest_user); @@ -639,6 +643,7 @@ i_assert(first_uid != 0); } } + mail_deliver_session_deinit(&session); if (client->state.first_saved_mail != NULL) { struct mail *mail = client->state.first_saved_mail; @@ -695,30 +700,28 @@ static const char *client_get_added_headers(struct client *client) { string_t *str = t_str_new(200); - const char *host, *address = NULL, *username = NULL; + const char *host, *rcpt_to = NULL; if (array_count(&client->state.rcpt_to) == 1) { const struct mail_recipient *rcpt = array_idx(&client->state.rcpt_to, 0); - const char *detail; - address = rcpt->address; - rcpt_address_parse(client, address, &username, &detail); + rcpt_to = rcpt->address; } str_printfa(str, "Return-Path: <%s>\r\n", client->state.mail_from); - if (username != NULL) - str_printfa(str, "Delivered-To: <%s>\r\n", username); + if (rcpt_to != NULL) + str_printfa(str, "Delivered-To: <%s>\r\n", rcpt_to); - str_printfa(str, "Received: from %s", client->state.lhlo); + str_printfa(str, "Received: from %s", client->lhlo); if ((host = net_ip2addr(&client->remote_ip)) != NULL) str_printfa(str, " ([%s])", host); str_printfa(str, "\r\n\tby %s ("PACKAGE_NAME") with LMTP id %s", client->my_domain, client->state.session_id); str_append(str, "\r\n\t"); - if (address != NULL) - str_printfa(str, "for <%s>", address); + if (rcpt_to != NULL) + str_printfa(str, "for <%s>", rcpt_to); str_printfa(str, "; %s\r\n", message_date_create(ioloop_time)); return str_c(str); }
--- a/src/login-common/client-common.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/login-common/client-common.c Tue Feb 22 15:31:31 2011 +0200 @@ -497,7 +497,8 @@ const char *client_get_extra_disconnect_reason(struct client *client) { - if (client->set->ssl_require_client_cert && client->ssl_proxy != NULL) { + if (client->set->auth_ssl_require_client_cert && + client->ssl_proxy != NULL) { if (ssl_proxy_has_broken_client_cert(client->ssl_proxy)) return "(client sent an invalid cert)"; if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy)) @@ -510,7 +511,7 @@ /* some auth attempts without SSL/TLS */ if (client->auth_tried_disabled_plaintext) return "(tried to use disabled plaintext auth)"; - if (client->set->ssl_require_client_cert) + if (client->set->auth_ssl_require_client_cert) return "(cert required, client didn't start TLS)"; if (client->auth_tried_unsupported_mech) return "(tried to use unsupported auth mechanism)";
--- a/src/login-common/login-settings.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/login-common/login-settings.c Tue Feb 22 15:31:31 2011 +0200 @@ -34,8 +34,8 @@ DEF(SET_STR, ssl_cipher_list), DEF(SET_STR, ssl_cert_username_field), DEF(SET_BOOL, ssl_verify_client_cert), - DEF(SET_BOOL, ssl_require_client_cert), - DEF(SET_BOOL, ssl_username_from_cert), + DEF(SET_BOOL, auth_ssl_require_client_cert), + DEF(SET_BOOL, auth_ssl_username_from_cert), DEF(SET_BOOL, verbose_ssl), DEF(SET_BOOL, disable_plaintext_auth), @@ -64,8 +64,8 @@ .ssl_cipher_list = "ALL:!LOW:!SSLv2:!EXP:!aNULL", .ssl_cert_username_field = "commonName", .ssl_verify_client_cert = FALSE, - .ssl_require_client_cert = FALSE, - .ssl_username_from_cert = FALSE, + .auth_ssl_require_client_cert = FALSE, + .auth_ssl_username_from_cert = FALSE, .verbose_ssl = FALSE, .disable_plaintext_auth = TRUE, @@ -131,7 +131,8 @@ set->log_format_elements_split = p_strsplit(pool, set->login_log_format_elements, " "); - if (set->ssl_require_client_cert || set->ssl_username_from_cert) { + if (set->auth_ssl_require_client_cert || + set->auth_ssl_username_from_cert) { /* if we require valid cert, make sure we also ask for it */ set->ssl_verify_client_cert = TRUE; }
--- a/src/login-common/login-settings.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/login-common/login-settings.h Tue Feb 22 15:31:31 2011 +0200 @@ -16,8 +16,8 @@ const char *ssl_cipher_list; const char *ssl_cert_username_field; bool ssl_verify_client_cert; - bool ssl_require_client_cert; - bool ssl_username_from_cert; + bool auth_ssl_require_client_cert; + bool auth_ssl_username_from_cert; bool verbose_ssl; bool disable_plaintext_auth;
--- a/src/master/main.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/master/main.c Tue Feb 22 15:31:31 2011 +0200 @@ -48,7 +48,6 @@ static char *pidfile_path; static failure_callback_t *orig_fatal_callback; static failure_callback_t *orig_error_callback; -static const char *child_process_env[3]; /* @UNSAFE */ static const struct setting_parser_info *set_roots[] = { &master_setting_parser_info, @@ -314,8 +313,7 @@ sets = master_service_settings_get_others(master_service); set = sets[0]; - if (services_create(set, child_process_env, - &new_services, &error) < 0) { + if (services_create(set, &new_services, &error) < 0) { /* new configuration is invalid, keep the old */ i_error("Config reload failed: %s", error); return; @@ -377,12 +375,39 @@ input.roots = set_roots; input.module = "master"; input.parse_full_config = TRUE; + input.preserve_environment = TRUE; if (master_service_settings_read(master_service, &input, &output, &error) < 0) i_fatal("Error reading configuration: %s", error); return master_service_settings_get_others(master_service)[0]; } +static void master_set_import_environment(const struct master_settings *set) +{ + const char *const *envs, *key, *value; + ARRAY_TYPE(const_string) keys; + + if (*set->import_environment == '\0') + return; + + t_array_init(&keys, 8); + envs = t_strsplit_spaces(set->import_environment, " "); + for (; *envs != NULL; envs++) { + value = strchr(*envs, '='); + if (value == NULL) + key = *envs; + else { + key = t_strdup_until(*envs, value); + env_put(*envs); + } + array_append(&keys, &key, 1); + } + (void)array_append_space(&keys); + + value = t_strarray_join(array_idx(&keys, 0), " "); + env_put(t_strconcat(DOVECOT_PRESERVE_ENVS_ENV"=", value, NULL)); +} + static void main_log_startup(void) { #define STARTUP_STRING PACKAGE_NAME" v"DOVECOT_VERSION_FULL" starting up" @@ -598,18 +623,8 @@ int main(int argc, char *argv[]) { - static const char *preserve_envs[] = { - /* AIX depends on TZ to get the timezone correctly. */ - "TZ", -#ifdef HAVE_SYSTEMD - "LISTEN_PID", - "LISTEN_FDS", -#endif - NULL - }; struct master_settings *set; - unsigned int child_process_env_idx = 0; - const char *error, *env_tz, *doveconf_arg = NULL; + const char *error, *doveconf_arg = NULL; failure_callback_t *orig_info_callback, *orig_debug_callback; bool foreground = FALSE, ask_key_pass = FALSE; bool doubleopts[argc]; @@ -618,8 +633,6 @@ #ifdef DEBUG if (getenv("GDB") == NULL) fd_debug_verify_leaks(3, 1024); - else - child_process_env[child_process_env_idx++] = "GDB=1"; #endif /* drop -- prefix from all --args. ugly, but the only way that it works with standard getopt() in all OSes.. */ @@ -740,23 +753,16 @@ master_settings_do_fixes(set); fatal_log_check(set); - /* clean up the environment */ - env_clean_except(preserve_envs); - - env_tz = getenv("TZ"); - if (env_tz != NULL) { - child_process_env[child_process_env_idx++] = - t_strconcat("TZ=", env_tz, NULL); - } - i_assert(child_process_env_idx < - sizeof(child_process_env) / sizeof(child_process_env[0])); - child_process_env[child_process_env_idx] = NULL; + T_BEGIN { + master_set_import_environment(set); + } T_END; + master_service_env_clean(); /* create service structures from settings. if there are any errors in service configuration we'll catch it here. */ service_pids_init(); service_anvil_global_init(); - if (services_create(set, child_process_env, &services, &error) < 0) + if (services_create(set, &services, &error) < 0) i_fatal("%s", error); services->config->config_file_path = get_full_config_path(services); @@ -765,14 +771,17 @@ if (services_listen(services) <= 0) i_fatal("Failed to start listeners"); - if (!foreground) - daemonize(); if (chdir(set->base_dir) < 0) i_fatal("chdir(%s) failed: %m", set->base_dir); + if (dup2(null_fd, STDERR_FILENO) < 0) + i_fatal("dup2(null_fd) failed: %m"); i_set_fatal_handler(master_fatal_callback); i_set_error_handler(orig_error_callback); + if (!foreground) + daemonize(); + main_init(set); master_service_run(master_service, NULL); main_deinit();
--- a/src/master/master-settings.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/master/master-settings.c Tue Feb 22 15:31:31 2011 +0200 @@ -170,6 +170,7 @@ static const struct setting_define master_setting_defines[] = { DEF(SET_STR, base_dir), DEF(SET_STR, libexec_dir), + DEF(SET_STR, import_environment), DEF(SET_STR, protocols), DEF(SET_STR, listen), DEF(SET_ENUM, ssl), @@ -192,9 +193,23 @@ SETTING_DEFINE_LIST_END }; +/* <settings checks> */ +#ifdef HAVE_SYSTEMD +# define ENV_SYSTEMD " LISTEN_PID LISTEN_FDS" +#else +# define ENV_SYSTEMD "" +#endif +#ifdef DEBUG +# define ENV_GDB " GDB" +#else +# define ENV_GDB "" +#endif +/* </settings checks> */ + static const struct master_settings master_default_settings = { .base_dir = PKG_RUNDIR, .libexec_dir = PKG_LIBEXECDIR, + .import_environment = "TZ" ENV_SYSTEMD ENV_GDB, .protocols = "imap pop3 lmtp", .listen = "*, ::", .ssl = "yes:no:required",
--- a/src/master/master-settings.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/master/master-settings.h Tue Feb 22 15:31:31 2011 +0200 @@ -6,6 +6,7 @@ struct master_settings { const char *base_dir; const char *libexec_dir; + const char *import_environment; const char *protocols; const char *listen; const char *ssl;
--- a/src/master/service-monitor.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/master/service-monitor.c Tue Feb 22 15:31:31 2011 +0200 @@ -195,8 +195,11 @@ service_throttle(service, SERVICE_STARTUP_FAILURE_THROTTLE_SECS); } -static void service_drop_connections(struct service *service) +static void service_drop_connections(struct service_listener *l) { + struct service *service = l->service; + int fd; + if (service->last_drop_warning + SERVICE_DROP_WARN_INTERVAL_SECS < ioloop_time) { service->last_drop_warning = ioloop_time; @@ -206,25 +209,35 @@ service->process_limit > 1 ? "process_limit" : "client_limit"); } - service->listen_pending = TRUE; - service_monitor_listen_stop(service); if (service->type == SERVICE_TYPE_LOGIN) { /* reached process limit, notify processes that they need to start killing existing connections if they reach connection limit */ service_login_notify(service, TRUE); + + service->listen_pending = TRUE; + service_monitor_listen_stop(service); + } else { + /* just accept and close the connection, so it's clear that + this is happening because of the limit, rather than because + the service processes aren't answering fast enough */ + fd = net_accept(l->fd, NULL, NULL); + if (fd > 0) + net_disconnect(fd); } } -static void service_accept(struct service *service) +static void service_accept(struct service_listener *l) { + struct service *service = l->service; + i_assert(service->process_avail == 0); if (service->process_count == service->process_limit) { /* we've reached our limits, new clients will have to wait until there are more processes available */ - service_drop_connections(service); + service_drop_connections(l); return; } @@ -274,7 +287,7 @@ struct service_listener *l = *listeners; if (l->io == NULL && l->fd != -1) - l->io = io_add(l->fd, IO_READ, service_accept, service); + l->io = io_add(l->fd, IO_READ, service_accept, l); } }
--- a/src/master/service-process.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/master/service-process.c Tue Feb 22 15:31:31 2011 +0200 @@ -180,12 +180,8 @@ service_process_setup_environment(struct service *service, unsigned int uid) { const struct master_service_settings *set = service->list->service_set; - const char *const *p; - /* remove all environment, and put back what we need */ - env_clean(); - for (p = service->list->child_process_env; *p != NULL; p++) - env_put(*p); + master_service_env_clean(); switch (service->type) { case SERVICE_TYPE_CONFIG:
--- a/src/master/service.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/master/service.c Tue Feb 22 15:31:31 2011 +0200 @@ -427,7 +427,6 @@ } int services_create(const struct master_settings *set, - const char *const *child_process_env, struct service_list **services_r, const char **error_r) { struct service_list *service_list; @@ -445,7 +444,6 @@ service_list->service_set = master_service_settings_get(master_service); service_list->set_pool = master_service_settings_detach(master_service); service_list->set = set; - service_list->child_process_env = child_process_env; service_list->master_log_fd[0] = -1; service_list->master_log_fd[1] = -1;
--- a/src/master/service.h Sat Feb 19 16:56:21 2011 +0200 +++ b/src/master/service.h Tue Feb 22 15:31:31 2011 +0200 @@ -114,7 +114,6 @@ struct service *config; struct service *log; struct service *anvil; - const char *const *child_process_env; /* nonblocking log fds usd by master */ int master_log_fd[2]; @@ -131,7 +130,6 @@ /* Create all services from settings */ int services_create(const struct master_settings *set, - const char *const *child_process_env, struct service_list **services_r, const char **error_r); /* Destroy services */
--- a/src/plugins/expire/doveadm-expire.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/plugins/expire/doveadm-expire.c Tue Feb 22 15:31:31 2011 +0200 @@ -64,7 +64,8 @@ static bool doveadm_expire_mail_want(struct doveadm_mail_cmd_context *ctx, - const char *key, time_t stamp, const char **username_r) + const char *key, time_t oldest_savedate, + const char **username_r) { struct doveadm_expire_mail_cmd_context *ectx = DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx); @@ -76,6 +77,7 @@ mailbox = strchr(username, '/'); if (mailbox == NULL) { /* invalid record, ignore */ + i_error("expire: Invalid key: %s", key); return FALSE; } username = t_strdup_until(username, mailbox++); @@ -85,7 +87,8 @@ return FALSE; } - if (!doveadm_expire_mail_match_mailbox(ectx, mailbox, stamp)) { + if (!doveadm_expire_mail_match_mailbox(ectx, mailbox, + oldest_savedate)) { /* this mailbox doesn't have any matching messages */ return FALSE; } @@ -103,19 +106,27 @@ struct doveadm_expire_mail_cmd_context *ectx = DOVEADM_EXPIRE_MAIL_CMD_CONTEXT(ctx); const char *key, *value; - unsigned long stamp; + unsigned long oldest_savedate; bool ret; while (dict_iterate(ectx->iter, &key, &value)) { - if (str_to_ulong(value, &stamp) < 0) { + if (str_to_ulong(value, &oldest_savedate) < 0) { /* invalid record */ + i_error("expire: Invalid timestamp: %s", value); continue; } - if ((time_t)stamp > ectx->oldest_before_time) + if ((time_t)oldest_savedate > ectx->oldest_before_time) { + if (doveadm_debug) { + i_debug("expire: Stopping iteration on key %s " + "(%lu > %ld)", key, oldest_savedate, + (long)ectx->oldest_before_time); + } break; + } T_BEGIN { - ret = doveadm_expire_mail_want(ctx, key, stamp, + ret = doveadm_expire_mail_want(ctx, key, + oldest_savedate, username_r); } T_END; if (ret)
--- a/src/plugins/virtual/virtual-save.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/plugins/virtual/virtual-save.c Tue Feb 22 15:31:31 2011 +0200 @@ -21,18 +21,20 @@ struct mailbox_transaction_context *backend_trans; struct virtual_save_context *ctx; - if (_t->save_ctx != NULL) - return _t->save_ctx; - - ctx = i_new(struct virtual_save_context, 1); - ctx->ctx.transaction = &t->t; + if (_t->save_ctx == NULL) { + ctx = i_new(struct virtual_save_context, 1); + ctx->ctx.transaction = &t->t; + _t->save_ctx = &ctx->ctx; + } else { + ctx = (struct virtual_save_context *)_t->save_ctx; + } if (mbox->save_bbox != NULL) { + i_assert(ctx->backend_save_ctx == NULL); backend_trans = virtual_transaction_get(_t, mbox->save_bbox->box); ctx->backend_save_ctx = mailbox_save_alloc(backend_trans); } - _t->save_ctx = &ctx->ctx; return _t->save_ctx; }
--- a/src/plugins/zlib/Makefile.am Sat Feb 19 16:56:21 2011 +0200 +++ b/src/plugins/zlib/Makefile.am Tue Feb 22 15:31:31 2011 +0200 @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \
--- a/src/plugins/zlib/doveadm-zlib.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/plugins/zlib/doveadm-zlib.c Tue Feb 22 15:31:31 2011 +0200 @@ -1,9 +1,13 @@ /* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "network.h" #include "istream.h" +#include "ostream.h" #include "istream-zlib.h" +#include "ostream-zlib.h" #include "module-dir.h" +#include "master-service.h" #include "doveadm-dump.h" #include <stdio.h> @@ -12,6 +16,8 @@ const char *doveadm_zlib_plugin_version = DOVECOT_VERSION; +extern struct doveadm_cmd doveadm_cmd_zlibconnect; + void doveadm_zlib_plugin_init(struct module *module); void doveadm_zlib_plugin_deinit(void); @@ -77,15 +83,120 @@ return match; } +struct client { + int fd; + struct io *io_client, *io_server; + struct istream *input; + struct ostream *output; + bool compressed; +}; + +static void client_input(struct client *client) +{ + struct istream *input; + struct ostream *output; + unsigned char buf[1024]; + ssize_t ret; + + ret = read(STDIN_FILENO, buf, sizeof(buf)); + if (ret == 0) { + if (client->compressed) { + master_service_stop(master_service); + return; + } + /* start compression */ + i_info("<Compression started>"); + input = i_stream_create_deflate(client->input, TRUE); + output = o_stream_create_deflate(client->output, 6); + i_stream_unref(&client->input); + o_stream_unref(&client->output); + client->input = input; + client->output = output; + client->compressed = TRUE; + return; + } + if (ret < 0) + i_fatal("read(stdin) failed: %m"); + + o_stream_send(client->output, buf, ret); +} + +static void server_input(struct client *client) +{ + const unsigned char *data; + size_t size; + + if (i_stream_read(client->input) == -1) { + if (client->input->stream_errno != 0) { + errno = client->input->stream_errno; + i_fatal("read(server) failed: %m"); + } + + i_info("Server disconnected"); + master_service_stop(master_service); + return; + } + + data = i_stream_get_data(client->input, &size); + if (write(STDOUT_FILENO, data, size) < 0) + i_fatal("write(stdout) failed: %m"); + i_stream_skip(client->input, size); +} + +static void cmd_zlibconnect(int argc ATTR_UNUSED, char *argv[]) +{ + struct client client; + struct ip_addr *ips; + unsigned int ips_count, port; + int fd, ret; + + if (argv[1] == NULL || argv[2] == NULL || + str_to_uint(argv[2], &port) < 0) + help(&doveadm_cmd_zlibconnect); + + ret = net_gethostbyname(argv[1], &ips, &ips_count); + if (ret != 0) { + i_fatal("Host %s lookup failed: %s", argv[1], + net_gethosterror(ret)); + } + + if ((fd = net_connect_ip(&ips[0], port, NULL)) == -1) + i_fatal("connect(%s, %u) failed: %m", argv[1], port); + + i_info("Connected to %s port %u. Ctrl-D starts compression", + net_ip2addr(&ips[0]), port); + + memset(&client, 0, sizeof(client)); + client.fd = fd; + client.input = i_stream_create_fd(fd, (size_t)-1, FALSE); + client.output = o_stream_create_fd(fd, 0, FALSE); + client.io_client = io_add(STDIN_FILENO, IO_READ, client_input, &client); + client.io_server = io_add(fd, IO_READ, server_input, &client); + master_service_run(master_service, NULL); + io_remove(&client.io_client); + io_remove(&client.io_server); + i_stream_unref(&client.input); + o_stream_unref(&client.output); + if (close(fd) < 0) + i_fatal("close() failed: %m"); +} + struct doveadm_cmd_dump doveadm_cmd_dump_zlib = { "imapzlib", test_dump_imapzlib, cmd_dump_imapzlib }; +struct doveadm_cmd doveadm_cmd_zlibconnect = { + cmd_zlibconnect, + "zlibconnect", + "<host> [<port>]" +}; + void doveadm_zlib_plugin_init(struct module *module ATTR_UNUSED) { doveadm_dump_register(&doveadm_cmd_dump_zlib); + doveadm_register_cmd(&doveadm_cmd_zlibconnect); } void doveadm_zlib_plugin_deinit(void)
--- a/src/plugins/zlib/ostream-zlib.c Sat Feb 19 16:56:21 2011 +0200 +++ b/src/plugins/zlib/ostream-zlib.c Tue Feb 22 15:31:31 2011 +0200 @@ -129,7 +129,8 @@ } zstream->crc = crc32_data_more(zstream->crc, data, size); zstream->bytes32 += size; - zstream->flushed = FALSE; + zstream->flushed = flush == Z_SYNC_FLUSH && + zs->avail_out == sizeof(zstream->outbuf); return 0; } @@ -222,8 +223,12 @@ return -1; bytes += iov[i].iov_len; } + stream->ostream.offset += bytes; - stream->ostream.offset += bytes; + if (!zstream->ostream.corked) { + if (o_stream_zlib_send_flush(zstream) < 0) + return -1; + } return bytes; }