Mercurial > dovecot > core-2.2
changeset 14576:fbb1ecb9b888
Merged changes from v2.1 tree.
line wrap: on
line diff
--- a/configure.in Sun May 20 03:08:01 2012 +0300 +++ b/configure.in Sun May 20 03:25:04 2012 +0300 @@ -1,5 +1,5 @@ AC_PREREQ([2.59]) -AC_INIT([Dovecot],[2.1.6],[dovecot@dovecot.org]) +AC_INIT([Dovecot],[2.2.UNSTABLE],[dovecot@dovecot.org]) AC_CONFIG_SRCDIR([src]) AM_INIT_AUTOMAKE([foreign]) @@ -292,7 +292,7 @@ sys/quota.h sys/fs/ufs_quota.h ufs/ufs/quota.h jfs/quota.h sys/fs/quota_common.h \ mntent.h sys/mnttab.h sys/event.h sys/time.h sys/mkdev.h linux/dqblk_xfs.h \ xfs/xqm.h execinfo.h ucontext.h malloc_np.h sys/utsname.h sys/vmount.h \ - sys/utsname.h glob.h linux/falloc.h ucred.h) + sys/utsname.h glob.h linux/falloc.h ucred.h sys/ucred.h) dnl * clang check have_clang=no @@ -408,6 +408,8 @@ walkcontext dirfd clearenv malloc_usable_size glob fallocate \ posix_fadvise getpeereid getpeerucred) +AC_CHECK_TYPES([struct sockpeercred]) + AC_CHECK_LIB(rt, clock_gettime, [ AC_DEFINE(HAVE_CLOCK_GETTIME,, Define if you have the clock_gettime function) LIBS="$LIBS -lrt" @@ -2857,3 +2859,6 @@ if test "$not_fts" != ""; then echo " :$not_fts" fi + +echo +echo "NOTE: This is the UNSTABLE development branch of Dovecot v2.2."
--- a/doc/man/Makefile.am Sun May 20 03:08:01 2012 +0300 +++ b/doc/man/Makefile.am Sun May 20 03:25:04 2012 +0300 @@ -1,5 +1,7 @@ pkgsysconfdir = $(sysconfdir)/dovecot +SUFFIXES = .1.in .1 + dist_man1_MANS = \ deliver.1 \ doveadm-config.1 \ @@ -81,114 +83,15 @@ CLEANFILES = $(nodist_man1_MANS) -doveadm.1: $(srcdir)/doveadm.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm.1.in > doveadm.1 - -doveadm-altmove.1: $(srcdir)/doveadm-altmove.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-altmove.1.in > doveadm-altmove.1 - -doveadm-auth.1: $(srcdir)/doveadm-auth.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-auth.1.in > doveadm-auth.1 - -doveadm-director.1: $(srcdir)/doveadm-director.1.in $(man_includefiles) Makefile +.1.in.1: $(man_includefiles) Makefile $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-director.1.in > doveadm-director.1 - -doveadm-dump.1: $(srcdir)/doveadm-dump.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-dump.1.in > doveadm-dump.1 - -doveadm-expunge.1: $(srcdir)/doveadm-expunge.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-expunge.1.in > doveadm-expunge.1 - -doveadm-fetch.1: $(srcdir)/doveadm-fetch.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-fetch.1.in > doveadm-fetch.1 - -doveadm-import.1: $(srcdir)/doveadm-import.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-import.1.in > doveadm-import.1 + < $(srcdir)/$< > $@ doveadm-instance.1: $(srcdir)/doveadm-instance.1.in $(man_includefiles) Makefile $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ < $(srcdir)/doveadm-instance.1.in > doveadm-instance.1 -doveadm-index.1: $(srcdir)/doveadm-index.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-index.1.in > doveadm-index.1 - -doveadm-force-resync.1: $(srcdir)/doveadm-force-resync.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-force-resync.1.in > doveadm-force-resync.1 - -doveadm-help.1: $(srcdir)/doveadm-help.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-help.1.in > doveadm-help.1 - -doveadm-kick.1: $(srcdir)/doveadm-kick.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-kick.1.in > doveadm-kick.1 - -doveadm-log.1: $(srcdir)/doveadm-log.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-log.1.in > doveadm-log.1 - -doveadm-mailbox.1: $(srcdir)/doveadm-mailbox.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-mailbox.1.in > doveadm-mailbox.1 doveadm-mount.1: $(srcdir)/doveadm-mount.1.in $(man_includefiles) Makefile $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-mount.1.in > doveadm-mount.1 - -doveadm-move.1: $(srcdir)/doveadm-move.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-move.1.in > doveadm-move.1 - -doveadm-penalty.1: $(srcdir)/doveadm-penalty.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-penalty.1.in > doveadm-penalty.1 - -doveadm-purge.1: $(srcdir)/doveadm-purge.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-purge.1.in > doveadm-purge.1 - -doveadm-pw.1: $(srcdir)/doveadm-pw.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-pw.1.in > doveadm-pw.1 - -doveadm-quota.1: $(srcdir)/doveadm-quota.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-quota.1.in > doveadm-quota.1 - -doveadm-search.1: $(srcdir)/doveadm-search.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-search.1.in > doveadm-search.1 - -doveadm-user.1: $(srcdir)/doveadm-user.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-user.1.in > doveadm-user.1 - -doveadm-who.1: $(srcdir)/doveadm-who.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveadm-who.1.in > doveadm-who.1 - -doveconf.1: $(srcdir)/doveconf.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/doveconf.1.in > doveconf.1 - -dovecot.1: $(srcdir)/dovecot.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/dovecot.1.in > dovecot.1 - -dovecot-lda.1: $(srcdir)/dovecot-lda.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/dovecot-lda.1.in > dovecot-lda.1 - -dsync.1: $(srcdir)/dsync.1.in $(man_includefiles) Makefile - $(SHELL) $(srcdir)/sed.sh $(srcdir) $(rundir) $(pkgsysconfdir) \ - < $(srcdir)/dsync.1.in > dsync.1 + < $(srcdir)/doveadm-mount.1.in > doveadm-mount.1 \ No newline at end of file
--- a/dovecot-config.in.in Sun May 20 03:08:01 2012 +0300 +++ b/dovecot-config.in.in Sun May 20 03:25:04 2012 +0300 @@ -15,11 +15,10 @@ LIBDOVECOT_LDA_DEPS="@LIBDOVECOT_LDA@" LIBDOVECOT_STORAGE_DEPS="@LIBDOVECOT_STORAGE_DEPS@" -LIBDOVECOT_INCLUDE="-I$(incdir) -I$(incdir)/src/lib -I$(incdir)/src/lib-dict -I$(incdir)/src/lib-dns -I$(incdir)/src/lib-mail -I$(incdir)/src/lib-imap -I$(incdir)/src/lib-fs -I$(incdir)/src/lib-charset" +LIBDOVECOT_INCLUDE="-I$(incdir) -I$(incdir)/src/lib -I$(incdir)/src/lib-dict -I$(incdir)/src/lib-dns -I$(incdir)/src/lib-mail -I$(incdir)/src/lib-imap -I$(incdir)/src/lib-fs -I$(incdir)/src/lib-charset -I$(incdir)/src/lib-auth -I$(incdir)/src/lib-master -I$(incdir)/src/lib-settings" LIBDOVECOT_LDA_INCLUDE="-I$(incdir)/src/lib-lda -I$(incdir)/src/lda" -LIBDOVECOT_SERVICE_INCLUDE="-I$(incdir)/src/lib-master -I$(incdir)/src/lib-settings" LIBDOVECOT_STORAGE_INCLUDE="-I$(incdir)/src/lib-index -I$(incdir)/src/lib-storage -I$(incdir)/src/lib-storage/index -I$(incdir)/src/lib-storage/index/raw" -LIBDOVECOT_LOGIN_INCLUDE="-I$(incdir)/src/lib-auth -I$(incdir)/src/login-common" +LIBDOVECOT_LOGIN_INCLUDE="-I$(incdir)/src/login-common" LIBDOVECOT_IMAP_INCLUDE="-I$(incdir)/src/imap" LIBDOVECOT_CONFIG_INCLUDE="-I$(incdir)/src/config"
--- a/src/auth/auth-request.c Sun May 20 03:08:01 2012 +0300 +++ b/src/auth/auth-request.c Sun May 20 03:25:04 2012 +0300 @@ -33,6 +33,12 @@ #define AUTH_DNS_WARN_MSECS 500 #define CACHED_PASSWORD_SCHEME "SHA1" +struct auth_request_proxy_dns_lookup_ctx { + struct auth_request *request; + auth_request_proxy_cb_t *callback; + struct dns_lookup *dns_lookup; +}; + unsigned int auth_request_state_count[AUTH_REQUEST_STATE_MAX]; static void get_log_prefix(string_t *str, struct auth_request *auth_request, @@ -167,6 +173,8 @@ strlen(request->mech_password)); } + if (request->dns_lookup_ctx != NULL) + dns_lookup_abort(&request->dns_lookup_ctx->dns_lookup); if (request->to_abort != NULL) timeout_remove(&request->to_abort); if (request->to_penalty != NULL) @@ -1450,23 +1458,28 @@ static bool auth_request_proxy_is_self(struct auth_request *request) { - const char *const *tmp, *port = NULL, *destuser = NULL; + const char *const *tmp, *port = NULL; if (!request->proxy_host_is_self) return FALSE; + /* check if the port is the same */ tmp = auth_stream_split(request->extra_fields); for (; *tmp != NULL; tmp++) { if (strncmp(*tmp, "port=", 5) == 0) port = *tmp + 5; - else if (strncmp(*tmp, "destuser=", 9) == 0) - destuser = *tmp + 9; } if (port != NULL && !str_uint_equals(port, request->local_port)) return FALSE; - return destuser == NULL || - strcmp(destuser, request->original_username) == 0; + /* don't check destuser. in some systems destuser is intentionally + changed to proxied connections, but that shouldn't affect the + proxying decision. + + it's unlikely any systems would actually want to proxy a connection + to itself only to change the username, since it can already be done + without proxying by changing the "user" field. */ + return TRUE; } static bool @@ -1510,20 +1523,17 @@ } } -struct auth_request_proxy_dns_lookup_ctx { - struct auth_request *request; - auth_request_proxy_cb_t *callback; -}; - static void auth_request_proxy_dns_callback(const struct dns_lookup_result *result, - void *context) + struct auth_request_proxy_dns_lookup_ctx *ctx) { - struct auth_request_proxy_dns_lookup_ctx *ctx = context; struct auth_request *request = ctx->request; const char *host; unsigned int i; + request->dns_lookup_ctx = NULL; + ctx->dns_lookup = NULL; + host = auth_stream_reply_find(request->extra_fields, "host"); i_assert(host != NULL); @@ -1552,7 +1562,6 @@ } if (ctx->callback != NULL) ctx->callback(result->ret == 0, request); - i_free(ctx); } static int auth_request_proxy_host_lookup(struct auth_request *request, @@ -1587,10 +1596,11 @@ } } - ctx = i_new(struct auth_request_proxy_dns_lookup_ctx, 1); + ctx = p_new(request->pool, struct auth_request_proxy_dns_lookup_ctx, 1); ctx->request = request; - if (dns_lookup(host, &dns_set, auth_request_proxy_dns_callback, ctx) < 0) { + if (dns_lookup(host, &dns_set, auth_request_proxy_dns_callback, ctx, + &ctx->dns_lookup) < 0) { /* failed early */ request->internal_failure = TRUE; auth_request_proxy_finish_failure(request);
--- a/src/auth/auth-request.h Sun May 20 03:08:01 2012 +0300 +++ b/src/auth/auth-request.h Sun May 20 03:25:04 2012 +0300 @@ -56,6 +56,7 @@ struct auth_stream_reply *extra_cache_fields; /* the whole userdb result reply */ struct auth_stream_reply *userdb_reply; + struct auth_request_proxy_dns_lookup_ctx *dns_lookup_ctx; /* Result of passdb lookup */ enum passdb_result passdb_result;
--- a/src/dns/dns-client-settings.c Sun May 20 03:08:01 2012 +0300 +++ b/src/dns/dns-client-settings.c Sun May 20 03:25:04 2012 +0300 @@ -9,12 +9,10 @@ /* <settings checks> */ static struct file_listener_settings dns_client_unix_listeners_array[] = { - { "dns-client", 0666, "", "" }, - { "login/dns-client", 0666, "", "" } + { "dns-client", 0666, "", "" } }; static struct file_listener_settings *dns_client_unix_listeners[] = { - &dns_client_unix_listeners_array[0], - &dns_client_unix_listeners_array[1] + &dns_client_unix_listeners_array[0] }; static buffer_t dns_client_unix_listeners_buf = { dns_client_unix_listeners, sizeof(dns_client_unix_listeners), { 0, }
--- a/src/imap-login/client-authenticate.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap-login/client-authenticate.c Sun May 20 03:25:04 2012 +0300 @@ -3,14 +3,12 @@ #include "login-common.h" #include "base64.h" #include "buffer.h" -#include "hostpid.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "safe-memset.h" #include "str.h" #include "str-sanitize.h" -#include "time-util.h" #include "imap-resp-code.h" #include "imap-parser.h" #include "auth-client.h" @@ -33,14 +31,19 @@ } } -bool imap_client_auth_handle_reply(struct client *client, - const struct client_auth_reply *reply) +void imap_client_auth_result(struct client *client, + enum client_auth_result result, + const struct client_auth_reply *reply, + const char *text) { - struct imap_client *imap_client = (struct imap_client *)client; - string_t *str; - const char *timestamp, *msg; + string_t *referral; - if (reply->host != NULL) { + switch (result) { + case CLIENT_AUTH_RESULT_SUCCESS: + /* nothing to be done for IMAP */ + break; + case CLIENT_AUTH_RESULT_REFERRAL_SUCCESS: + case CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN: /* IMAP referral [nologin] referral host=.. [port=..] [destuser=..] @@ -50,55 +53,46 @@ OK [...] Logged in, but you should use this server instead. .. [REFERRAL ..] (Reason from auth server) */ - str = t_str_new(128); - str_append(str, imap_client->cmd_tag); - str_append_c(str, ' '); - str_append(str, reply->nologin ? "NO " : "OK "); - str_printfa(str, "[REFERRAL imap://%s;AUTH=%s@%s", + referral = t_str_new(128); + str_printfa(referral, "REFERRAL imap://%s;AUTH=%s@%s", reply->destuser, client->auth_mech_name, reply->host); if (reply->port != 143) - str_printfa(str, ":%u", reply->port); - str_append(str, "/] "); - if (reply->reason != NULL) - str_append(str, reply->reason); - else if (reply->nologin) - str_append(str, "Try this server instead."); - else { - str_append(str, "Logged in, but you should use " - "this server instead."); - } - str_append(str, "\r\n"); - client_send_raw(client, str_c(str)); - if (!reply->nologin) { - client_destroy_success(client, "Login with referral"); - return TRUE; + str_printfa(referral, ":%u", reply->port); + str_append(referral, "/"); + + if (result == CLIENT_AUTH_RESULT_REFERRAL_SUCCESS) { + client_send_reply_code(client, IMAP_CMD_REPLY_OK, + str_c(referral), text); + } else { + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + str_c(referral), text); } - } else if (!reply->nologin) { - /* normal login/failure */ - return FALSE; - } else if (reply->reason != NULL) { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_REASON, - reply->reason); - } else if (reply->temp) { - timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time); - msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]", - my_hostname, timestamp); - client_send_line(client, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, msg); - } else if (reply->authz_failure) { - client_send_line(client, CLIENT_CMD_REPLY_AUTHZ_FAILED, - "Authorization failed"); - } else { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); + break; + case CLIENT_AUTH_RESULT_ABORTED: + client_send_reply(client, IMAP_CMD_REPLY_BAD, text); + break; + case CLIENT_AUTH_RESULT_AUTHFAILED_REASON: + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + "ALERT", text); + break; + case CLIENT_AUTH_RESULT_AUTHZFAILED: + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + IMAP_RESP_CODE_AUTHZFAILED, text); + break; + case CLIENT_AUTH_RESULT_TEMPFAIL: + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + IMAP_RESP_CODE_UNAVAILABLE, text); + break; + case CLIENT_AUTH_RESULT_SSL_REQUIRED: + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + IMAP_RESP_CODE_PRIVACYREQUIRED, text); + break; + case CLIENT_AUTH_RESULT_AUTHFAILED: + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + IMAP_RESP_CODE_AUTHFAILED, text); + break; } - - i_assert(reply->nologin); - - if (!client->destroyed) - client_auth_failed(client); - return TRUE; } static int
--- a/src/imap-login/client-authenticate.h Sun May 20 03:08:01 2012 +0300 +++ b/src/imap-login/client-authenticate.h Sun May 20 03:25:04 2012 +0300 @@ -5,8 +5,10 @@ void client_authenticate_get_capabilities(struct client *client, string_t *str); -bool imap_client_auth_handle_reply(struct client *client, - const struct client_auth_reply *reply); +void imap_client_auth_result(struct client *client, + enum client_auth_result result, + const struct client_auth_reply *reply, + const char *text); int cmd_login(struct imap_client *client, const struct imap_arg *args); int cmd_authenticate(struct imap_client *imap_client, bool *parsed_r);
--- a/src/imap-login/client.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap-login/client.c Sun May 20 03:25:04 2012 +0300 @@ -82,8 +82,8 @@ imap_client->client_ignores_capability_resp_code = TRUE; client_send_raw(client, t_strconcat( "* CAPABILITY ", get_capability(client), "\r\n", NULL)); - client_send_line(client, CLIENT_CMD_REPLY_OK, - "Pre-login capabilities listed, post-login capabilities have more."); + client_send_reply(client, IMAP_CMD_REPLY_OK, + "Pre-login capabilities listed, post-login capabilities have more."); return 1; } @@ -94,6 +94,16 @@ } static void +imap_client_notify_starttls(struct client *client, + bool success, const char *text) +{ + if (success) + client_send_reply(client, IMAP_CMD_REPLY_OK, text); + else + client_send_reply(client, IMAP_CMD_REPLY_BAD, text); +} + +static void client_update_info(struct imap_client *client, const struct imap_arg *args) { const char *key, *value; @@ -111,6 +121,8 @@ (void)net_addr2ip(value, &client->common.local_ip); else if (strcasecmp(key, "x-connected-port") == 0) client->common.local_port = atoi(value); + else if (strcasecmp(key, "x-proxy-ttl") == 0) + client->common.proxy_ttl = atoi(value); else if (strcasecmp(key, "x-session-id") == 0) { client->common.session_id = p_strdup(client->common.pool, value); @@ -138,22 +150,22 @@ client_send_raw(&client->common, t_strdup_printf("* ID %s\r\n", imap_id_reply_generate(client->set->imap_id_send))); - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "ID completed."); + client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ID completed."); return 1; } static int cmd_noop(struct imap_client *client) { - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, - "NOOP completed."); + client_send_reply(&client->common, IMAP_CMD_REPLY_OK, + "NOOP completed."); return 1; } static int cmd_logout(struct imap_client *client) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, "Logging out"); - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, - "Logout completed."); + client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Logging out"); + client_send_reply(&client->common, IMAP_CMD_REPLY_OK, + "Logout completed."); client_destroy(&client->common, "Aborted login"); return 1; } @@ -161,8 +173,8 @@ static int cmd_enable(struct imap_client *client) { client_send_raw(&client->common, "* ENABLED\r\n"); - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, - "ENABLE ignored in non-authenticated state."); + client_send_reply(&client->common, IMAP_CMD_REPLY_OK, + "ENABLE ignored in non-authenticated state."); return 1; } @@ -226,14 +238,14 @@ /* error */ msg = imap_parser_get_error(client->parser, &fatal); if (fatal) { - client_send_line(&client->common, - CLIENT_CMD_REPLY_BYE, msg); + client_send_reply(&client->common, + IMAP_CMD_REPLY_BYE, msg); client_destroy(&client->common, t_strconcat("Disconnected: ", msg, NULL)); return FALSE; } - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, msg); + client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg); client->cmd_finished = TRUE; client->skip_line = TRUE; return -1; @@ -312,7 +324,7 @@ client->cmd_finished = TRUE; if (ret == -2 && strcasecmp(client->cmd_tag, "LOGIN") == 0) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, "First parameter in line is IMAP's command tag, " "not the command name. Add that before the command, " "like: a login user pass"); @@ -320,13 +332,13 @@ if (*client->cmd_tag == '\0') client->cmd_tag = "*"; if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Too many invalid IMAP commands."); client_destroy(&client->common, "Disconnected: Too many invalid commands"); return FALSE; } - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, "Error in IMAP command received by server."); } @@ -346,8 +358,8 @@ if (!auth_client_is_connected(auth_client)) { /* we're not currently connected to auth process - don't allow any commands */ - client_send_line(client, CLIENT_CMD_REPLY_STATUS, - AUTH_SERVER_WAITING_MSG); + client_notify_status(client, FALSE, + AUTH_SERVER_WAITING_MSG); if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); @@ -389,7 +401,7 @@ imap_parser_unref(&imap_client->parser); } -static void imap_client_send_greeting(struct client *client) +static void imap_client_notify_auth_ready(struct client *client) { string_t *greet; @@ -400,7 +412,6 @@ str_append(greet, "\r\n"); client_send_raw(client, str_c(greet)); - client->greeting_sent = TRUE; } static void imap_client_starttls(struct client *client) @@ -417,50 +428,11 @@ } static void -imap_client_send_line(struct client *client, enum client_cmd_reply reply, - const char *text) +client_send_reply_raw(struct client *client, + const char *prefix, const char *resp_code, + const char *text, bool tagged) { struct imap_client *imap_client = (struct imap_client *)client; - const char *resp_code = NULL; - const char *prefix = "NO"; - bool tagged = TRUE; - - switch (reply) { - case CLIENT_CMD_REPLY_OK: - prefix = "OK"; - break; - case CLIENT_CMD_REPLY_AUTH_FAILED: - resp_code = IMAP_RESP_CODE_AUTHFAILED; - break; - case CLIENT_CMD_REPLY_AUTHZ_FAILED: - resp_code = IMAP_RESP_CODE_AUTHZFAILED; - break; - case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP: - resp_code = IMAP_RESP_CODE_UNAVAILABLE; - break; - case CLIENT_CMD_REPLY_AUTH_FAIL_REASON: - resp_code = "ALERT"; - break; - case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL: - resp_code = IMAP_RESP_CODE_PRIVACYREQUIRED; - break; - case CLIENT_CMD_REPLY_BAD: - prefix = "BAD"; - break; - case CLIENT_CMD_REPLY_BYE: - prefix = "BYE"; - tagged = FALSE; - break; - case CLIENT_CMD_REPLY_STATUS: - prefix = "OK"; - tagged = FALSE; - break; - case CLIENT_CMD_REPLY_STATUS_BAD: - prefix = "BAD"; - tagged = FALSE; - resp_code = "ALERT"; - break; - } T_BEGIN { string_t *line = t_str_new(256); @@ -477,11 +449,61 @@ str_append(line, text); str_append(line, "\r\n"); - client_send_raw_data(client, str_data(line), - str_len(line)); + client_send_raw_data(client, str_data(line), str_len(line)); } T_END; } +void client_send_reply_code(struct client *client, enum imap_cmd_reply reply, + const char *resp_code, const char *text) +{ + const char *prefix = "NO"; + bool tagged = TRUE; + + switch (reply) { + case IMAP_CMD_REPLY_OK: + prefix = "OK"; + break; + case IMAP_CMD_REPLY_NO: + break; + case IMAP_CMD_REPLY_BAD: + prefix = "BAD"; + break; + case IMAP_CMD_REPLY_BYE: + prefix = "BYE"; + tagged = FALSE; + break; + } + client_send_reply_raw(client, prefix, resp_code, text, tagged); +} + +void client_send_reply(struct client *client, enum imap_cmd_reply reply, + const char *text) +{ + client_send_reply_code(client, reply, NULL, text); +} + +static void +imap_client_notify_status(struct client *client, bool bad, const char *text) +{ + if (bad) + client_send_reply_raw(client, "BAD", "ALERT", text, FALSE); + else + client_send_reply_raw(client, "OK", NULL, text, FALSE); +} + +static void +imap_client_notify_disconnect(struct client *client, + enum client_disconnect_reason reason, + const char *text) +{ + if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) { + client_send_reply_code(client, IMAP_CMD_REPLY_BYE, + IMAP_RESP_CODE_UNAVAILABLE, text); + } else { + client_send_reply_code(client, IMAP_CMD_REPLY_BYE, NULL, text); + } +} + static void imap_login_preinit(void) { login_set_roots = imap_login_setting_roots; @@ -500,15 +522,18 @@ imap_client_alloc, imap_client_create, imap_client_destroy, - imap_client_send_greeting, + imap_client_notify_auth_ready, + imap_client_notify_disconnect, + imap_client_notify_status, + imap_client_notify_starttls, imap_client_starttls, imap_client_input, - imap_client_send_line, - imap_client_auth_handle_reply, NULL, NULL, + imap_client_auth_result, imap_proxy_reset, - imap_proxy_parse_line + imap_proxy_parse_line, + imap_proxy_error }; static const struct login_binary imap_login_binary = {
--- a/src/imap-login/client.h Sun May 20 03:08:01 2012 +0300 +++ b/src/imap-login/client.h Sun May 20 03:25:04 2012 +0300 @@ -28,4 +28,18 @@ bool client_skip_line(struct imap_client *client); +enum imap_cmd_reply { + IMAP_CMD_REPLY_OK, + IMAP_CMD_REPLY_NO, + IMAP_CMD_REPLY_BAD, + IMAP_CMD_REPLY_BYE +}; + +void client_send_reply(struct client *client, + enum imap_cmd_reply reply, const char *text); + +void client_send_reply_code(struct client *client, + enum imap_cmd_reply reply, const char *resp_code, + const char *text); + #endif
--- a/src/imap-login/imap-proxy.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap-login/imap-proxy.c Sun May 20 03:25:04 2012 +0300 @@ -29,17 +29,21 @@ static void proxy_write_id(struct imap_client *client, string_t *str) { + i_assert(client->common.proxy_ttl > 0); + str_printfa(str, "I ID (" "\"x-session-id\" \"%s\" " "\"x-originating-ip\" \"%s\" " "\"x-originating-port\" \"%u\" " "\"x-connected-ip\" \"%s\" " - "\"x-connected-port\" \"%u\")\r\n", + "\"x-connected-port\" \"%u\" " + "\"x-proxy-ttl\" \"%u\")\r\n", client_get_session_id(&client->common), net_ip2addr(&client->common.ip), client->common.remote_port, net_ip2addr(&client->common.local_ip), - client->common.local_port); + client->common.local_port, + client->common.proxy_ttl - 1); } static void proxy_free_password(struct client *client) @@ -248,8 +252,9 @@ the remote is sending a different error message an attacker can't find out what users exist in the system. */ - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + IMAP_RESP_CODE_AUTHFAILED, + AUTH_FAILED_MSG); } else if (strncmp(line, "NO [", 4) == 0) { /* remote sent some other resp-code. forward it. */ client_send_raw(client, t_strconcat( @@ -262,8 +267,9 @@ failures. since other errors are pretty rare, it's safer to just hide them. they're still available in logs though. */ - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + IMAP_RESP_CODE_AUTHFAILED, + AUTH_FAILED_MSG); } client->proxy_auth_failed = TRUE; @@ -304,3 +310,9 @@ imap_client->proxy_wait_auth_continue = FALSE; client->proxy_state = IMAP_PROXY_STATE_NONE; } + +void imap_proxy_error(struct client *client, const char *text) +{ + client_send_reply_code(client, IMAP_CMD_REPLY_NO, + IMAP_RESP_CODE_UNAVAILABLE, text); +}
--- a/src/imap-login/imap-proxy.h Sun May 20 03:08:01 2012 +0300 +++ b/src/imap-login/imap-proxy.h Sun May 20 03:25:04 2012 +0300 @@ -4,4 +4,6 @@ void imap_proxy_reset(struct client *client); int imap_proxy_parse_line(struct client *client, const char *line); +void imap_proxy_error(struct client *client, const char *text); + #endif
--- a/src/imap/cmd-fetch.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap/cmd-fetch.c Sun May 20 03:25:04 2012 +0300 @@ -21,13 +21,14 @@ }; static bool -fetch_parse_args(struct imap_fetch_context *ctx, const struct imap_arg *arg, - const struct imap_arg **next_arg_r) +fetch_parse_args(struct imap_fetch_context *ctx, + struct client_command_context *cmd, + const struct imap_arg *arg, const struct imap_arg **next_arg_r) { const char *str, *const *macro; - if (ctx->cmd->uid) { - if (!imap_fetch_init_handler(ctx, "UID", &arg)) + if (cmd->uid) { + if (!imap_fetch_cmd_init_handler(ctx, cmd, "UID", &arg)) return FALSE; } if (imap_arg_get_atom(arg, &str)) { @@ -43,12 +44,12 @@ macro = full_macro; else { macro = NULL; - if (!imap_fetch_init_handler(ctx, str, &arg)) + if (!imap_fetch_cmd_init_handler(ctx, cmd, str, &arg)) return FALSE; } if (macro != NULL) { while (*macro != NULL) { - if (!imap_fetch_init_handler(ctx, *macro, &arg)) + if (!imap_fetch_cmd_init_handler(ctx, cmd, *macro, &arg)) return FALSE; macro++; } @@ -58,18 +59,18 @@ *next_arg_r = arg + 1; arg = imap_arg_as_list(arg); if (IMAP_ARG_IS_EOL(arg)) { - client_send_command_error(ctx->cmd, + client_send_command_error(cmd, "FETCH list is empty."); return FALSE; } while (imap_arg_get_atom(arg, &str)) { str = t_str_ucase(str); arg++; - if (!imap_fetch_init_handler(ctx, str, &arg)) + if (!imap_fetch_cmd_init_handler(ctx, cmd, str, &arg)) return FALSE; } if (!IMAP_ARG_IS_EOL(arg)) { - client_send_command_error(ctx->cmd, + client_send_command_error(cmd, "FETCH list contains non-atoms."); return FALSE; } @@ -79,6 +80,7 @@ static bool fetch_parse_modifier(struct imap_fetch_context *ctx, + struct client_command_context *cmd, const char *name, const struct imap_arg **args) { const char *str; @@ -87,58 +89,59 @@ if (strcmp(name, "CHANGEDSINCE") == 0) { if (!imap_arg_get_atom(*args, &str) || str_to_uint64(str, &modseq) < 0) { - client_send_command_error(ctx->cmd, + client_send_command_error(cmd, "Invalid CHANGEDSINCE modseq."); return FALSE; } *args += 1; - return imap_fetch_add_changed_since(ctx, modseq); + imap_fetch_add_changed_since(ctx, modseq); + return TRUE; } - if (strcmp(name, "VANISHED") == 0 && ctx->cmd->uid) { + if (strcmp(name, "VANISHED") == 0 && cmd->uid) { if ((ctx->client->enabled_features & MAILBOX_FEATURE_QRESYNC) == 0) { - client_send_command_error(ctx->cmd, - "QRESYNC not enabled"); + client_send_command_error(cmd, "QRESYNC not enabled"); return FALSE; } ctx->send_vanished = TRUE; return TRUE; } - client_send_command_error(ctx->cmd, "Unknown FETCH modifier"); + client_send_command_error(cmd, "Unknown FETCH modifier"); return FALSE; } static bool fetch_parse_modifiers(struct imap_fetch_context *ctx, + struct client_command_context *cmd, const struct imap_arg *args) { const char *name; while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &name)) { - client_send_command_error(ctx->cmd, + client_send_command_error(cmd, "FETCH modifiers contain non-atoms."); return FALSE; } args++; - if (!fetch_parse_modifier(ctx, t_str_ucase(name), &args)) + if (!fetch_parse_modifier(ctx, cmd, t_str_ucase(name), &args)) return FALSE; } if (ctx->send_vanished && (ctx->search_args->args->next == NULL || ctx->search_args->args->next->type != SEARCH_MODSEQ)) { - client_send_command_error(ctx->cmd, + client_send_command_error(cmd, "VANISHED used without CHANGEDSINCE"); return FALSE; } return TRUE; } -static bool cmd_fetch_finish(struct imap_fetch_context *ctx) +static bool cmd_fetch_finish(struct imap_fetch_context *ctx, + struct client_command_context *cmd) { static const char *ok_message = "OK Fetch completed."; - struct client_command_context *cmd = ctx->cmd; const char *tagged_reply = ok_message; if (ctx->partial_fetch) { @@ -175,11 +178,11 @@ { struct imap_fetch_context *ctx = cmd->context; - if (imap_fetch_more(ctx) == 0) { + if (imap_fetch_more(ctx, cmd) == 0) { /* unfinished */ return FALSE; } - return cmd_fetch_finish(ctx); + return cmd_fetch_finish(ctx, cmd); } bool cmd_fetch(struct client_command_context *cmd) @@ -218,15 +221,15 @@ } ctx->search_args = search_args; - if (!fetch_parse_args(ctx, &args[1], &next_arg) || + if (!fetch_parse_args(ctx, cmd, &args[1], &next_arg) || (imap_arg_get_list(next_arg, &list_arg) && - !fetch_parse_modifiers(ctx, list_arg))) { + !fetch_parse_modifiers(ctx, cmd, list_arg))) { imap_fetch_deinit(ctx); return TRUE; } if (imap_fetch_begin(ctx) == 0) { - if (imap_fetch_more(ctx) == 0) { + if (imap_fetch_more(ctx, cmd) == 0) { /* unfinished */ cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; @@ -235,5 +238,5 @@ return FALSE; } } - return cmd_fetch_finish(ctx); + return cmd_fetch_finish(ctx, cmd); }
--- a/src/imap/cmd-idle.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap/cmd-idle.c Sun May 20 03:25:04 2012 +0300 @@ -253,14 +253,8 @@ ctx->client = client; idle_add_keepalive_timeout(ctx); - if (client->mailbox != NULL) { - const struct mail_storage_settings *set; - - set = mailbox_get_settings(client->mailbox); - mailbox_notify_changes(client->mailbox, - set->mailbox_idle_check_interval, - idle_callback, ctx); - } + if (client->mailbox != NULL) + mailbox_notify_changes(client->mailbox, idle_callback, ctx); client_send_line(client, "+ idling"); io_remove(&client->io);
--- a/src/imap/cmd-select.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap/cmd-select.c Sun May 20 03:25:04 2012 +0300 @@ -214,7 +214,7 @@ struct imap_select_context *ctx = cmd->context; int ret; - if (imap_fetch_more(ctx->fetch_ctx) == 0) { + if (imap_fetch_more(ctx->fetch_ctx, cmd) == 0) { /* unfinished */ return FALSE; } @@ -247,16 +247,13 @@ fetch_ctx->qresync_sample_seqset = &ctx->qresync_sample_seqset; fetch_ctx->qresync_sample_uidset = &ctx->qresync_sample_uidset; - if (!imap_fetch_add_changed_since(fetch_ctx, ctx->qresync_modseq) || - !imap_fetch_init_handler(fetch_ctx, "UID", NULL) || - !imap_fetch_init_handler(fetch_ctx, "FLAGS", NULL) || - !imap_fetch_init_handler(fetch_ctx, "MODSEQ", NULL)) { - (void)imap_fetch_deinit(fetch_ctx); - return -1; - } + imap_fetch_add_changed_since(fetch_ctx, ctx->qresync_modseq); + imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_uid_init); + imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_flags_init); + imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_modseq_init); if (imap_fetch_begin(fetch_ctx) == 0) { - if (imap_fetch_more(fetch_ctx) == 0) { + if (imap_fetch_more(fetch_ctx, ctx->cmd) == 0) { /* unfinished */ ctx->fetch_ctx = fetch_ctx; ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT;
--- a/src/imap/imap-client.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap/imap-client.c Sun May 20 03:25:04 2012 +0300 @@ -20,6 +20,8 @@ #include <unistd.h> extern struct mail_storage_callbacks mail_storage_callbacks; +extern struct imap_client_vfuncs imap_client_vfuncs; + struct imap_module_register imap_module_register = { 0 }; struct client *imap_clients = NULL; @@ -48,6 +50,7 @@ pool = pool_alloconly_create("imap client", 2048); client = p_new(pool, struct client, 1); client->pool = pool; + client->v = imap_client_vfuncs; client->set = set; client->service_user = service_user; client->session_id = p_strdup(pool, session_id); @@ -174,6 +177,11 @@ void client_destroy(struct client *client, const char *reason) { + client->v.destroy(client, reason); +} + +static void client_default_destroy(struct client *client, const char *reason) +{ struct client_command_context *cmd; i_assert(!client->destroyed); @@ -1006,3 +1014,7 @@ client_destroy(imap_clients, "Server shutting down."); } } + +struct imap_client_vfuncs imap_client_vfuncs = { + client_default_destroy +};
--- a/src/imap/imap-client.h Sun May 20 03:08:01 2012 +0300 +++ b/src/imap/imap-client.h Sun May 20 03:25:04 2012 +0300 @@ -44,15 +44,6 @@ CLIENT_COMMAND_STATE_DONE }; -struct imap_module_register { - unsigned int id; -}; - -union imap_module_context { - struct imap_module_register *reg; -}; -extern struct imap_module_register imap_module_register; - struct client_command_context { struct client_command_context *prev, *next; struct client *client; @@ -96,10 +87,16 @@ struct message_size pos; }; +struct imap_client_vfuncs { + void (*destroy)(struct client *client, const char *reason); +}; + struct client { struct client *prev, *next; + struct imap_client_vfuncs v; const char *session_id; + int fd_in, fd_out; struct io *io; struct istream *input; @@ -166,6 +163,16 @@ unsigned int modseqs_sent_since_sync:1; }; +struct imap_module_register { + unsigned int id; +}; + +union imap_module_context { + struct imap_client_vfuncs super; + struct imap_module_register *reg; +}; +extern struct imap_module_register imap_module_register; + extern struct client *imap_clients; extern unsigned int imap_client_count;
--- a/src/imap/imap-fetch-body.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap/imap-fetch-body.c Sun May 20 03:25:04 2012 +0300 @@ -305,7 +305,7 @@ { string_t *str; - ctx->cur_name = p_strconcat(ctx->cmd->pool, + ctx->cur_name = p_strconcat(ctx->pool, "[", body->section, "]", NULL); ctx->cur_size = get_send_size(body, size->virtual_size); @@ -607,63 +607,60 @@ return TRUE; } -static bool fetch_body_header_fields_init(struct imap_fetch_context *ctx, +static bool fetch_body_header_fields_init(struct imap_fetch_init_context *ctx, struct imap_fetch_body_data *body, const char *section) { - const char *const *arr, *name; + const char *const *arr; if (!fetch_body_header_fields_check(section)) return FALSE; - name = get_body_name(body); - if ((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER | - MAIL_FETCH_STREAM_BODY)) != 0) { + if ((ctx->fetch_ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER | + MAIL_FETCH_STREAM_BODY)) != 0) { /* we'll need to open the file anyway, don't try to get the headers from cache. */ - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", + imap_fetch_add_handler(ctx, 0, "NIL", fetch_body_header_partial, body); return TRUE; } for (arr = body->fields; *arr != NULL; arr++) { - const char *hdr = p_strdup(ctx->cmd->pool, *arr); - array_append(&ctx->all_headers, &hdr, 1); + const char *hdr = p_strdup(ctx->fetch_ctx->pool, *arr); + array_append(&ctx->fetch_ctx->all_headers, &hdr, 1); } - body->header_ctx = mailbox_header_lookup_init(ctx->box, body->fields); - imap_fetch_add_handler(ctx, FALSE, TRUE, name, "NIL", + body->header_ctx = mailbox_header_lookup_init(ctx->fetch_ctx->box, + body->fields); + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT, "NIL", fetch_body_header_fields, body); return TRUE; } -static bool fetch_body_section_name_init(struct imap_fetch_context *ctx, - struct imap_fetch_body_data *body) +static bool +fetch_body_section_name_init(struct imap_fetch_init_context *ctx, + struct imap_fetch_body_data *body) { - const char *name, *section = body->section; + const char *section = body->section; - name = get_body_name(body); if (*section == '\0') { - ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER | + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", - fetch_body, body); + imap_fetch_add_handler(ctx, 0, "NIL", fetch_body, body); return TRUE; } if (strcmp(section, "TEXT") == 0) { - ctx->fetch_data |= MAIL_FETCH_STREAM_BODY; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", - fetch_body, body); + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_BODY; + imap_fetch_add_handler(ctx, 0, "NIL", fetch_body, body); return TRUE; } if (strncmp(section, "HEADER", 6) == 0) { /* exact header matches could be cached */ if (section[6] == '\0') { - ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", - fetch_body, body); + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER; + imap_fetch_add_handler(ctx, 0, "NIL", fetch_body, body); return TRUE; } @@ -673,12 +670,12 @@ if (strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0 && fetch_body_header_fields_check(section+18)) { - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", + imap_fetch_add_handler(ctx, 0, "NIL", fetch_body_header_partial, body); return TRUE; } } else if (*section >= '0' && *section <= '9') { - ctx->fetch_data |= MAIL_FETCH_STREAM_BODY | + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_BODY | MAIL_FETCH_MESSAGE_PARTS; while ((*section >= '0' && *section <= '9') || @@ -692,14 +689,13 @@ fetch_body_header_fields_check(section+14)) || (strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0 && fetch_body_header_fields_check(section+18))) { - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", + imap_fetch_add_handler(ctx, 0, "NIL", fetch_body_mime, body); return TRUE; } } - client_send_command_error(ctx->cmd, - "Invalid BODY[..] parameter: Unknown or broken section"); + ctx->error = "Invalid BODY[..] parameter: Unknown or broken section"; return FALSE; } @@ -723,34 +719,32 @@ return TRUE; } -static bool body_section_build(struct imap_fetch_context *ctx, - struct imap_fetch_body_data *body, - const char *prefix, - const struct imap_arg *args, - unsigned int args_count) +static bool +body_section_build(struct imap_fetch_init_context *ctx, + struct imap_fetch_body_data *body, const char *prefix, + const struct imap_arg *args, unsigned int args_count) { string_t *str; const char **arr, *value; size_t i; - str = str_new(ctx->cmd->pool, 128); + str = str_new(ctx->fetch_ctx->pool, 128); str_append(str, prefix); str_append(str, " ("); /* @UNSAFE: NULL-terminated list of headers */ - arr = p_new(ctx->cmd->pool, const char *, args_count + 1); + arr = p_new(ctx->fetch_ctx->pool, const char *, args_count + 1); for (i = 0; i < args_count; i++) { if (!imap_arg_get_astring(&args[i], &value)) { - client_send_command_error(ctx->cmd, - "Invalid BODY[..] parameter: " - "Header list contains non-strings"); + ctx->error = "Invalid BODY[..] parameter: " + "Header list contains non-strings"; return FALSE; } if (i != 0) str_append_c(str, ' '); - arr[i] = p_strdup(ctx->cmd->pool, t_str_ucase(value)); + arr[i] = p_strdup(ctx->fetch_ctx->pool, t_str_ucase(value)); if (args[i].type == IMAP_ARG_ATOM) str_append(str, arr[i]); @@ -769,53 +763,51 @@ return TRUE; } -bool fetch_body_section_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args) +bool imap_fetch_body_section_init(struct imap_fetch_init_context *ctx) { struct imap_fetch_body_data *body; const struct imap_arg *list_args; unsigned int list_count; - const char *partial, *str; - const char *p = name + 4; + const char *partial, *str, *p; - body = p_new(ctx->cmd->pool, struct imap_fetch_body_data, 1); + i_assert(strncmp(ctx->name, "BODY", 4) == 0); + p = ctx->name + 4; + + body = p_new(ctx->fetch_ctx->pool, struct imap_fetch_body_data, 1); body->max_size = (uoff_t)-1; if (strncmp(p, ".PEEK", 5) == 0) { body->peek = TRUE; p += 5; } else { - ctx->flags_update_seen = TRUE; + ctx->fetch_ctx->flags_update_seen = TRUE; } if (*p != '[') { - client_send_command_error(ctx->cmd, - "Invalid BODY[..] parameter: Missing '['"); + ctx->error = "Invalid BODY[..] parameter: Missing '['"; return FALSE; } - if (imap_arg_get_list_full(&(*args)[0], &list_args, &list_count)) { + if (imap_arg_get_list_full(&ctx->args[0], &list_args, &list_count)) { /* BODY[HEADER.FIELDS.. (headers list)] */ - if (!imap_arg_get_atom(&(*args)[1], &str) || + if (!imap_arg_get_atom(&ctx->args[1], &str) || str[0] != ']') { - client_send_command_error(ctx->cmd, - "Invalid BODY[..] parameter: Missing ']'"); + ctx->error = "Invalid BODY[..] parameter: Missing ']'"; return FALSE; } if (!body_section_build(ctx, body, p+1, list_args, list_count)) return FALSE; p = str; - *args += 2; + ctx->args += 2; } else { /* no headers list */ body->section = p+1; p = strchr(body->section, ']'); if (p == NULL) { - client_send_command_error(ctx->cmd, - "Invalid BODY[..] parameter: Missing ']'"); + ctx->error = "Invalid BODY[..] parameter: Missing ']'"; return FALSE; } - body->section = p_strdup_until(ctx->cmd->pool, + body->section = p_strdup_until(ctx->fetch_ctx->pool, body->section, p); } @@ -827,9 +819,8 @@ if (!read_uoff_t(&p, &body->skip) || body->skip > OFF_T_MAX) { /* wrapped */ - client_send_command_error(ctx->cmd, - "Invalid BODY[..] parameter: " - "Too big partial start"); + ctx->error = "Invalid BODY[..] parameter: " + "Too big partial start"; return FALSE; } @@ -838,22 +829,22 @@ if (!read_uoff_t(&p, &body->max_size) || body->max_size > OFF_T_MAX) { /* wrapped */ - client_send_command_error(ctx->cmd, - "Invalid BODY[..] parameter: " - "Too big partial end"); + ctx->error = "Invalid BODY[..] parameter: " + "Too big partial end"; return FALSE; } } if (*p != '>') { - client_send_command_error(ctx->cmd, - t_strdup_printf("Invalid BODY[..] parameter: " - "Missing '>' in '%s'", - partial)); + ctx->error = t_strdup_printf( + "Invalid BODY[..] parameter: " + "Missing '>' in '%s'", partial); return FALSE; } } + /* sanitize the name */ + ctx->name = get_body_name(body); return fetch_body_section_name_init(ctx, body); } @@ -954,39 +945,37 @@ return fetch_stream(ctx, &body_size); } -bool fetch_rfc822_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args ATTR_UNUSED) +bool imap_fetch_rfc822_init(struct imap_fetch_init_context *ctx) { + const char *name = ctx->name; + if (name[6] == '\0') { - ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER | + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY; - ctx->flags_update_seen = TRUE; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", - fetch_rfc822, NULL); + ctx->fetch_ctx->flags_update_seen = TRUE; + imap_fetch_add_handler(ctx, 0, "NIL", fetch_rfc822, NULL); return TRUE; } if (strcmp(name+6, ".SIZE") == 0) { - ctx->fetch_data |= MAIL_FETCH_VIRTUAL_SIZE; - imap_fetch_add_handler(ctx, TRUE, FALSE, name, "0", - fetch_rfc822_size, NULL); + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_VIRTUAL_SIZE; + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, + "0", fetch_rfc822_size, NULL); return TRUE; } if (strcmp(name+6, ".HEADER") == 0) { - ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER; + imap_fetch_add_handler(ctx, 0, "NIL", fetch_rfc822_header, NULL); return TRUE; } if (strcmp(name+6, ".TEXT") == 0) { - ctx->fetch_data |= MAIL_FETCH_STREAM_BODY; - ctx->flags_update_seen = TRUE; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL", - fetch_rfc822_text, NULL); + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_STREAM_BODY; + ctx->fetch_ctx->flags_update_seen = TRUE; + imap_fetch_add_handler(ctx, 0, "NIL", fetch_rfc822_text, NULL); return TRUE; } - client_send_command_error(ctx->cmd, t_strconcat( - "Unknown parameter ", name, NULL)); + ctx->error = t_strconcat("Unknown parameter ", name, NULL); return FALSE; }
--- a/src/imap/imap-fetch.c Sun May 20 03:08:01 2012 +0300 +++ b/src/imap/imap-fetch.c Sun May 20 03:25:04 2012 +0300 @@ -45,24 +45,53 @@ return strcmp(name, h->name); } -bool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args) +bool imap_fetch_init_handler(struct imap_fetch_init_context *init_ctx) { const struct imap_fetch_handler *handler; const char *lookup_name, *p; - for (p = name; i_isalnum(*p) || *p == '-'; p++) ; - lookup_name = t_strdup_until(name, p); + for (p = init_ctx->name; i_isalnum(*p) || *p == '-'; p++) ; + lookup_name = t_strdup_until(init_ctx->name, p); handler = array_bsearch(&fetch_handlers, lookup_name, imap_fetch_handler_bsearch); if (handler == NULL) { - client_send_command_error(ctx->cmd, - t_strconcat("Unknown parameter ", name, NULL)); + init_ctx->error = t_strdup_printf("Unknown parameter: %s", + init_ctx->name); return FALSE; } + return handler->init(init_ctx); +} - return handler->init(ctx, name, args); +void imap_fetch_init_nofail_handler(struct imap_fetch_context *ctx, + bool (*init)(struct imap_fetch_init_context *)) +{ + struct imap_fetch_init_context init_ctx; + + memset(&init_ctx, 0, sizeof(init_ctx)); + init_ctx.fetch_ctx = ctx; + if (!init(&init_ctx)) + i_unreached(); +} + +bool imap_fetch_cmd_init_handler(struct imap_fetch_context *ctx, + struct client_command_context *cmd, + const char *name, const struct imap_arg **args) +{ + struct imap_fetch_init_context init_ctx; + + memset(&init_ctx, 0, sizeof(init_ctx)); + init_ctx.fetch_ctx = ctx; + init_ctx.name = name; + init_ctx.args = *args; + + if (!imap_fetch_init_handler(&init_ctx)) { + i_assert(init_ctx.error != NULL); + client_send_command_error(cmd, init_ctx.error); + return FALSE; + } + *args = init_ctx.args; + return TRUE; } struct imap_fetch_context * @@ -73,7 +102,7 @@ ctx = p_new(cmd->pool, struct imap_fetch_context, 1); ctx->client = client; - ctx->cmd = cmd; + ctx->pool = cmd->pool; ctx->box = box; ctx->cur_str = str_new(default_pool, 8192); @@ -85,7 +114,7 @@ return ctx; } -bool imap_fetch_add_changed_since(struct imap_fetch_context *ctx, +void imap_fetch_add_changed_since(struct imap_fetch_context *ctx, uint64_t modseq) { struct mail_search_arg *search_arg; @@ -93,26 +122,26 @@ search_arg = p_new(ctx->search_args->pool, struct mail_search_arg, 1); search_arg->type = SEARCH_MODSEQ; search_arg->value.modseq = - p_new(ctx->cmd->pool, struct mail_search_modseq, 1); + p_new(ctx->pool, struct mail_search_modseq, 1); search_arg->value.modseq->modseq = modseq + 1; search_arg->next = ctx->search_args->args->next; ctx->search_args->args->next = search_arg; - return imap_fetch_init_handler(ctx, "MODSEQ", NULL); + imap_fetch_init_nofail_handler(ctx, imap_fetch_modseq_init); } #undef imap_fetch_add_handler -void imap_fetch_add_handler(struct imap_fetch_context *ctx, - bool buffered, bool want_deinit, - const char *name, const char *nil_reply, +void imap_fetch_add_handler(struct imap_fetch_init_context *ctx, + enum imap_fetch_handler_flags flags, + const char *nil_reply, imap_fetch_handler_t *handler, void *context) { /* partially because of broken clients, but also partially because it potentially can make client implementations faster, we have a buffered parameter which basically means that the handler promises - to write the output in ctx->cur_str. The cur_str is then sent to - client before calling any non-buffered handlers. + to write the output in fetch_ctx->cur_str. The cur_str is then sent + to client before calling any non-buffered handlers. We try to keep the handler registration order the same as the client requested them. This is especially useful to get UID @@ -123,7 +152,7 @@ if (context == NULL) { /* don't allow duplicate handlers */ - array_foreach(&ctx->handlers, ctx_handler) { + array_foreach(&ctx->fetch_ctx->handlers, ctx_handler) { if (ctx_handler->handler == handler && ctx_handler->context == NULL) return; @@ -133,17 +162,17 @@ memset(&h, 0, sizeof(h)); h.handler = handler; h.context = context; - h.buffered = buffered; - h.want_deinit = want_deinit; - h.name = p_strdup(ctx->cmd->pool, name); - h.nil_reply = p_strdup(ctx->cmd->pool, nil_reply); + h.buffered = (flags & IMAP_FETCH_HANDLER_FLAG_BUFFERED) != 0; + h.want_deinit = (flags & IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT) != 0; + h.name = p_strdup(ctx->fetch_ctx->pool, ctx->name); + h.nil_reply = p_strdup(ctx->fetch_ctx->pool, nil_reply); - if (!buffered) - array_append(&ctx->handlers, &h, 1); + if (!h.buffered) + array_append(&ctx->fetch_ctx->handlers, &h, 1); else { - array_insert(&ctx->handlers, ctx->buffered_handlers_count, - &h, 1); - ctx->buffered_handlers_count++; + array_insert(&ctx->fetch_ctx->handlers, + ctx->fetch_ctx->buffered_handlers_count, &h, 1); + ctx->fetch_ctx->buffered_handlers_count++; } } @@ -312,7 +341,7 @@ ctx->flags_update_seen = FALSE; else if (!ctx->flags_have_handler) { ctx->flags_show_only_seen_changes = TRUE; - (void)imap_fetch_init_handler(ctx, "FLAGS", NULL); + imap_fetch_init_nofail_handler(ctx, imap_fetch_flags_init); } } @@ -337,7 +366,7 @@ /* Delayed uidset -> seqset conversion. VANISHED needs the uidset. */ mail_search_args_init(ctx->search_args, ctx->box, TRUE, - &ctx->cmd->client->search_saved_uidset); + &ctx->client->search_saved_uidset); ctx->search_ctx = mailbox_search_init(ctx->trans, ctx->search_args, NULL, ctx->fetch_data, ctx->all_headers_ctx); @@ -387,7 +416,8 @@ return 0; } -static int imap_fetch_more_int(struct imap_fetch_context *ctx) +static int imap_fetch_more_int(struct imap_fetch_context *ctx, + struct client_command_context *cmd) { struct client *client = ctx->client; const struct imap_fetch_context_handler *handlers; @@ -430,7 +460,7 @@ } if (ctx->cur_mail == NULL) { - if (ctx->cmd->cancel) + if (cmd->cancel) return 1; if (!mailbox_search_next(ctx->search_ctx, @@ -503,19 +533,20 @@ return 1; } -int imap_fetch_more(struct imap_fetch_context *ctx) +int imap_fetch_more(struct imap_fetch_context *ctx, + struct client_command_context *cmd) { int ret; i_assert(ctx->client->output_lock == NULL || - ctx->client->output_lock == ctx->cmd); + ctx->client->output_lock == cmd); - ret = imap_fetch_more_int(ctx); + ret = imap_fetch_more_int(ctx, cmd); if (ret < 0) ctx->failed = TRUE; if (ctx->line_partial) { /* nothing can be sent until FETCH is finished */ - ctx->client->output_lock = ctx->cmd; + ctx->client->output_lock = cmd; } return ret; } @@ -580,16 +611,15 @@ return 1; } -static bool fetch_body_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args) +static bool fetch_body_init(struct imap_fetch_init_context *ctx) { - if (name[4] == '\0') { - ctx->fetch_data |= MAIL_FETCH_IMAP_BODY; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, - "("BODY_NIL_REPLY")", fetch_body, NULL); + if (ctx->name[4] == '\0') { + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_BODY; + imap_fetch_add_handler(ctx, 0, "("BODY_NIL_REPLY")", + fetch_body, NULL); return TRUE; } - return fetch_body_section_init(ctx, name, args); + return imap_fetch_body_section_init(ctx); } static int fetch_bodystructure(struct imap_fetch_context *ctx, @@ -616,13 +646,10 @@ return 1; } -static bool -fetch_bodystructure_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args ATTR_UNUSED) +static bool fetch_bodystructure_init(struct imap_fetch_init_context *ctx) { - ctx->fetch_data |= MAIL_FETCH_IMAP_BODYSTRUCTURE; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, - "("BODY_NIL_REPLY" NIL NIL NIL NIL)", + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_BODYSTRUCTURE; + imap_fetch_add_handler(ctx, 0, "("BODY_NIL_REPLY" NIL NIL NIL NIL)", fetch_bodystructure, NULL); return TRUE; } @@ -649,12 +676,10 @@ return 1; } -static bool -fetch_envelope_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args ATTR_UNUSED) +static bool fetch_envelope_init(struct imap_fetch_init_context *ctx) { - ctx->fetch_data |= MAIL_FETCH_IMAP_ENVELOPE; - imap_fetch_add_handler(ctx, FALSE, FALSE, name, ENVELOPE_NIL_REPLY, + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_IMAP_ENVELOPE; + imap_fetch_add_handler(ctx, 0, ENVELOPE_NIL_REPLY, fetch_envelope, NULL); return TRUE; } @@ -684,13 +709,12 @@ return 1; } -static bool -fetch_flags_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args ATTR_UNUSED) +bool imap_fetch_flags_init(struct imap_fetch_init_context *ctx) { - ctx->flags_have_handler = TRUE; - ctx->fetch_data |= MAIL_FETCH_FLAGS; - imap_fetch_add_handler(ctx, TRUE, FALSE, name, "()", fetch_flags, NULL); + ctx->fetch_ctx->flags_have_handler = TRUE; + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_FLAGS; + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, + "()", fetch_flags, NULL); return TRUE; } @@ -707,12 +731,10 @@ return 1; } -static bool -fetch_internaldate_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args ATTR_UNUSED) +static bool fetch_internaldate_init(struct imap_fetch_init_context *ctx) { - ctx->fetch_data |= MAIL_FETCH_RECEIVED_DATE; - imap_fetch_add_handler(ctx, TRUE, FALSE, name, + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_RECEIVED_DATE; + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, "\"01-Jan-1970 00:00:00 +0000\"", fetch_internaldate, NULL); return TRUE; @@ -731,13 +753,11 @@ return 1; } -static bool -fetch_modseq_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args ATTR_UNUSED) +bool imap_fetch_modseq_init(struct imap_fetch_init_context *ctx) { - (void)client_enable(ctx->client, MAILBOX_FEATURE_CONDSTORE); - imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL, - fetch_modseq, NULL); + (void)client_enable(ctx->fetch_ctx->client, MAILBOX_FEATURE_CONDSTORE); + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, + NULL, fetch_modseq, NULL); return TRUE; } @@ -748,11 +768,10 @@ return 1; } -static bool -fetch_uid_init(struct imap_fetch_context *ctx ATTR_UNUSED, const char *name, - const struct imap_arg **args ATTR_UNUSED) +bool imap_fetch_uid_init(struct imap_fetch_init_context *ctx) { - imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL, fetch_uid, NULL); + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, + NULL, fetch_uid, NULL); return TRUE; } @@ -770,12 +789,11 @@ return 1; } -static bool -fetch_guid_init(struct imap_fetch_context *ctx ATTR_UNUSED, const char *name, - const struct imap_arg **args ATTR_UNUSED) +static bool fetch_guid_init(struct imap_fetch_init_context *ctx) { - ctx->fetch_data |= MAIL_FETCH_GUID; - imap_fetch_add_handler(ctx, TRUE, FALSE, name, "", fetch_guid, NULL); + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_GUID; + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, + "", fetch_guid, NULL); return TRUE; } @@ -798,13 +816,10 @@ return 1; } -static bool -fetch_x_mailbox_init(struct imap_fetch_context *ctx ATTR_UNUSED, - const char *name, - const struct imap_arg **args ATTR_UNUSED) +static bool fetch_x_mailbox_init(struct imap_fetch_init_context *ctx) { - imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL, - fetch_x_mailbox, NULL); + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, + NULL, fetch_x_mailbox, NULL); return TRUE; } @@ -816,13 +831,10 @@ return 1; } -static bool -fetch_x_real_uid_init(struct imap_fetch_context *ctx ATTR_UNUSED, - const char *name, - const struct imap_arg **args ATTR_UNUSED) +static bool fetch_x_real_uid_init(struct imap_fetch_init_context *ctx) { - imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL, - fetch_x_real_uid, NULL); + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, + NULL, fetch_x_real_uid, NULL); return TRUE; } @@ -839,12 +851,10 @@ return 1; } -static bool -fetch_x_savedate_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args ATTR_UNUSED) +static bool fetch_x_savedate_init(struct imap_fetch_init_context *ctx) { - ctx->fetch_data |= MAIL_FETCH_SAVE_DATE; - imap_fetch_add_handler(ctx, TRUE, FALSE, name, + ctx->fetch_ctx->fetch_data |= MAIL_FETCH_SAVE_DATE; + imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_BUFFERED, "\"01-Jan-1970 00:00:00 +0000\"", fetch_x_savedate, NULL); return TRUE; @@ -855,11 +865,11 @@ { "BODY", fetch_body_init }, { "BODYSTRUCTURE", fetch_bodystructure_init }, { "ENVELOPE", fetch_envelope_init }, - { "FLAGS", fetch_flags_init }, + { "FLAGS", imap_fetch_flags_init }, { "INTERNALDATE", fetch_internaldate_init }, - { "MODSEQ", fetch_modseq_init }, - { "RFC822", fetch_rfc822_init }, - { "UID", fetch_uid_init }, + { "MODSEQ", imap_fetch_modseq_init }, + { "RFC822", imap_fetch_rfc822_init }, + { "UID", imap_fetch_uid_init }, { "X-GUID", fetch_guid_init }, { "X-MAILBOX", fetch_x_mailbox_init }, { "X-REAL-UID", fetch_x_real_uid_init },
--- a/src/imap/imap-fetch.h Sun May 20 03:08:01 2012 +0300 +++ b/src/imap/imap-fetch.h Sun May 20 03:25:04 2012 +0300 @@ -3,17 +3,29 @@ struct imap_fetch_context; +enum imap_fetch_handler_flags { + IMAP_FETCH_HANDLER_FLAG_BUFFERED = 0x01, + IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT = 0x02 +}; + /* Returns 1 = ok, 0 = client output buffer full, call again, -1 = error. mail = NULL for deinit. */ typedef int imap_fetch_handler_t(struct imap_fetch_context *ctx, struct mail *mail, void *context); +struct imap_fetch_init_context { + struct imap_fetch_context *fetch_ctx; + const char *name; + const struct imap_arg *args; + + const char *error; +}; + struct imap_fetch_handler { const char *name; - /* Returns FALSE if arg is invalid. */ - bool (*init)(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args); + /* Returns FALSE and sets ctx->error if arg is invalid */ + bool (*init)(struct imap_fetch_init_context *ctx); }; struct imap_fetch_context_handler { @@ -29,8 +41,8 @@ struct imap_fetch_context { struct client *client; - struct client_command_context *cmd; struct mailbox *box; + pool_t pool; struct mailbox_transaction_context *trans; struct mail_search_args *search_args; @@ -76,40 +88,45 @@ void imap_fetch_handlers_register(const struct imap_fetch_handler *handlers, size_t count); -void imap_fetch_add_handler(struct imap_fetch_context *ctx, - bool buffered, bool want_deinit, - const char *name, const char *nil_reply, +void imap_fetch_add_handler(struct imap_fetch_init_context *ctx, + enum imap_fetch_handler_flags flags, + const char *nil_reply, imap_fetch_handler_t *handler, void *context); #ifdef CONTEXT_TYPE_SAFETY -# define imap_fetch_add_handler(ctx, buffered, want_deinit, name, nil_reply, \ - handler, context) \ +# define imap_fetch_add_handler(ctx, flags, nil_reply, handler, context) \ ({(void)(1 ? 0 : handler((struct imap_fetch_context *)NULL, \ (struct mail *)NULL, context)); \ - imap_fetch_add_handler(ctx, buffered, want_deinit, name, nil_reply, \ + imap_fetch_add_handler(ctx, flags, nil_reply, \ (imap_fetch_handler_t *)handler, context); }) #else -# define imap_fetch_add_handler(ctx, buffered, want_deinit, name, nil_reply, \ - handler, context) \ - imap_fetch_add_handler(ctx, buffered, want_deinit, name, nil_reply, \ +# define imap_fetch_add_handler(ctx, flags, nil_reply, handler, context) \ + imap_fetch_add_handler(ctx, flags, nil_reply, \ (imap_fetch_handler_t *)handler, context) #endif struct imap_fetch_context * imap_fetch_init(struct client_command_context *cmd, struct mailbox *box); int imap_fetch_deinit(struct imap_fetch_context *ctx); -bool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args); +bool imap_fetch_init_handler(struct imap_fetch_init_context *init_ctx); +void imap_fetch_init_nofail_handler(struct imap_fetch_context *ctx, + bool (*init)(struct imap_fetch_init_context *)); +bool imap_fetch_cmd_init_handler(struct imap_fetch_context *ctx, + struct client_command_context *cmd, + const char *name, const struct imap_arg **args); -bool imap_fetch_add_changed_since(struct imap_fetch_context *ctx, +void imap_fetch_add_changed_since(struct imap_fetch_context *ctx, uint64_t modseq); int imap_fetch_begin(struct imap_fetch_context *ctx); -int imap_fetch_more(struct imap_fetch_context *ctx); +int imap_fetch_more(struct imap_fetch_context *ctx, + struct client_command_context *cmd); -bool fetch_body_section_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args); -bool fetch_rfc822_init(struct imap_fetch_context *ctx, const char *name, - const struct imap_arg **args); +bool imap_fetch_flags_init(struct imap_fetch_init_context *ctx); +bool imap_fetch_modseq_init(struct imap_fetch_init_context *ctx); +bool imap_fetch_uid_init(struct imap_fetch_init_context *ctx); + +bool imap_fetch_body_section_init(struct imap_fetch_init_context *ctx); +bool imap_fetch_rfc822_init(struct imap_fetch_init_context *ctx); void imap_fetch_handlers_init(void); void imap_fetch_handlers_deinit(void);
--- a/src/lib-dns/dns-lookup.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-dns/dns-lookup.c Sun May 20 03:25:04 2012 +0300 @@ -121,6 +121,7 @@ #undef dns_lookup int dns_lookup(const char *host, const struct dns_lookup_settings *set, + struct dns_lookup **lookup_r, dns_lookup_callback_t *callback, void *context) { struct dns_lookup *lookup; @@ -162,6 +163,8 @@ lookup->context = context; if (gettimeofday(&lookup->start_time, NULL) < 0) i_fatal("gettimeofday() failed: %m"); + + *lookup_r = lookup; return 0; } @@ -182,3 +185,8 @@ i_free(lookup->path); i_free(lookup); } + +void dns_lookup_abort(struct dns_lookup **lookup) +{ + dns_lookup_free(lookup); +}
--- a/src/lib-dns/dns-lookup.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-dns/dns-lookup.h Sun May 20 03:25:04 2012 +0300 @@ -1,6 +1,8 @@ #ifndef DNS_LOOKUP_H #define DNS_LOOKUP_H +struct dns_lookup; + struct dns_lookup_settings { const char *dns_client_socket_path; unsigned int timeout_msecs; @@ -22,11 +24,18 @@ typedef void dns_lookup_callback_t(const struct dns_lookup_result *result, void *context); +/* Do asynchronous DNS lookup via dns-client UNIX socket. Returns 0 if lookup + started, -1 if there was an error communicating with the UNIX socket. + When failing with -1, the callback is called before returning from the + function. */ int dns_lookup(const char *host, const struct dns_lookup_settings *set, + struct dns_lookup **lookup_r, dns_lookup_callback_t *callback, void *context); -#define dns_lookup(host, set, callback, context) \ +#define dns_lookup(host, set, callback, context, lookup_r) \ CONTEXT_CALLBACK2(dns_lookup, dns_lookup_callback_t, \ callback, const struct dns_lookup_result *, \ - context, host, set) + context, host, set, lookup_r) +/* Abort the DNS lookup without calling the callback. */ +void dns_lookup_abort(struct dns_lookup **lookup); #endif
--- a/src/lib-imap-client/imapc-connection.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-imap-client/imapc-connection.c Sun May 20 03:25:04 2012 +0300 @@ -86,6 +86,7 @@ struct imap_parser *parser; struct timeout *to; struct timeout *to_output; + struct dns_lookup *dns_lookup; struct ssl_iostream *ssl_iostream; @@ -352,6 +353,8 @@ if (conn->client->set.debug) i_debug("imapc(%s): Disconnected", conn->name); + if (conn->dns_lookup != NULL) + dns_lookup_abort(&conn->dns_lookup); imapc_connection_lfiles_free(conn); imapc_connection_literal_reset(&conn->literal); if (conn->to != NULL) @@ -1315,9 +1318,9 @@ static void imapc_connection_dns_callback(const struct dns_lookup_result *result, - void *context) + struct imapc_connection *conn) { - struct imapc_connection *conn = context; + conn->dns_lookup = NULL; if (result->ret != 0) { i_error("imapc(%s): dns_lookup(%s) failed: %s", @@ -1385,7 +1388,8 @@ if (conn->ips_count == 0) { (void)dns_lookup(conn->client->set.host, &dns_set, - imapc_connection_dns_callback, conn); + imapc_connection_dns_callback, conn, + &conn->dns_lookup); } else { imapc_connection_connect_next_ip(conn); }
--- a/src/lib-index/mail-index-fsck.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-fsck.c Sun May 20 03:25:04 2012 +0300 @@ -454,8 +454,7 @@ mail_index_fsck_map(index, map); } T_END; - map->write_base_header = TRUE; - map->write_atomic = TRUE; + map->header_changed = TRUE; mail_index_write(index, FALSE); if (!orig_locked)
--- a/src/lib-index/mail-index-lock.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-lock.c Sun May 20 03:25:04 2012 +0300 @@ -36,27 +36,15 @@ timeout_secs, lock_r); } -static int mail_index_lock(struct mail_index *index, int lock_type, +static int mail_index_lock(struct mail_index *index, unsigned int timeout_secs, unsigned int *lock_id_r) { int ret; - i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK); - - if (lock_type == F_RDLCK && index->lock_type != F_UNLCK) { + if (index->lock_type != F_UNLCK) { + /* file is already locked */ index->shared_lock_count++; *lock_id_r = index->lock_id_counter; - ret = 1; - } else if (lock_type == F_WRLCK && index->lock_type == F_WRLCK) { - index->excl_lock_count++; - *lock_id_r = index->lock_id_counter + 1; - ret = 1; - } else { - ret = 0; - } - - if (ret > 0) { - /* file is already locked */ return 1; } @@ -65,39 +53,25 @@ /* FIXME: exclusive locking will rewrite the index file every time. shouldn't really be needed.. reading doesn't require locks then, though */ - if (lock_type == F_WRLCK) - return 0; - index->shared_lock_count++; index->lock_type = F_RDLCK; *lock_id_r = index->lock_id_counter; return 1; } - if (index->file_lock == NULL) { - i_assert(index->lock_type == F_UNLCK); - ret = mail_index_lock_fd(index, index->filepath, index->fd, - lock_type, timeout_secs, - &index->file_lock); - } else { - i_assert(index->lock_type == F_RDLCK && lock_type == F_WRLCK); - ret = file_lock_try_update(index->file_lock, lock_type); - } + i_assert(index->lock_type == F_UNLCK); + ret = mail_index_lock_fd(index, index->filepath, index->fd, + F_RDLCK, timeout_secs, + &index->file_lock); if (ret <= 0) return ret; if (index->lock_type == F_UNLCK) index->lock_id_counter += 2; - index->lock_type = lock_type; + index->lock_type = F_RDLCK; - if (lock_type == F_RDLCK) { - index->shared_lock_count++; - *lock_id_r = index->lock_id_counter; - } else { - index->excl_lock_count++; - *lock_id_r = index->lock_id_counter + 1; - } - + index->shared_lock_count++; + *lock_id_r = index->lock_id_counter; return 1; } @@ -125,7 +99,7 @@ timeout_secs = I_MIN(MAIL_INDEX_SHARED_LOCK_TIMEOUT, index->max_lock_timeout_secs); - ret = mail_index_lock(index, F_RDLCK, timeout_secs, lock_id_r); + ret = mail_index_lock(index, timeout_secs, lock_id_r); if (ret > 0) { mail_index_flush_read_cache(index, index->filepath, index->fd, TRUE); @@ -141,48 +115,23 @@ return -1; } -int mail_index_try_lock_exclusive(struct mail_index *index, - unsigned int *lock_id_r) -{ - int ret; - - if ((ret = mail_index_lock(index, F_WRLCK, 0, lock_id_r)) > 0) { - mail_index_flush_read_cache(index, index->filepath, - index->fd, TRUE); - } - return ret; -} - void mail_index_unlock(struct mail_index *index, unsigned int *_lock_id) { unsigned int lock_id = *_lock_id; *_lock_id = 0; - if ((lock_id & 1) == 0) { - /* shared lock */ - if (!mail_index_is_locked(index, lock_id)) { - /* unlocking some older generation of the index file. - we've already closed the file so just ignore this. */ - return; - } - - i_assert(index->shared_lock_count > 0); - index->shared_lock_count--; - } else { - /* exclusive lock */ - i_assert(lock_id == index->lock_id_counter + 1); - i_assert(index->excl_lock_count > 0); - i_assert(index->lock_type == F_WRLCK); - if (--index->excl_lock_count == 0 && - index->shared_lock_count > 0) { - /* drop back to a shared lock. */ - index->lock_type = F_RDLCK; - (void)file_lock_try_update(index->file_lock, F_RDLCK); - } + /* shared lock */ + if (!mail_index_is_locked(index, lock_id)) { + /* unlocking some older generation of the index file. + we've already closed the file so just ignore this. */ + return; } - if (index->shared_lock_count == 0 && index->excl_lock_count == 0) { + i_assert(index->shared_lock_count > 0); + index->shared_lock_count--; + + if (index->shared_lock_count == 0) { index->lock_id_counter += 2; index->lock_type = F_UNLCK; if (index->lock_method != FILE_LOCK_METHOD_DOTLOCK) {
--- a/src/lib-index/mail-index-map-read.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-map-read.c Sun May 20 03:25:04 2012 +0300 @@ -400,7 +400,6 @@ { int ret; - i_assert(index->lock_type != F_WRLCK); i_assert(!index->mapping); index->mapping = TRUE;
--- a/src/lib-index/mail-index-map.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-map.c Sun May 20 03:25:04 2012 +0300 @@ -339,10 +339,7 @@ dest->records = buffer_get_modifiable_data(dest->buffer, NULL); dest->records_count = src->records_count; - /* if the map is ever written back to disk, we need to keep track of - what has changed. */ - dest->write_seq_first = src->write_seq_first; - dest->write_seq_last = src->write_seq_last; + dest->records_changed = src->records_changed; } static void mail_index_map_copy_header(struct mail_index_map *dest, @@ -405,9 +402,7 @@ mail_index_map_copy_header(mem_map, map); - mem_map->write_atomic = map->write_atomic; - mem_map->write_base_header = map->write_base_header; - mem_map->write_ext_header = map->write_ext_header; + mem_map->header_changed = map->header_changed; /* copy extensions */ if (array_is_created(&map->ext_id_map)) {
--- a/src/lib-index/mail-index-modseq.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-modseq.c Sun May 20 03:25:04 2012 +0300 @@ -463,8 +463,7 @@ } T_END; } } - mail_index_sync_write_seq_update(ctx->sync_map_ctx, 1, - map->hdr.messages_count); + map->rec_map->records_changed = TRUE; mail_transaction_log_view_close(&ctx->log_view); } @@ -515,7 +514,7 @@ buffer_write(map->hdr_copy_buf, ext->hdr_offset, &new_modseq_hdr, sizeof(new_modseq_hdr)); map->hdr_base = map->hdr_copy_buf->data; - map->write_ext_header = TRUE; + map->header_changed = TRUE; } }
--- a/src/lib-index/mail-index-private.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-private.h Sun May 20 03:25:04 2012 +0300 @@ -129,10 +129,8 @@ struct mail_index_map_modseq *modseq; uint32_t last_appended_uid; - /* If this mapping is written to disk and write_atomic=FALSE, - write_seq_* specify the message sequence range that needs to be - written. */ - uint32_t write_seq_first, write_seq_last; + /* The records have changed since it was read */ + bool records_changed; }; struct mail_index_map { @@ -151,9 +149,7 @@ struct mail_index_record_map *rec_map; - unsigned int write_base_header:1; - unsigned int write_ext_header:1; - unsigned int write_atomic:1; /* write to a new file and rename() */ + unsigned int header_changed:1; }; struct mail_index_module_register { @@ -209,7 +205,7 @@ /* syncing will update this if non-NULL */ struct mail_index_transaction_commit_result *sync_commit_result; - int lock_type, shared_lock_count, excl_lock_count; + int lock_type, shared_lock_count; unsigned int lock_id_counter; enum file_lock_method lock_method; unsigned int max_lock_timeout_secs; @@ -279,9 +275,6 @@ /* Returns 0 = ok, -1 = error. */ int mail_index_lock_shared(struct mail_index *index, unsigned int *lock_id_r); -/* Returns 1 = ok, 0 = already locked, -1 = error. */ -int mail_index_try_lock_exclusive(struct mail_index *index, - unsigned int *lock_id_r); void mail_index_unlock(struct mail_index *index, unsigned int *lock_id); /* Returns TRUE if given lock_id is valid. */ bool mail_index_is_locked(struct mail_index *index, unsigned int lock_id);
--- a/src/lib-index/mail-index-sync-ext.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-sync-ext.c Sun May 20 03:25:04 2012 +0300 @@ -310,7 +310,7 @@ i_assert((map->hdr_copy_buf->used % sizeof(uint64_t)) == 0); map->hdr_base = map->hdr_copy_buf->data; map->hdr.header_size = map->hdr_copy_buf->used; - map->write_base_header = map->write_ext_header = TRUE; + map->header_changed = TRUE; ext_hdr = get_ext_header(map, ext); ext_hdr->reset_id = ext->reset_id; @@ -570,8 +570,7 @@ memset(PTR_OFFSET(rec, ext->record_offset), 0, ext->record_size); } - map->rec_map->write_seq_first = 1; - map->rec_map->write_seq_last = view->map->rec_map->records_count; + map->rec_map->records_changed = TRUE; } int mail_index_sync_ext_reset(struct mail_index_sync_map_ctx *ctx, @@ -635,7 +634,7 @@ if (ext->index_idx == ctx->view->index->modseq_ext_id) mail_index_modseq_hdr_update(ctx->modseq_ctx); - map->write_ext_header = TRUE; + map->header_changed = TRUE; return 1; } @@ -684,7 +683,7 @@ return ret; } - mail_index_sync_write_seq_update(ctx, seq, seq); + view->map->rec_map->records_changed = TRUE; /* @UNSAFE */ memcpy(old_data, u + 1, ext->record_size); @@ -786,6 +785,6 @@ return -1; } - mail_index_sync_write_seq_update(ctx, seq, seq); + view->map->rec_map->records_changed = TRUE; return 1; }
--- a/src/lib-index/mail-index-sync-keywords.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-sync-keywords.c Sun May 20 03:25:04 2012 +0300 @@ -215,7 +215,7 @@ if (!mail_index_lookup_seq_range(view, uid1, uid2, &seq1, &seq2)) return 1; - mail_index_sync_write_seq_update(ctx, seq1, seq2); + view->map->rec_map->records_changed = TRUE; mail_index_modseq_update_keyword(ctx->modseq_ctx, keyword_idx, seq1, seq2); @@ -337,7 +337,7 @@ &seq1, &seq2)) continue; - mail_index_sync_write_seq_update(ctx, seq1, seq2); + map->rec_map->records_changed = TRUE; mail_index_modseq_reset_keywords(ctx->modseq_ctx, seq1, seq2); for (seq1--; seq1 < seq2; seq1++) { rec = MAIL_INDEX_MAP_IDX(map, seq1);
--- a/src/lib-index/mail-index-sync-private.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-sync-private.h Sun May 20 03:25:04 2012 +0300 @@ -60,8 +60,6 @@ struct mail_index_map * mail_index_sync_get_atomic_map(struct mail_index_sync_map_ctx *ctx); -void mail_index_sync_write_seq_update(struct mail_index_sync_map_ctx *ctx, - uint32_t seq1, uint32_t seq2); void mail_index_sync_init_expunge_handlers(struct mail_index_sync_map_ctx *ctx); void
--- a/src/lib-index/mail-index-sync-update.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-sync-update.c Sun May 20 03:25:04 2012 +0300 @@ -95,7 +95,6 @@ mail_index_sync_move_to_private_memory(ctx); mail_index_record_map_move_to_private(ctx->view->map); mail_index_modseq_sync_map_replaced(ctx->modseq_ctx); - ctx->view->map->write_atomic = TRUE; return ctx->view->map; } @@ -344,18 +343,6 @@ return 1; } -void mail_index_sync_write_seq_update(struct mail_index_sync_map_ctx *ctx, - uint32_t seq1, uint32_t seq2) -{ - struct mail_index_map *map = ctx->view->map; - - if (map->rec_map->write_seq_first == 0 || - map->rec_map->write_seq_first > seq1) - map->rec_map->write_seq_first = seq1; - if (map->rec_map->write_seq_last < seq2) - map->rec_map->write_seq_last = seq2; -} - static int sync_append(const struct mail_index_record *rec, struct mail_index_sync_map_ctx *ctx) { @@ -396,9 +383,7 @@ map->rec_map->last_appended_uid = rec->uid; new_flags = rec->flags; - mail_index_sync_write_seq_update(ctx, - map->rec_map->records_count, - map->rec_map->records_count); + map->rec_map->records_changed = TRUE; mail_index_modseq_append(ctx->modseq_ctx, map->rec_map->records_count); } @@ -426,7 +411,7 @@ if (!mail_index_lookup_seq_range(view, u->uid1, u->uid2, &seq1, &seq2)) return 1; - mail_index_sync_write_seq_update(ctx, seq1, seq2); + view->map->rec_map->records_changed = TRUE; if (!MAIL_TRANSACTION_FLAG_UPDATE_IS_INTERNAL(u)) { mail_index_modseq_update_flags(ctx->modseq_ctx, u->add_flags | u->remove_flags, @@ -483,7 +468,7 @@ buffer_write(map->hdr_copy_buf, u->offset, u + 1, u->size); map->hdr_base = map->hdr_copy_buf->data; - map->write_base_header = TRUE; + map->header_changed = TRUE; /* @UNSAFE */ if ((uint32_t)(u->offset + u->size) <= sizeof(map->hdr)) { @@ -979,7 +964,7 @@ had_dirty = (map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0; if (had_dirty) { map->hdr.flags &= ~MAIL_INDEX_HDR_FLAG_HAVE_DIRTY; - map->write_base_header = TRUE; + map->header_changed = TRUE; } if (map->hdr_base != map->hdr_copy_buf->data) {
--- a/src/lib-index/mail-index-write.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index-write.c Sun May 20 03:25:04 2012 +0300 @@ -119,152 +119,21 @@ return ret; } -static int mail_index_write_map_over(struct mail_index *index) -{ - struct mail_index_map *map = index->map; - struct mail_index_record_map *rec_map = map->rec_map; - unsigned int base_size; - - if (MAIL_INDEX_IS_IN_MEMORY(index)) - return 0; - - /* write extended headers */ - if (map->write_ext_header) { - base_size = map->hdr.base_header_size; - if (pwrite_full(index->fd, - CONST_PTR_OFFSET(map->hdr_base, base_size), - map->hdr.header_size - base_size, - base_size) < 0) - return -1; - } - - /* write records. */ - if (rec_map->write_seq_first != 0) { - size_t rec_offset = - (rec_map->write_seq_first-1) * map->hdr.record_size; - size_t recs_size = map->hdr.record_size * - (rec_map->write_seq_last - - rec_map->write_seq_first + 1); - - if (pwrite_full(index->fd, - CONST_PTR_OFFSET(rec_map->records, rec_offset), - recs_size, - map->hdr.header_size + rec_offset) < 0) - return -1; - } - - /* Write base header last. If we happen to crash in above pwrites, it - doesn't matter because we haven't yet written log file offsets, so - all the changes will be re-applied and the header/data state will - stay valid. - - The base header changes practically always, so - map->write_base_header might not be TRUE here in all situations. - It's used only to figure out if we want to write the map at all. */ - base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr)); - if (pwrite_full(index->fd, &map->hdr, base_size, 0) < 0) - return -1; - return 0; -} - -static bool mail_index_has_last_changed(struct mail_index *index) -{ - struct mail_index_header hdr; - int ret; - - if ((ret = pread_full(index->fd, &hdr, sizeof(hdr), 0)) <= 0) { - if (ret < 0 && errno != ESTALE) - mail_index_set_syscall_error(index, "pread_full()"); - return TRUE; - } - - return hdr.log_file_head_offset != - index->last_read_log_file_head_offset || - hdr.log_file_seq != index->last_read_log_file_seq; -} - #define mail_index_map_has_changed(map) \ - ((map)->write_base_header || (map)->write_ext_header || \ - (map)->rec_map->write_seq_first != 0) + ((map)->header_changed || (map)->rec_map->records_changed) void mail_index_write(struct mail_index *index, bool want_rotate) { struct mail_index_map *map = index->map; const struct mail_index_header *hdr = &map->hdr; - struct stat st; - unsigned int lock_id; - int ret; i_assert(index->log_sync_locked); if (!mail_index_map_has_changed(map) || index->readonly) return; - if (hdr->base_header_size < sizeof(*hdr)) { - /* header size growed. we can't update this file anymore. */ - map->write_atomic = TRUE; - } - if (index->fd == -1 || index->last_read_log_file_seq == 0) { - /* index file doesn't exist, it's corrupted or we haven't - opened it for some reason */ - map->write_atomic = TRUE; - } - - if (index->last_read_stat.st_size < MAIL_INDEX_MIN_UPDATE_SIZE || - (map->rec_map->write_seq_last - map->rec_map->write_seq_first + 1) + - MAIL_INDEX_MAX_OVERWRITE_NEG_SEQ_COUNT >= - map->rec_map->records_count) { - /* the file is so small that we don't even bother trying to - update it / changes are so large we might as well recreate */ - map->write_atomic = TRUE; - } - - if (!map->write_atomic) { - /* we can't update the file unless it's the same as it was - when we last read it. this is the first quick check before - locking. */ - if (stat(index->filepath, &st) < 0) { - if (errno != ENOENT) - mail_index_set_syscall_error(index, "stat()"); - map->write_atomic = TRUE; - } else if (st.st_ino != index->last_read_stat.st_ino || - !CMP_ST_CTIME(&st, &index->last_read_stat)) - map->write_atomic = TRUE; - } - - if (!map->write_atomic) { - if (mail_index_try_lock_exclusive(index, &lock_id) <= 0) { - /* locking failed, recreate */ - map->write_atomic = TRUE; - } else if (mail_index_has_last_changed(index)) { - /* changed, we can't trust updating it anymore */ - map->write_atomic = TRUE; - mail_index_unlock(index, &lock_id); - } - } - - if (map->write_atomic) { - if (!MAIL_INDEX_IS_IN_MEMORY(index)) { - if (mail_index_recreate(index) < 0) { - mail_index_move_to_memory(index); - return; - } - } - } else { - ret = mail_index_write_map_over(index); - if (ret < 0) - mail_index_set_syscall_error(index, "pwrite_full()"); - else if (index->fsync_mode == FSYNC_MODE_ALWAYS) { - ret = fdatasync(index->fd); - if (ret < 0) { - mail_index_set_syscall_error(index, - "fdatasync()"); - } - } - mail_index_unlock(index, &lock_id); - - if (ret < 0) { - /* hopefully didn't break badly */ + if (!MAIL_INDEX_IS_IN_MEMORY(index)) { + if (mail_index_recreate(index) < 0) { mail_index_move_to_memory(index); return; } @@ -274,10 +143,8 @@ index->last_read_log_file_head_offset = hdr->log_file_head_offset; index->last_read_log_file_tail_offset = hdr->log_file_tail_offset; - map->rec_map->write_seq_first = map->rec_map->write_seq_last = 0; - map->write_atomic = FALSE; - map->write_base_header = FALSE; - map->write_ext_header = FALSE; + map->rec_map->records_changed = FALSE; + map->header_changed = FALSE; if (want_rotate && hdr->log_file_seq == index->log->head->hdr.file_seq &&
--- a/src/lib-index/mail-index.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/mail-index.c Sun May 20 03:25:04 2012 +0300 @@ -570,7 +570,6 @@ i_strconcat(index->dir, "/", index->prefix, NULL); index->shared_lock_count = 0; - index->excl_lock_count = 0; index->lock_type = F_UNLCK; index->lock_id_counter = 2; @@ -629,7 +628,6 @@ index->lock_id_counter += 2; index->lock_type = F_UNLCK; index->shared_lock_count = 0; - index->excl_lock_count = 0; } void mail_index_close(struct mail_index *index) @@ -695,7 +693,6 @@ i_assert(index->shared_lock_count == 0 || (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) == 0); - i_assert(index->excl_lock_count == 0); if (MAIL_INDEX_IS_IN_MEMORY(index)) return 0;
--- a/src/lib-index/test-mail-index-sync-ext.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-index/test-mail-index-sync-ext.c Sun May 20 03:25:04 2012 +0300 @@ -32,9 +32,6 @@ *seq_r = uid; return TRUE; } -void mail_index_sync_write_seq_update(struct mail_index_sync_map_ctx *ctx ATTR_UNUSED, - uint32_t seq1 ATTR_UNUSED, - uint32_t seq2 ATTR_UNUSED) {} static void test_mail_index_sync_ext_atomic_inc(void) {
--- a/src/lib-lda/lmtp-client.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-lda/lmtp-client.c Sun May 20 03:25:04 2012 +0300 @@ -6,6 +6,7 @@ #include "network.h" #include "istream.h" #include "ostream.h" +#include "str.h" #include "dns-lookup.h" #include "lmtp-client.h" @@ -20,7 +21,8 @@ LMTP_INPUT_STATE_MAIL_FROM, LMTP_INPUT_STATE_RCPT_TO, LMTP_INPUT_STATE_DATA_CONTINUE, - LMTP_INPUT_STATE_DATA + LMTP_INPUT_STATE_DATA, + LMTP_INPUT_STATE_XCLIENT }; struct lmtp_rcpt { @@ -44,7 +46,10 @@ enum lmtp_client_protocol protocol; enum lmtp_input_state input_state; const char *global_fail_string; + string_t *input_multiline; + const char **xclient_args; + struct dns_lookup *dns_lookup; struct istream *input; struct ostream *output; struct io *io; @@ -64,6 +69,7 @@ struct istream *data_input; unsigned char output_last; + unsigned int xclient_sent:1; unsigned int rcpt_to_successes:1; unsigned int output_finished:1; unsigned int finish_called:1; @@ -89,15 +95,21 @@ client->set.my_hostname = p_strdup(pool, set->my_hostname); client->set.dns_client_socket_path = p_strdup(pool, set->dns_client_socket_path); + client->set.source_ip = set->source_ip; + client->set.source_port = set->source_port; + client->set.proxy_ttl_plus_1 = set->proxy_ttl_plus_1; client->finish_callback = finish_callback; client->finish_context = context; client->fd = -1; + client->input_multiline = str_new(default_pool, 128); p_array_init(&client->recipients, pool, 16); return client; } void lmtp_client_close(struct lmtp_client *client) { + if (client->dns_lookup != NULL) + dns_lookup_abort(&client->dns_lookup); if (client->io != NULL) io_remove(&client->io); if (client->input != NULL) @@ -138,6 +150,7 @@ i_stream_unref(&client->input); if (client->output != NULL) o_stream_unref(&client->output); + str_free(&client->input_multiline); pool_unref(&client->pool); } @@ -177,6 +190,8 @@ return t_strdup_printf("DATA (%"PRIuUOFF_T"/?)", client->data_input->v_offset); } + case LMTP_INPUT_STATE_XCLIENT: + return "XCLIENT"; } return "??"; } @@ -356,7 +371,8 @@ } } -static int lmtp_input_get_reply_code(const char *line, int *reply_code_r) +static int lmtp_input_get_reply_code(const char *line, int *reply_code_r, + string_t *multiline) { if (!i_isdigit(line[0]) || !i_isdigit(line[1]) || !i_isdigit(line[2])) return -1; @@ -369,7 +385,9 @@ /* final reply */ return 1; } else if (line[3] == '-') { - /* multiline reply. just ignore it. */ + /* multiline reply. */ + str_append(multiline, line); + str_append_c(multiline, '\n'); return 0; } else { /* invalid input */ @@ -377,11 +395,61 @@ } } +static void +lmtp_client_parse_capabilities(struct lmtp_client *client, const char *lines) +{ + const char *const *linep; + + for (linep = t_strsplit(lines, "\n"); *linep != NULL; linep++) { + const char *line = *linep; + + line += 4; /* already checked this is valid */ + if (strncasecmp(line, "XCLIENT ", 8) == 0) { + client->xclient_args = + (void *)p_strsplit(client->pool, line + 8, " "); + } + } +} + +static bool lmtp_client_send_xclient(struct lmtp_client *client) +{ + string_t *str; + unsigned int empty_len; + + if (client->xclient_args == NULL) { + /* not supported */ + return FALSE; + } + if (client->xclient_sent) + return FALSE; + + str = t_str_new(64); + str_append(str, "XCLIENT"); + empty_len = str_len(str); + if (client->set.source_ip.family != 0 && + str_array_icase_find(client->xclient_args, "ADDR")) + str_printfa(str, " ADDR=%s", net_ip2addr(&client->set.source_ip)); + if (client->set.source_port != 0 && + str_array_icase_find(client->xclient_args, "PORT")) + str_printfa(str, " PORT=%u", client->set.source_port); + if (client->set.proxy_ttl_plus_1 != 0 && + str_array_icase_find(client->xclient_args, "TTL")) + str_printfa(str, " TTL=%u", client->set.proxy_ttl_plus_1-1); + + if (str_len(str) == empty_len) + return FALSE; + + str_append(str, "\r\n"); + o_stream_send(client->output, str_data(str), str_len(str)); + return TRUE; +} + static int lmtp_client_input_line(struct lmtp_client *client, const char *line) { int ret, reply_code = 0; - if ((ret = lmtp_input_get_reply_code(line, &reply_code)) <= 0) { + if ((ret = lmtp_input_get_reply_code(line, &reply_code, + client->input_multiline)) <= 0) { if (ret == 0) return 0; lmtp_client_fail(client, line); @@ -390,12 +458,13 @@ switch (client->input_state) { case LMTP_INPUT_STATE_GREET: + case LMTP_INPUT_STATE_XCLIENT: if (reply_code != 220) { lmtp_client_fail(client, line); return -1; } lmtp_client_send_handshake(client); - client->input_state++; + client->input_state = LMTP_INPUT_STATE_LHLO; break; case LMTP_INPUT_STATE_LHLO: case LMTP_INPUT_STATE_MAIL_FROM: @@ -403,6 +472,15 @@ lmtp_client_fail(client, line); return -1; } + str_append(client->input_multiline, line); + lmtp_client_parse_capabilities(client, + str_c(client->input_multiline)); + if (client->input_state == LMTP_INPUT_STATE_LHLO && + lmtp_client_send_xclient(client)) { + client->input_state = LMTP_INPUT_STATE_XCLIENT; + client->xclient_sent = TRUE; + break; + } if (client->input_state == LMTP_INPUT_STATE_LHLO) { o_stream_send_str(client->output, t_strdup_printf("MAIL FROM:%s\r\n", @@ -435,21 +513,27 @@ return -1; break; } - return 0; + return 1; } static void lmtp_client_input(struct lmtp_client *client) { const char *line; + int ret; lmtp_client_ref(client); o_stream_cork(client->output); while ((line = i_stream_read_next_line(client->input)) != NULL) { - if (lmtp_client_input_line(client, line) < 0) { + T_BEGIN { + ret = lmtp_client_input_line(client, line); + } T_END; + if (ret < 0) { o_stream_uncork(client->output); lmtp_client_unref(&client); return; } + if (ret > 0) + str_truncate(client->input_multiline, 0); } if (client->input->stream_errno != 0) { @@ -519,6 +603,8 @@ static void lmtp_client_dns_done(const struct dns_lookup_result *result, struct lmtp_client *client) { + client->dns_lookup = NULL; + if (result->ret != 0) { i_error("lmtp client: DNS lookup of %s failed: %s", client->host, result->error); @@ -570,7 +656,8 @@ client->ip = ips[0]; } else { if (dns_lookup(host, &dns_lookup_set, - lmtp_client_dns_done, client) < 0) + lmtp_client_dns_done, client, + &client->dns_lookup) < 0) return -1; return 0; }
--- a/src/lib-lda/lmtp-client.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-lda/lmtp-client.h Sun May 20 03:25:04 2012 +0300 @@ -1,6 +1,8 @@ #ifndef LMTP_CLIENT_H #define LMTP_CLIENT_H +#include "network.h" + #define ERRSTR_TEMP_REMOTE_FAILURE "451 4.4.0 Remote server not answering" /* LMTP/SMTP client code. */ @@ -14,6 +16,13 @@ const char *my_hostname; const char *mail_from; const char *dns_client_socket_path; + + /* if remote server supports XCLIENT capability, + send the these as ADDR/PORT */ + struct ip_addr source_ip; + unsigned int source_port; + /* send TTL as this -1, so the default 0 means "don't send it" */ + unsigned int proxy_ttl_plus_1; }; /* reply contains the reply coming from remote server, or NULL
--- a/src/lib-lda/mail-send.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-lda/mail-send.c Sun May 20 03:25:04 2012 +0300 @@ -4,6 +4,7 @@ #include "ioloop.h" #include "hostpid.h" #include "istream.h" +#include "ostream.h" #include "str.h" #include "str-sanitize.h" #include "var-expand.h" @@ -55,12 +56,10 @@ struct mail *mail = ctx->src_mail; struct istream *input; struct smtp_client *smtp_client; - FILE *f; + struct ostream *output; const char *return_addr, *hdr; - const unsigned char *data; const char *value, *msgid, *orig_msgid, *boundary; string_t *str; - size_t size; int ret; if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0) @@ -87,76 +86,75 @@ str_sanitize(reason, 512)); } - smtp_client = smtp_client_open(ctx->set, return_addr, NULL, &f); + smtp_client = smtp_client_open(ctx->set, return_addr, NULL, &output); msgid = mail_deliver_get_new_message_id(ctx); boundary = t_strdup_printf("%s/%s", my_pid, ctx->set->hostname); - fprintf(f, "Message-ID: %s\r\n", msgid); - fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time)); - fprintf(f, "From: Mail Delivery Subsystem <%s>\r\n", - ctx->set->postmaster_address); - fprintf(f, "To: <%s>\r\n", return_addr); - fprintf(f, "MIME-Version: 1.0\r\n"); - fprintf(f, "Content-Type: " - "multipart/report; report-type=%s;\r\n" - "\tboundary=\"%s\"\r\n", - ctx->dsn ? "delivery-status" : "disposition-notification", - boundary); - - str = t_str_new(256); + str = t_str_new(512); + str_printfa(str, "Message-ID: %s\r\n", msgid); + str_printfa(str, "Date: %s\r\n", message_date_create(ioloop_time)); + str_printfa(str, "From: Mail Delivery Subsystem <%s>\r\n", + ctx->set->postmaster_address); + str_printfa(str, "To: <%s>\r\n", return_addr); + str_append(str, "MIME-Version: 1.0\r\n"); + str_printfa(str, "Content-Type: " + "multipart/report; report-type=%s;\r\n" + "\tboundary=\"%s\"\r\n", + ctx->dsn ? "delivery-status" : "disposition-notification", + boundary); + str_append(str, "Subject: "); var_expand(str, ctx->set->rejection_subject, get_var_expand_table(mail, reason, recipient)); - fprintf(f, "Subject: %s\r\n", str_c(str)); + str_append(str, "\r\n"); - fprintf(f, "Auto-Submitted: auto-replied (rejected)\r\n"); - fprintf(f, "Precedence: bulk\r\n"); - fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); + str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n"); + str_append(str, "Precedence: bulk\r\n"); + str_append(str, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); /* human readable status report */ - fprintf(f, "--%s\r\n", boundary); - fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n"); - fprintf(f, "Content-Disposition: inline\r\n"); - fprintf(f, "Content-Transfer-Encoding: 8bit\r\n\r\n"); + str_printfa(str, "--%s\r\n", boundary); + str_append(str, "Content-Type: text/plain; charset=utf-8\r\n"); + str_append(str, "Content-Disposition: inline\r\n"); + str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n"); - str_truncate(str, 0); var_expand(str, ctx->set->rejection_reason, get_var_expand_table(mail, reason, recipient)); - fprintf(f, "%s\r\n", str_c(str)); + str_append(str, "\r\n"); if (ctx->dsn) { /* DSN status report: For LDA rejects. currently only used when user is out of quota */ - fprintf(f, "--%s\r\n" - "Content-Type: message/delivery-status\r\n\r\n", - boundary); - fprintf(f, "Reporting-MTA: dns; %s\r\n", - ctx->set->hostname); + str_printfa(str, "--%s\r\n" + "Content-Type: message/delivery-status\r\n\r\n", + boundary); + str_printfa(str, "Reporting-MTA: dns; %s\r\n", ctx->set->hostname); if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0) - fprintf(f, "Original-Recipient: rfc822; %s\r\n", hdr); - fprintf(f, "Final-Recipient: rfc822; %s\r\n", recipient); - fprintf(f, "Action: failed\r\n"); - fprintf(f, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0"); + str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr); + str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient); + str_append(str, "Action: failed\r\n"); + str_printfa(str, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0"); } else { /* MDN status report: For Sieve "reject" */ - fprintf(f, "--%s\r\n" - "Content-Type: message/disposition-notification\r\n\r\n", - boundary); - fprintf(f, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n", - ctx->set->hostname); + str_printfa(str, "--%s\r\n" + "Content-Type: message/disposition-notification\r\n\r\n", + boundary); + str_printfa(str, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n", + ctx->set->hostname); if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0) - fprintf(f, "Original-Recipient: rfc822; %s\r\n", hdr); - fprintf(f, "Final-Recipient: rfc822; %s\r\n", recipient); + str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr); + str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient); if (orig_msgid != NULL) - fprintf(f, "Original-Message-ID: %s\r\n", orig_msgid); - fprintf(f, "Disposition: " - "automatic-action/MDN-sent-automatically; deleted\r\n"); + str_printfa(str, "Original-Message-ID: %s\r\n", orig_msgid); + str_append(str, "Disposition: " + "automatic-action/MDN-sent-automatically; deleted\r\n"); } - fprintf(f, "\r\n"); + str_append(str, "\r\n"); /* original message's headers */ - fprintf(f, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary); + str_printfa(str, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary); + o_stream_send(output, str_data(str), str_len(str)); if (mail_get_hdr_stream(mail, NULL, &input) == 0) { /* Note: If you add more headers, they need to be sorted. @@ -173,17 +171,15 @@ N_ELEMENTS(exclude_headers), null_header_filter_callback, NULL); - while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { - if (fwrite(data, size, 1, f) == 0) - break; - i_stream_skip(input, size); - } + ret = o_stream_send_istream(output, input); i_stream_unref(&input); i_assert(ret != 0); } - fprintf(f, "\r\n\r\n--%s--\r\n", boundary); + str_truncate(str, 0); + str_printfa(str, "\r\n\r\n--%s--\r\n", boundary); + o_stream_send(output, str_data(str), str_len(str)); return smtp_client_close(smtp_client); } @@ -193,11 +189,9 @@ "Return-Path" }; struct istream *input; + struct ostream *output; struct smtp_client *smtp_client; - FILE *f; - const unsigned char *data; const char *return_path; - size_t size; if (mail_get_stream(ctx->src_mail, NULL, NULL, &input) < 0) return -1; @@ -208,18 +202,14 @@ forwardto, return_path); } - smtp_client = smtp_client_open(ctx->set, forwardto, return_path, &f); + smtp_client = smtp_client_open(ctx->set, forwardto, return_path, &output); input = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers, N_ELEMENTS(hide_headers), null_header_filter_callback, NULL); - while (i_stream_read_data(input, &data, &size, 0) > 0) { - if (fwrite(data, size, 1, f) == 0) - break; - i_stream_skip(input, size); - } + o_stream_send_istream(output, input); i_stream_unref(&input); return smtp_client_close(smtp_client);
--- a/src/lib-lda/smtp-client.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-lda/smtp-client.c Sun May 20 03:25:04 2012 +0300 @@ -2,11 +2,13 @@ #include "lib.h" #include "ioloop.h" +#include "buffer.h" #include "str.h" #include "close-keep-errno.h" #include "safe-mkstemp.h" #include "execv-const.h" #include "istream.h" +#include "ostream.h" #include "master-service.h" #include "lmtp-client.h" #include "lda-settings.h" @@ -20,7 +22,9 @@ #define DEFAULT_SUBMISSION_PORT 25 struct smtp_client { - FILE *f; + struct ostream *output; + buffer_t *buf; + int temp_fd; pid_t pid; bool use_smtp; @@ -33,15 +37,17 @@ char *return_path; }; -static struct smtp_client *smtp_client_devnull(FILE **file_r) +static struct smtp_client *smtp_client_devnull(struct ostream **output_r) { struct smtp_client *client; - + client = i_new(struct smtp_client, 1); - client->f = *file_r = fopen("/dev/null", "w"); - if (client->f == NULL) - i_fatal("fopen() failed: %m"); + client->buf = buffer_create_dynamic(default_pool, 1); + client->output = o_stream_create_buffer(client->buf); + o_stream_close(client->output); client->pid = (pid_t)-1; + + *output_r = client->output; return client; } @@ -76,7 +82,7 @@ static struct smtp_client * smtp_client_open_sendmail(const struct lda_settings *set, const char *destination, const char *return_path, - FILE **file_r) + struct ostream **output_r) { struct smtp_client *client; int fd[2]; @@ -84,13 +90,13 @@ if (pipe(fd) < 0) { i_error("pipe() failed: %m"); - return smtp_client_devnull(file_r); + return smtp_client_devnull(output_r); } if ((pid = fork()) == (pid_t)-1) { i_error("fork() failed: %m"); (void)close(fd[0]); (void)close(fd[1]); - return smtp_client_devnull(file_r); + return smtp_client_devnull(output_r); } if (pid == 0) { /* child */ @@ -100,10 +106,10 @@ (void)close(fd[0]); client = i_new(struct smtp_client, 1); - client->f = *file_r = fdopen(fd[1], "w"); + client->output = o_stream_create_fd(fd[1], IO_BLOCK_SIZE, TRUE); client->pid = pid; - if (client->f == NULL) - i_fatal("fdopen() failed: %m"); + + *output_r = client->output; return client; } @@ -137,7 +143,7 @@ struct smtp_client * smtp_client_open(const struct lda_settings *set, const char *destination, - const char *return_path, FILE **file_r) + const char *return_path, struct ostream **output_r) { struct smtp_client *client; const char *path; @@ -145,21 +151,22 @@ if (*set->submission_host == '\0') { return smtp_client_open_sendmail(set, destination, - return_path, file_r); + return_path, output_r); } if ((fd = create_temp_file(&path)) == -1) - return smtp_client_devnull(file_r); + return smtp_client_devnull(output_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->temp_fd = fd; + client->output = o_stream_create_fd(fd, IO_BLOCK_SIZE, TRUE); client->use_smtp = TRUE; + + *output_r = client->output; return client; } @@ -167,7 +174,7 @@ { int ret = EX_TEMPFAIL, status; - fclose(client->f); + o_stream_destroy(&client->output); if (client->pid == (pid_t)-1) { /* smtp_client_open() failed already */ @@ -189,6 +196,8 @@ i_error("Sendmail process terminated abnormally, " "return status %d", status); } + if (client->buf != NULL) + buffer_free(&client->buf); i_free(client); return ret; } @@ -247,12 +256,12 @@ } } - if (fflush(smtp_client->f) != 0) { - i_error("fflush(%s) failed: %m", smtp_client->temp_path); + if (o_stream_flush(smtp_client->output) < 0) { + i_error("write(%s) failed: %m", smtp_client->temp_path); return -1; } - if (lseek(fileno(smtp_client->f), 0, SEEK_SET) < 0) { + if (o_stream_seek(smtp_client->output, 0) < 0) { i_error("lseek(%s) failed: %m", smtp_client->temp_path); return -1; } @@ -276,7 +285,7 @@ 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); + input = i_stream_create_fd(smtp_client->temp_fd, (size_t)-1, FALSE); lmtp_client_send(client, input); i_stream_unref(&input); @@ -296,7 +305,7 @@ /* the mail has been written to a file. now actually send it. */ ret = smtp_client_send(client); - fclose(client->f); + o_stream_destroy(&client->output); i_free(client->return_path); i_free(client->destination); i_free(client->temp_path);
--- a/src/lib-lda/smtp-client.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-lda/smtp-client.h Sun May 20 03:25:04 2012 +0300 @@ -5,7 +5,7 @@ struct smtp_client * smtp_client_open(const struct lda_settings *set, const char *destination, - const char *return_path, FILE **file_r); + const char *return_path, struct ostream **output_r); /* Returns sysexits-compatible return value */ int smtp_client_close(struct smtp_client *client);
--- a/src/lib-mail/message-parser.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-mail/message-parser.c Sun May 20 03:25:04 2012 +0300 @@ -54,6 +54,8 @@ struct message_block *block_r); static int parse_next_body_to_eof(struct message_parser_ctx *ctx, struct message_block *block_r); +static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx, + struct message_block *block_r); static int preparsed_parse_next_header_init(struct message_parser_ctx *ctx, struct message_block *block_r); @@ -271,9 +273,17 @@ block_r->size = (ptr - block_r->data) + 1; parse_body_add_block(ctx, block_r); - /* a new MIME part begins */ - ctx->parse_next_block = parse_next_mime_header_init; - return 1; + if (ctx->boundaries == NULL || ctx->boundaries->part != ctx->part) { + /* epilogue */ + if (ctx->boundaries != NULL) + ctx->parse_next_block = parse_next_body_to_boundary; + else + ctx->parse_next_block = parse_next_body_to_eof; + } else { + /* a new MIME part begins */ + ctx->parse_next_block = parse_next_mime_header_init; + } + return ctx->parse_next_block(ctx, block_r); } static int parse_part_finish(struct message_parser_ctx *ctx, @@ -293,28 +303,24 @@ if (boundary->epilogue_found) { /* this boundary isn't needed anymore */ ctx->boundaries = boundary->next; - - if (ctx->boundaries != NULL) - ctx->parse_next_block = parse_next_body_to_boundary; - else - ctx->parse_next_block = parse_next_body_to_eof; - return ctx->parse_next_block(ctx, block_r); + } else { + /* forget about the boundaries we possibly skipped */ + ctx->boundaries = boundary; } - /* forget about the boundaries we possibly skipped */ - ctx->boundaries = boundary; - /* the boundary itself should already be in buffer. add that. */ block_r->data = i_stream_get_data(ctx->input, &block_r->size); i_assert(block_r->size >= ctx->skip + 2 + boundary->len + (first_line ? 0 : 1)); block_r->data += ctx->skip; - /* [\n]--<boundary> */ - block_r->size = (first_line ? 0 : 1) + 2 + boundary->len; + /* [\n]--<boundary>[--] */ + block_r->size = (first_line ? 0 : 1) + 2 + boundary->len + + (boundary->epilogue_found ? 2 : 0); parse_body_add_block(ctx, block_r); ctx->parse_next_block = parse_next_body_skip_boundary_line; - return 1; + + return ctx->parse_next_block(ctx, block_r); } static int parse_next_body_to_boundary(struct message_parser_ctx *ctx, @@ -392,6 +398,11 @@ } if (block_r->size != 0) { parse_body_add_block(ctx, block_r); + + if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && + (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) == 0) + return 0; + return 1; } return ret <= 0 ? ret : @@ -408,6 +419,11 @@ return ret; parse_body_add_block(ctx, block_r); + + if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && + (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) == 0) + return 0; + return 1; } @@ -583,6 +599,24 @@ ctx->part = ctx->part->next; break; } + + /* parse epilogue of multipart parent if requested */ + if (ctx->part->parent != NULL && + (ctx->part->parent->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && + (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) { + /* check for presence of epilogue */ + uoff_t part_end = ctx->part->physical_pos + + ctx->part->header_size.physical_size + + ctx->part->body_size.physical_size; + uoff_t parent_end = ctx->part->parent->physical_pos + + ctx->part->parent->header_size.physical_size + + ctx->part->parent->body_size.physical_size; + + if (parent_end > part_end) { + ctx->parse_next_block = preparsed_parse_epilogue_init; + break; + } + } ctx->part = ctx->part->parent; } if (ctx->part == NULL) @@ -599,6 +633,17 @@ return ctx->parse_next_block(ctx, block_r); } +static int preparsed_parse_prologue_finish(struct message_parser_ctx *ctx, + struct message_block *block_r) +{ + i_stream_skip(ctx->input, ctx->skip); + ctx->skip = 0; + + ctx->parse_next_block = preparsed_parse_next_header_init; + ctx->part = ctx->part->children; + return ctx->parse_next_block(ctx, block_r); +} + static int preparsed_parse_body_more(struct message_parser_ctx *ctx, struct message_block *block_r) { @@ -619,6 +664,143 @@ return 1; } +static int preparsed_parse_prologue_more(struct message_parser_ctx *ctx, + struct message_block *block_r) +{ + uoff_t end_offset = ctx->part->children->physical_pos; + uoff_t boundary_min_start; + const unsigned char *cur; + bool full; + int ret; + + if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) + return ret; + + if (ctx->input->v_offset + block_r->size >= end_offset) { + /* we've got the full prologue: clip off the initial boundary */ + block_r->size = end_offset - ctx->input->v_offset; + cur = block_r->data + block_r->size - 1; + + /* [\r]\n--boundary[\r]\n */ + if (block_r->size < 5 || *cur != '\n') { + ctx->broken = TRUE; + return -1; + } + + cur--; + if (*cur == '\r') cur--; + + /* find newline just before boundary */ + for (; cur >= block_r->data; cur--) { + if (*cur == '\n') break; + } + + if (cur[0] != '\n' || cur[1] != '-' || cur[2] != '-') { + ctx->broken = TRUE; + return -1; + } + + if (cur != block_r->data && cur[-1] == '\r') cur--; + + /* clip boundary */ + block_r->size = cur - block_r->data; + + ctx->parse_next_block = preparsed_parse_prologue_finish; + ctx->skip = block_r->size; + return 1; + } + + /* retain enough data in the stream buffer to contain initial boundary */ + if (end_offset > BOUNDARY_END_MAX_LEN) + boundary_min_start = end_offset - BOUNDARY_END_MAX_LEN; + else + boundary_min_start = 0; + + if (ctx->input->v_offset + block_r->size >= boundary_min_start) { + if (boundary_min_start <= ctx->input->v_offset) + return 0; + block_r->size = boundary_min_start - ctx->input->v_offset; + } + ctx->skip = block_r->size; + return 1; +} + +static int preparsed_parse_epilogue_more(struct message_parser_ctx *ctx, + struct message_block *block_r) +{ + uoff_t end_offset = ctx->part->physical_pos + + ctx->part->header_size.physical_size + + ctx->part->body_size.physical_size; + bool full; + int ret; + + if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) + return ret; + + if (ctx->input->v_offset + block_r->size >= end_offset) { + block_r->size = end_offset - ctx->input->v_offset; + ctx->parse_next_block = preparsed_parse_body_finish; + } + ctx->skip = block_r->size; + return 1; +} + +static int preparsed_parse_epilogue_boundary(struct message_parser_ctx *ctx, + struct message_block *block_r) +{ + uoff_t end_offset = ctx->part->physical_pos + + ctx->part->header_size.physical_size + + ctx->part->body_size.physical_size; + const unsigned char *data, *cur; + size_t size; + bool full; + int ret; + + if (end_offset - ctx->input->v_offset < 7) { + ctx->broken = TRUE; + return -1; + } + + if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) + return ret; + + /* [\r]\n--boundary--[\r]\n */ + if (block_r->size < 7) { + ctx->want_count = 7; + return 0; + } + + data = block_r->data; + size = block_r->size; + cur = data; + + if (*cur == '\r') cur++; + + if (cur[0] != '\n' || cur[1] != '-' || data[2] != '-') { + ctx->broken = TRUE; + return -1; + } + + /* find the end of the line */ + cur += 3; + if ((cur = memchr(cur, '\n', size - (cur-data))) == NULL) { + if (end_offset < ctx->input->v_offset + size) { + ctx->broken = TRUE; + return -1; + } else if (ctx->input->v_offset + size < end_offset && + size < BOUNDARY_END_MAX_LEN && + !ctx->input->eof && !full) { + ctx->want_count = BOUNDARY_END_MAX_LEN; + return 0; + } + } + + block_r->size = 0; + ctx->parse_next_block = preparsed_parse_epilogue_more; + ctx->skip = cur - data + 1; + return 0; +} + static int preparsed_parse_body_init(struct message_parser_ctx *ctx, struct message_block *block_r) { @@ -632,16 +814,45 @@ } i_stream_skip(ctx->input, offset - ctx->input->v_offset); - ctx->parse_next_block = preparsed_parse_body_more; - return preparsed_parse_body_more(ctx, block_r); + if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0) + ctx->parse_next_block = preparsed_parse_body_more; + else + ctx->parse_next_block = preparsed_parse_prologue_more; + return ctx->parse_next_block(ctx, block_r); +} + +static int preparsed_parse_epilogue_init(struct message_parser_ctx *ctx, + struct message_block *block_r) +{ + uoff_t offset = ctx->part->physical_pos + + ctx->part->header_size.physical_size + + ctx->part->body_size.physical_size; + + ctx->part = ctx->part->parent; + + if (offset < ctx->input->v_offset) { + /* last child was actually larger than the cached size + suggested */ + ctx->broken = TRUE; + return -1; + } + i_stream_skip(ctx->input, offset - ctx->input->v_offset); + + ctx->parse_next_block = preparsed_parse_epilogue_boundary; + return ctx->parse_next_block(ctx, block_r); } static int preparsed_parse_finish_header(struct message_parser_ctx *ctx, struct message_block *block_r) { if (ctx->part->children != NULL) { - ctx->parse_next_block = preparsed_parse_next_header_init; - ctx->part = ctx->part->children; + if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 && + (ctx->flags & MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS) != 0) + ctx->parse_next_block = preparsed_parse_body_init; + else { + ctx->parse_next_block = preparsed_parse_next_header_init; + ctx->part = ctx->part->children; + } } else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) { ctx->parse_next_block = preparsed_parse_body_init; } else {
--- a/src/lib-mail/message-parser.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-mail/message-parser.h Sun May 20 03:25:04 2012 +0300 @@ -10,7 +10,9 @@ /* Buggy software creates Content-Type: headers without Mime-Version: header. By default we allow this and assume message is MIME if Content-Type: is found. This flag disables this. */ - MESSAGE_PARSER_FLAG_MIME_VERSION_STRICT = 0x02 + MESSAGE_PARSER_FLAG_MIME_VERSION_STRICT = 0x02, + /* Return multipart (preamble and epilogue) blocks */ + MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS = 0x04 }; /* Note that these flags are used directly by message-parser-serialize, so
--- a/src/lib-storage/index/imapc/imapc-storage.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-storage/index/imapc/imapc-storage.c Sun May 20 03:25:04 2012 +0300 @@ -705,10 +705,11 @@ static void imapc_notify_changes(struct mailbox *box) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; + const struct mail_storage_settings *set = box->storage->set; struct imapc_command *cmd; enum imapc_capability capa; - if (box->notify_min_interval == 0) { + if (box->notify_callback == NULL) { if (mbox->to_idle_check != NULL) timeout_remove(&mbox->to_idle_check); return; @@ -728,7 +729,7 @@ check for changes with NOOP every once in a while. */ i_assert(!imapc_client_is_running(mbox->storage->client)); mbox->to_idle_check = - timeout_add(box->notify_min_interval * 1000, + timeout_add(set->mailbox_idle_check_interval * 1000, imapc_idle_timeout, mbox); } }
--- a/src/lib-storage/index/index-mailbox-check.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-storage/index/index-mailbox-check.c Sun May 20 03:25:04 2012 +0300 @@ -67,12 +67,13 @@ void index_mailbox_check_add(struct mailbox *box, const char *path) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); + const struct mail_storage_settings *set = box->storage->set; struct index_notify_file *file; struct stat st; struct io *io = NULL; struct index_notify_io *aio; - i_assert(box->notify_min_interval > 0); + i_assert(set->mailbox_idle_check_interval > 0); (void)io_add_notify(path, notify_callback, box, &io); if (io != NULL) { @@ -94,7 +95,7 @@ * when the filesystem is remote (NFS, ...) */ if (ibox->notify_to == NULL) { ibox->notify_to = - timeout_add(box->notify_min_interval * 1000, + timeout_add(set->mailbox_idle_check_interval * 1000, check_timeout, box); } }
--- a/src/lib-storage/index/pop3c/pop3c-client.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-storage/index/pop3c/pop3c-client.c Sun May 20 03:25:04 2012 +0300 @@ -50,6 +50,7 @@ struct ostream *output, *raw_output; struct ssl_iostream *ssl_iostream; struct timeout *to; + struct dns_lookup *dns_lookup; enum pop3c_client_state state; enum pop3c_capability capabilities; @@ -66,7 +67,8 @@ }; static void -pop3c_dns_callback(const struct dns_lookup_result *result, void *context); +pop3c_dns_callback(const struct dns_lookup_result *result, + struct pop3c_client *client); struct pop3c_client * pop3c_client_init(const struct pop3c_client_settings *set) @@ -134,6 +136,8 @@ if (client->running) io_loop_stop(current_ioloop); + if (client->dns_lookup != NULL) + dns_lookup_abort(&client->dns_lookup); if (client->to != NULL) timeout_remove(&client->to); if (client->io != NULL) @@ -215,7 +219,8 @@ client->set.dns_client_socket_path; dns_set.timeout_msecs = POP3C_DNS_LOOKUP_TIMEOUT_MSECS; (void)dns_lookup(client->set.host, &dns_set, - pop3c_dns_callback, client); + pop3c_dns_callback, client, + &client->dns_lookup); } else if (client->to == NULL) { client->to = timeout_add(POP3C_COMMAND_TIMEOUT_MSECS, pop3c_client_timeout, client); @@ -542,9 +547,10 @@ } static void -pop3c_dns_callback(const struct dns_lookup_result *result, void *context) +pop3c_dns_callback(const struct dns_lookup_result *result, + struct pop3c_client *client) { - struct pop3c_client *client = context; + client->dns_lookup = NULL; if (result->ret != 0) { i_error("pop3c(%s): dns_lookup() failed: %s",
--- a/src/lib-storage/mail-storage-private.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-storage/mail-storage-private.h Sun May 20 03:25:04 2012 +0300 @@ -243,7 +243,6 @@ struct mail_index_view *tmp_sync_view; /* Mailbox notification settings: */ - unsigned int notify_min_interval; mailbox_notify_callback_t *notify_callback; void *notify_context;
--- a/src/lib-storage/mail-storage.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-storage/mail-storage.c Sun May 20 03:25:04 2012 +0300 @@ -1370,10 +1370,9 @@ } #undef mailbox_notify_changes -void mailbox_notify_changes(struct mailbox *box, unsigned int min_interval, +void mailbox_notify_changes(struct mailbox *box, mailbox_notify_callback_t *callback, void *context) { - box->notify_min_interval = min_interval; box->notify_callback = callback; box->notify_context = context; @@ -1382,7 +1381,7 @@ void mailbox_notify_changes_stop(struct mailbox *box) { - mailbox_notify_changes(box, 0, NULL, NULL); + mailbox_notify_changes(box, NULL, NULL); } struct mail_search_context *
--- a/src/lib-storage/mail-storage.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib-storage/mail-storage.h Sun May 20 03:25:04 2012 +0300 @@ -491,16 +491,16 @@ int mailbox_sync(struct mailbox *box, enum mailbox_sync_flags flags); /* Call given callback function when something changes in the mailbox. */ -void mailbox_notify_changes(struct mailbox *box, unsigned int min_interval, +void mailbox_notify_changes(struct mailbox *box, mailbox_notify_callback_t *callback, void *context); #ifdef CONTEXT_TYPE_SAFETY -# define mailbox_notify_changes(box, min_interval, callback, context) \ +# define mailbox_notify_changes(box, callback, context) \ ({(void)(1 ? 0 : callback((struct mailbox *)NULL, context)); \ - mailbox_notify_changes(box, min_interval, \ + mailbox_notify_changes(box, \ (mailbox_notify_callback_t *)callback, context); }) #else -# define mailbox_notify_changes(box, min_interval, callback, context) \ - mailbox_notify_changes(box, min_interval, \ +# define mailbox_notify_changes(box, callback, context) \ + mailbox_notify_changes(box, \ (mailbox_notify_callback_t *)callback, context) #endif void mailbox_notify_changes_stop(struct mailbox *box);
--- a/src/lib/network.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lib/network.c Sun May 20 03:25:04 2012 +0300 @@ -13,8 +13,10 @@ #include <ctype.h> #include <sys/un.h> #include <netinet/tcp.h> -#ifdef HAVE_UCRED_H +#if defined(HAVE_UCRED_H) # include <ucred.h> /* for getpeerucred() */ +#elif defined(HAVE_SYS_UCRED_H) +# include <sys/ucred.h> /* for FreeBSD struct xucred */ #endif union sockaddr_union { @@ -694,16 +696,14 @@ int net_getunixcred(int fd, struct net_unix_cred *cred_r) { -#if defined(HAVE_GETPEEREID) - /* OSX 10.4+, FreeBSD 4.6+, OpenBSD 3.0+, NetBSD 5.0+ */ - if (getpeereid(fd, &cred_r->uid, &cred_r->gid) < 0) { - i_error("getpeereid() failed: %m"); - return -1; - } - return 0; -#elif defined(SO_PEERCRED) +#if defined(SO_PEERCRED) +# if defined(HAVE_STRUCT_SOCKPEERCRED) + /* OpenBSD (may also provide getpeereid, but we also want pid) */ + struct sockpeercred ucred; +# else /* Linux */ struct ucred ucred; +# endif socklen_t len = sizeof(ucred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { @@ -712,6 +712,48 @@ } cred_r->uid = ucred.uid; cred_r->gid = ucred.gid; + cred_r->pid = ucred.pid; + return 0; +#elif defined(LOCAL_PEEREID) + /* NetBSD (may also provide getpeereid, but we also want pid) */ + struct unpcbid ucred; + socklen_t len = sizeof(ucred); + + if (getsockopt(s, 0, LOCAL_PEEREID, &ucred, &len) < 0) { + i_error("getsockopt(LOCAL_PEEREID) failed: %m"); + return -1; + } + + cred_r->uid = ucred.unp_euid; + cred_r->gid = ucred.unp_egid; + cred_r->pid = ucred.unp_pid; + return 0; +#elif defined(HAVE_GETPEEREID) + /* OSX 10.4+, FreeBSD 4.6+, OpenBSD 3.0+, NetBSD 5.0+ */ + if (getpeereid(fd, &cred_r->uid, &cred_r->gid) < 0) { + i_error("getpeereid() failed: %m"); + return -1; + } + cred_r->pid = (pid_t)-1; + return 0; +#elif defined(LOCAL_PEERCRED) + /* Older FreeBSD */ + struct xucred ucred; + socklen_t len = sizeof(ucred); + + if (getsockopt(fd, 0, LOCAL_PEERCRED, &ucred, &len) < 0) { + i_error("getsockopt(LOCAL_PEERCRED) failed: %m"); + return -1; + } + + if (ucred.cr_version != XUCRED_VERSION) { + errno = EINVAL; + return -1; + } + + cred_r->uid = ucred.cr_uid; + cred_r->gid = ucred.cr_gid; + cred_r->pid = (pid_t)-1; return 0; #elif defined(HAVE_GETPEERUCRED) /* Solaris */ @@ -723,6 +765,7 @@ } cred_r->uid = ucred_geteuid(ucred); cred_r->gid = ucred_getrgid(ucred); + cred_r->pid = ucred_getpid(ucred); ucred_free(ucred); if (cred_r->uid == (uid_t)-1 ||
--- a/src/lib/network.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lib/network.h Sun May 20 03:25:04 2012 +0300 @@ -34,6 +34,7 @@ struct net_unix_cred { uid_t uid; gid_t gid; + pid_t pid; }; /* maxmimum string length of IP address */ @@ -115,7 +116,8 @@ int net_getpeername(int fd, struct ip_addr *addr, unsigned int *port); /* Get UNIX socket name. */ int net_getunixname(int fd, const char **name_r); -/* Get UNIX socket peer process's credentials. */ +/* Get UNIX socket peer process's credentials. The pid may be (pid_t)-1 if + unavailable. */ int net_getunixcred(int fd, struct net_unix_cred *cred_r); /* Returns ip_addr as string, or NULL if ip is invalid. */
--- a/src/lmtp/client.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lmtp/client.c Sun May 20 03:25:04 2012 +0300 @@ -68,6 +68,8 @@ return cmd_rset(client, args); if (strcmp(cmd, "NOOP") == 0) return cmd_noop(client, args); + if (strcmp(cmd, "XCLIENT") == 0) + return cmd_xclient(client, args); client_send_line(client, "502 5.5.2 Unknown command"); return 0; @@ -225,6 +227,7 @@ client_generate_session_id(client); client->my_domain = client->set->hostname; client->lhlo = i_strdup("missing"); + client->proxy_ttl = LMTP_PROXY_DEFAULT_TTL; DLLIST_PREPEND(&clients, client); clients_count++; @@ -342,6 +345,29 @@ va_end(args); } +bool client_is_trusted(struct client *client) +{ + const char *const *net; + struct ip_addr net_ip; + unsigned int bits; + + if (client->lmtp_set->login_trusted_networks == NULL) + return FALSE; + + net = t_strsplit_spaces(client->lmtp_set->login_trusted_networks, ", "); + for (; *net != NULL; net++) { + if (net_parse_range(*net, &net_ip, &bits) < 0) { + i_error("login_trusted_networks: " + "Invalid network '%s'", *net); + break; + } + + if (net_is_in_network(&client->remote_ip, &net_ip, bits)) + return TRUE; + } + return FALSE; +} + void clients_destroy(void) { while (clients != NULL) {
--- a/src/lmtp/client.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lmtp/client.h Sun May 20 03:25:04 2012 +0300 @@ -62,6 +62,7 @@ struct client_state state; struct istream *dot_input; struct lmtp_proxy *proxy; + unsigned int proxy_ttl; unsigned int disconnected:1; }; @@ -82,6 +83,7 @@ void client_send_line(struct client *client, const char *fmt, ...) ATTR_FORMAT(2, 3); +bool client_is_trusted(struct client *client); void clients_destroy(void);
--- a/src/lmtp/commands.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lmtp/commands.c Sun May 20 03:25:04 2012 +0300 @@ -68,6 +68,8 @@ client_state_reset(client); client_send_line(client, "250-%s", client->my_domain); + if (client_is_trusted(client)) + client_send_line(client, "250-XCLIENT ADDR PORT TTL"); client_send_line(client, "250-8BITMIME"); client_send_line(client, "250-ENHANCEDSTATUSCODES"); client_send_line(client, "250 PIPELINING"); @@ -148,7 +150,7 @@ } static bool -client_proxy_rcpt_parse_fields(struct lmtp_proxy_settings *set, +client_proxy_rcpt_parse_fields(struct lmtp_proxy_rcpt_settings *set, const char *const *args, const char **address) { const char *p, *key, *value; @@ -201,7 +203,7 @@ static bool client_proxy_is_ourself(const struct client *client, - const struct lmtp_proxy_settings *set) + const struct lmtp_proxy_rcpt_settings *set) { struct ip_addr ip; @@ -235,7 +237,7 @@ const char *username, const char *detail) { struct auth_master_connection *auth_conn; - struct lmtp_proxy_settings set; + struct lmtp_proxy_rcpt_settings set; struct auth_user_info info; struct mail_storage_service_input input; const char *args, *const *fields, *errstr, *orig_username = username; @@ -294,6 +296,15 @@ return TRUE; } + if (client->proxy_ttl == 0) { + i_error("Proxying to <%s> appears to be looping (TTL=0)", + username); + client_send_line(client, "554 5.4.6 <%s> " + "Proxying appears to be looping (TTL=0)", + username); + pool_unref(&pool); + return TRUE; + } if (array_count(&client->state.rcpt_to) != 0) { client_send_line(client, "451 4.3.0 <%s> " "Can't handle mixed proxy/non-proxy destinations", @@ -302,9 +313,16 @@ return TRUE; } if (client->proxy == NULL) { - client->proxy = lmtp_proxy_init(client->set->hostname, - dns_client_socket_path, - client->output); + struct lmtp_proxy_settings proxy_set; + + memset(&proxy_set, 0, sizeof(proxy_set)); + proxy_set.my_hostname = client->set->hostname; + proxy_set.dns_client_socket_path = dns_client_socket_path; + proxy_set.source_ip = client->remote_ip; + proxy_set.source_port = client->remote_port; + proxy_set.proxy_ttl = client->proxy_ttl-1; + + client->proxy = lmtp_proxy_init(&proxy_set, client->output); if (client->state.mail_body_8bitmime) args = " BODY=8BITMIME"; else if (client->state.mail_body_7bit) @@ -909,3 +927,46 @@ client_input_data_handle(client); return -1; } + +int cmd_xclient(struct client *client, const char *args) +{ + const char *const *tmp; + struct ip_addr remote_ip; + unsigned int remote_port = 0, ttl = -1U; + bool args_ok = TRUE; + + if (!client_is_trusted(client)) { + client_send_line(client, "550 You are not from trusted IP"); + return 0; + } + remote_ip.family = 0; + for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) { + if (strncasecmp(*tmp, "ADDR=", 5) == 0) { + if (net_addr2ip(*tmp + 5, &remote_ip) < 0) + args_ok = FALSE; + } else if (strncasecmp(*tmp, "PORT=", 5) == 0) { + if (str_to_uint(*tmp + 5, &remote_port) < 0 || + remote_port == 0 || remote_port > 65535) + args_ok = FALSE; + } else if (strncasecmp(*tmp, "TTL=", 4) == 0) { + if (str_to_uint(*tmp + 4, &ttl) < 0) + args_ok = FALSE; + } + } + if (!args_ok) { + client_send_line(client, "501 Invalid parameters"); + return 0; + } + + /* args ok, set them and reset the state */ + client_state_reset(client); + if (remote_ip.family != 0) + client->remote_ip = remote_ip; + if (remote_port != 0) + client->remote_port = remote_port; + if (ttl != -1U) + client->proxy_ttl = ttl; + client_send_line(client, "220 %s %s", client->my_domain, + client->lmtp_set->login_greeting); + return 0; +}
--- a/src/lmtp/commands.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lmtp/commands.h Sun May 20 03:25:04 2012 +0300 @@ -11,5 +11,6 @@ int cmd_rset(struct client *client, const char *args); int cmd_noop(struct client *client, const char *args); int cmd_data(struct client *client, const char *args); +int cmd_xclient(struct client *client, const char *args); #endif
--- a/src/lmtp/lmtp-proxy.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lmtp/lmtp-proxy.c Sun May 20 03:25:04 2012 +0300 @@ -21,7 +21,7 @@ struct lmtp_proxy_connection { struct lmtp_proxy *proxy; - struct lmtp_proxy_settings set; + struct lmtp_proxy_rcpt_settings set; struct lmtp_client *client; struct istream *data_input; @@ -33,8 +33,8 @@ struct lmtp_proxy { pool_t pool; - const char *mail_from, *my_hostname; - const char *dns_client_socket_path; + const char *mail_from; + struct lmtp_proxy_settings set; ARRAY_DEFINE(connections, struct lmtp_proxy_connection *); ARRAY_DEFINE(rcpt_to, struct lmtp_proxy_recipient *); @@ -55,7 +55,7 @@ static void lmtp_conn_finish(void *context); struct lmtp_proxy * -lmtp_proxy_init(const char *my_hostname, const char *dns_client_socket_path, +lmtp_proxy_init(const struct lmtp_proxy_settings *set, struct ostream *client_output) { struct lmtp_proxy *proxy; @@ -66,9 +66,13 @@ pool = pool_alloconly_create("lmtp proxy", 1024); proxy = p_new(pool, struct lmtp_proxy, 1); proxy->pool = pool; - proxy->my_hostname = p_strdup(pool, my_hostname); proxy->client_output = client_output; - proxy->dns_client_socket_path = p_strdup(pool, dns_client_socket_path); + proxy->set.my_hostname = p_strdup(pool, set->my_hostname); + proxy->set.dns_client_socket_path = + p_strdup(pool, set->dns_client_socket_path); + proxy->set.source_ip = set->source_ip; + proxy->set.source_port = set->source_port; + proxy->set.proxy_ttl = set->proxy_ttl; i_array_init(&proxy->rcpt_to, 32); i_array_init(&proxy->connections, 32); return proxy; @@ -110,7 +114,7 @@ static struct lmtp_proxy_connection * lmtp_proxy_get_connection(struct lmtp_proxy *proxy, - const struct lmtp_proxy_settings *set) + const struct lmtp_proxy_rcpt_settings *set) { struct lmtp_proxy_connection *const *conns, *conn; struct lmtp_client_settings client_set; @@ -127,8 +131,11 @@ memset(&client_set, 0, sizeof(client_set)); client_set.mail_from = proxy->mail_from; - client_set.my_hostname = proxy->my_hostname; - client_set.dns_client_socket_path = proxy->dns_client_socket_path; + client_set.my_hostname = proxy->set.my_hostname; + client_set.dns_client_socket_path = proxy->set.dns_client_socket_path; + client_set.source_ip = proxy->set.source_ip; + client_set.source_port = proxy->set.source_port; + client_set.proxy_ttl_plus_1 = proxy->set.proxy_ttl+1; conn = p_new(proxy->pool, struct lmtp_proxy_connection, 1); conn->proxy = proxy; @@ -236,7 +243,7 @@ } int lmtp_proxy_add_rcpt(struct lmtp_proxy *proxy, const char *address, - const struct lmtp_proxy_settings *set) + const struct lmtp_proxy_rcpt_settings *set) { struct lmtp_proxy_connection *conn; struct lmtp_proxy_recipient *rcpt; @@ -294,4 +301,5 @@ lmtp_client_send(conn->client, conn->data_input); lmtp_client_send_more(conn->client); } + lmtp_proxy_try_finish(proxy); }
--- a/src/lmtp/lmtp-proxy.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lmtp/lmtp-proxy.h Sun May 20 03:25:04 2012 +0300 @@ -4,7 +4,19 @@ #include "network.h" #include "lmtp-client.h" +#define LMTP_PROXY_DEFAULT_TTL 5 + struct lmtp_proxy_settings { + const char *my_hostname; + const char *dns_client_socket_path; + + /* the original client's IP/port that connected to the proxy */ + struct ip_addr source_ip; + unsigned int source_port; + unsigned int proxy_ttl; +}; + +struct lmtp_proxy_rcpt_settings { const char *host; unsigned int port; unsigned int timeout_msecs; @@ -14,7 +26,7 @@ typedef void lmtp_proxy_finish_callback_t(void *context); struct lmtp_proxy * -lmtp_proxy_init(const char *my_hostname, const char *dns_client_socket_path, +lmtp_proxy_init(const struct lmtp_proxy_settings *set, struct ostream *client_output); void lmtp_proxy_deinit(struct lmtp_proxy **proxy); @@ -23,7 +35,7 @@ /* Add a new recipient. Returns -1 if we already know that the destination host can't be reached. */ int lmtp_proxy_add_rcpt(struct lmtp_proxy *proxy, const char *address, - const struct lmtp_proxy_settings *set); + const struct lmtp_proxy_rcpt_settings *set); /* Start proxying */ void lmtp_proxy_start(struct lmtp_proxy *proxy, struct istream *data_input, const char *header,
--- a/src/lmtp/lmtp-settings.c Sun May 20 03:08:01 2012 +0300 +++ b/src/lmtp/lmtp-settings.c Sun May 20 03:25:04 2012 +0300 @@ -60,6 +60,7 @@ DEF(SET_BOOL, lmtp_proxy), DEF(SET_BOOL, lmtp_save_to_detail_mailbox), DEF(SET_STR_VARS, login_greeting), + DEF(SET_STR, login_trusted_networks), SETTING_DEFINE_LIST_END }; @@ -67,7 +68,8 @@ static const struct lmtp_settings lmtp_default_settings = { .lmtp_proxy = FALSE, .lmtp_save_to_detail_mailbox = FALSE, - .login_greeting = PACKAGE_NAME" ready." + .login_greeting = PACKAGE_NAME" ready.", + .login_trusted_networks = "" }; static const struct setting_parser_info *lmtp_setting_dependencies[] = {
--- a/src/lmtp/lmtp-settings.h Sun May 20 03:08:01 2012 +0300 +++ b/src/lmtp/lmtp-settings.h Sun May 20 03:25:04 2012 +0300 @@ -8,6 +8,7 @@ bool lmtp_proxy; bool lmtp_save_to_detail_mailbox; const char *login_greeting; + const char *login_trusted_networks; }; extern const struct setting_parser_info lmtp_setting_parser_info;
--- a/src/login-common/Makefile.am Sun May 20 03:08:01 2012 +0300 +++ b/src/login-common/Makefile.am Sun May 20 03:25:04 2012 +0300 @@ -4,7 +4,6 @@ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ - -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-ssl-iostream \ -I$(top_srcdir)/src/lib-mail \
--- a/src/login-common/client-common-auth.c Sun May 20 03:08:01 2012 +0300 +++ b/src/login-common/client-common-auth.c Sun May 20 03:25:04 2012 +0300 @@ -1,10 +1,12 @@ /* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */ +#include "hostpid.h" #include "login-common.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "safe-memset.h" +#include "time-util.h" #include "login-proxy.h" #include "auth-client.h" #include "client-common.h" @@ -12,14 +14,13 @@ #include <stdlib.h> #define PROXY_FAILURE_MSG "Account is temporarily unavailable." -#define LOGIN_DNS_CLIENT_SOCKET_PATH "dns-client" /* If we've been waiting auth server to respond for over this many milliseconds, send a "waiting" message. */ #define AUTH_WAITING_TIMEOUT_MSECS (30*1000) -#define GREETING_WARNING_TIMEOUT_MSECS (10*1000) +#define AUTH_WAITING_WARNING_TIMEOUT_MSECS (10*1000) -void client_auth_failed(struct client *client) +static void client_auth_failed(struct client *client) { i_free_and_null(client->master_data_prefix); if (client->auth_response != NULL) @@ -37,13 +38,12 @@ static void client_auth_waiting_timeout(struct client *client) { - if (!client->greeting_sent) { + if (!client->notified_auth_ready) { client_log_warn(client, "Auth process not responding, " - "delayed sending greeting"); + "delayed sending initial response (greeting)"); } - client_send_line(client, CLIENT_CMD_REPLY_STATUS, - client->master_tag == 0 ? - AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG); + client_notify_status(client, FALSE, client->master_tag == 0 ? + AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG); timeout_remove(&client->to_auth_waiting); } @@ -51,8 +51,8 @@ { i_assert(client->to_auth_waiting == NULL); client->to_auth_waiting = - timeout_add(!client->greeting_sent ? - GREETING_WARNING_TIMEOUT_MSECS : + timeout_add(!client->notified_auth_ready ? + AUTH_WAITING_WARNING_TIMEOUT_MSECS : AUTH_WAITING_TIMEOUT_MSECS, client_auth_waiting_timeout, client); } @@ -165,6 +165,11 @@ client_destroy_success(client, str_c(str)); } +static void client_proxy_error(struct client *client, const char *text) +{ + client->v.proxy_error(client, text); +} + void client_proxy_log_failure(struct client *client, const char *line) { string_t *str = t_str_new(128); @@ -188,8 +193,7 @@ void client_proxy_failed(struct client *client, bool send_line) { if (send_line) { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - PROXY_FAILURE_MSG); + client_proxy_error(client, PROXY_FAILURE_MSG); } login_proxy_free(&client->login_proxy); @@ -272,14 +276,12 @@ if (reply->password == NULL) { client_log_err(client, "proxy: password not given"); - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - PROXY_FAILURE_MSG); + client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } if (reply->host == NULL || *reply->host == '\0') { client_log_err(client, "proxy: host not given"); - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - PROXY_FAILURE_MSG); + client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } @@ -293,8 +295,7 @@ if (login_proxy_is_ourself(client, reply->host, reply->port, reply->destuser)) { client_log_err(client, "Proxying loops to itself"); - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - PROXY_FAILURE_MSG); + client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } @@ -304,14 +305,12 @@ net_addr2ip(reply->hostip, &proxy_set.ip) < 0) proxy_set.ip.family = 0; proxy_set.port = reply->port; - proxy_set.dns_client_socket_path = LOGIN_DNS_CLIENT_SOCKET_PATH; proxy_set.connect_timeout_msecs = reply->proxy_timeout_msecs; proxy_set.notify_refresh_secs = reply->proxy_refresh_secs; proxy_set.ssl_flags = reply->ssl_flags; if (login_proxy_new(client, &proxy_set, proxy_input) < 0) { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - PROXY_FAILURE_MSG); + client_proxy_error(client, PROXY_FAILURE_MSG); return -1; } @@ -325,6 +324,13 @@ return 0; } +static void +client_auth_result(struct client *client, enum client_auth_result result, + const struct client_auth_reply *reply, const char *text) +{ + client->v.auth_result(client, result, reply, text); +} + static bool client_auth_handle_reply(struct client *client, const struct client_auth_reply *reply, bool success) @@ -341,7 +347,79 @@ client_auth_failed(client); return TRUE; } - return client->v.auth_handle_reply(client, reply); + + if (reply->host != NULL) { + const char *reason; + + if (reply->reason != NULL) + reason = reply->reason; + else if (reply->nologin) + reason = "Try this server instead."; + else + reason = "Logged in, but you should use this server instead."; + + if (reply->nologin) { + client_auth_result(client, + CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN, + reply, reason); + } else { + client_auth_result(client, + CLIENT_AUTH_RESULT_REFERRAL_SUCCESS, + reply, reason); + return TRUE; + } + } else if (reply->nologin) { + /* Authentication went ok, but for some reason user isn't + allowed to log in. Shouldn't probably happen. */ + if (reply->reason != NULL) { + client_auth_result(client, + CLIENT_AUTH_RESULT_AUTHFAILED_REASON, + reply, reply->reason); + } else if (reply->temp) { + const char *timestamp, *msg; + + timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time); + msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]", + my_hostname, timestamp); + client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL, + reply, msg); + } else if (reply->authz_failure) { + client_auth_result(client, + CLIENT_AUTH_RESULT_AUTHZFAILED, reply, + "Authorization failed"); + } else { + client_auth_result(client, + CLIENT_AUTH_RESULT_AUTHFAILED, reply, + AUTH_FAILED_MSG); + } + } else { + /* normal login/failure */ + return FALSE; + } + + i_assert(reply->nologin); + + if (!client->destroyed) + client_auth_failed(client); + return TRUE; +} + +void client_auth_respond(struct client *client, const char *response) +{ + client->auth_waiting = FALSE; + client_set_auth_waiting(client); + auth_client_request_continue(client->auth_request, response); + io_remove(&client->io); +} + +void client_auth_abort(struct client *client) +{ + sasl_server_auth_abort(client); +} + +void client_auth_fail(struct client *client, const char *text) +{ + sasl_server_auth_failed(client, text); } int client_auth_read_line(struct client *client) @@ -377,33 +455,24 @@ return i < size; } -int client_auth_parse_response(struct client *client) +void client_auth_parse_response(struct client *client) { - int ret; - - if ((ret = client_auth_read_line(client)) <= 0) - return ret; + if (client_auth_read_line(client) <= 0) + return; if (strcmp(str_c(client->auth_response), "*") == 0) { sasl_server_auth_abort(client); - return -1; + return; } - return 1; + + client_auth_respond(client, str_c(client->auth_response)); + memset(str_c_modifiable(client->auth_response), 0, + str_len(client->auth_response)); } static void client_auth_input(struct client *client) { - if (client->v.auth_parse_response(client) <= 0) - return; - - client->auth_waiting = FALSE; - client_set_auth_waiting(client); - auth_client_request_continue(client->auth_request, - str_c(client->auth_response)); - io_remove(&client->io); - - memset(str_c_modifiable(client->auth_response), 0, - str_len(client->auth_response)); + client->v.auth_parse_response(client); } void client_auth_send_challenge(struct client *client, const char *data) @@ -439,6 +508,8 @@ if (client_auth_handle_reply(client, &reply, TRUE)) break; } + client_auth_result(client, CLIENT_AUTH_RESULT_SUCCESS, + NULL, NULL); client_destroy_success(client, "Login"); break; case SASL_SERVER_REPLY_AUTH_FAILED: @@ -453,15 +524,16 @@ } if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) { - client_send_line(client, CLIENT_CMD_REPLY_BAD, - "Authentication aborted by client."); + client_auth_result(client, CLIENT_AUTH_RESULT_ABORTED, + NULL, "Authentication aborted by client."); } else if (data == NULL) { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); + client_auth_result(client, + CLIENT_AUTH_RESULT_AUTHFAILED, NULL, + AUTH_FAILED_MSG); } else { - client_send_line(client, - CLIENT_CMD_REPLY_AUTH_FAIL_REASON, - data); + client_auth_result(client, + CLIENT_AUTH_RESULT_AUTHFAILED_REASON, NULL, + AUTH_FAILED_MSG); } if (!client->destroyed) @@ -471,8 +543,8 @@ if (data != NULL) { /* authentication itself succeeded, we just hit some internal failure. */ - client_send_line(client, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data); + client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL, + NULL, data); } /* the fd may still be hanging somewhere in kernel or another @@ -514,7 +586,7 @@ "SSL required for authentication"); } client->auth_attempts++; - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, + client_auth_result(client, CLIENT_AUTH_RESULT_SSL_REQUIRED, NULL, "Authentication not allowed until SSL/TLS is enabled."); return 1; } @@ -545,13 +617,13 @@ "Plaintext authentication disabled"); } if (pass_sent) { - client_send_line(client, CLIENT_CMD_REPLY_STATUS_BAD, + client_notify_status(client, TRUE, "Plaintext authentication not allowed " "without SSL/TLS, but your client did it anyway. " "If anyone was listening, the password was exposed."); } - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, - AUTH_PLAINTEXT_DISABLED_MSG); + client_auth_result(client, CLIENT_AUTH_RESULT_SSL_REQUIRED, NULL, + AUTH_PLAINTEXT_DISABLED_MSG); client->auth_tried_disabled_plaintext = TRUE; client->auth_attempts++; return FALSE; @@ -566,8 +638,9 @@ if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); - if (!client->greeting_sent) - client->v.send_greeting(client); + + client_notify_auth_ready(client); + if (client->input_blocked) { client->input_blocked = FALSE; client_input(client);
--- a/src/login-common/client-common.c Sun May 20 03:08:01 2012 +0300 +++ b/src/login-common/client-common.c Sun May 20 03:25:04 2012 +0300 @@ -56,7 +56,7 @@ user_reason = "Disconnected for inactivity."; destroy_reason = "Disconnected: Inactivity"; } - client_send_line(client, CLIENT_CMD_REPLY_BYE, user_reason); + client_notify_disconnect(client, CLIENT_DISCONNECT_TIMEOUT, user_reason); client_destroy(client, destroy_reason); } @@ -74,6 +74,29 @@ } } +static bool client_is_trusted(struct client *client) +{ + const char *const *net; + struct ip_addr net_ip; + unsigned int bits; + + if (client->set->login_trusted_networks == NULL) + return FALSE; + + net = t_strsplit_spaces(client->set->login_trusted_networks, ", "); + for (; *net != NULL; net++) { + if (net_parse_range(*net, &net_ip, &bits) < 0) { + i_error("login_trusted_networks: " + "Invalid network '%s'", *net); + break; + } + + if (net_is_in_network(&client->ip, &net_ip, bits)) + return TRUE; + } + return FALSE; +} + struct client * client_create(int fd, bool ssl, pool_t pool, const struct login_settings *set, void **other_sets, @@ -102,6 +125,7 @@ client->trusted = client_is_trusted(client); client->secured = ssl || client->trusted || net_ip_compare(remote_ip, local_ip); + client->proxy_ttl = LOGIN_PROXY_TTL; if (last_client == NULL) last_client = client; @@ -116,7 +140,7 @@ client->v.create(client, other_sets); if (auth_client_is_connected(auth_client)) - client->v.send_greeting(client); + client_notify_auth_ready(client); else client_set_auth_waiting(client); @@ -203,9 +227,9 @@ void client_destroy_internal_failure(struct client *client) { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - "Internal login failure. " - "Refer to server log for more information."); + client_notify_disconnect(client, CLIENT_DISCONNECT_INTERNAL_ERROR, + "Internal login failure. " + "Refer to server log for more information."); client_destroy(client, t_strdup_printf( "Internal login failure (pid=%s id=%u)", my_pid, client->master_auth_id)); @@ -270,6 +294,8 @@ if (client == NULL) client = last_client; + client_notify_disconnect(client, CLIENT_DISCONNECT_RESOURCE_CONSTRAINT, + "Connection queue full"); client_destroy(client, "Disconnected: Connection queue full"); } @@ -279,6 +305,8 @@ for (client = clients; client != NULL; client = next) { next = client->next; + client_notify_disconnect(client, + CLIENT_DISCONNECT_SYSTEM_SHUTDOWN, reason); client_destroy(client, reason); } } @@ -299,10 +327,11 @@ fd_ssl = ssl_proxy_alloc(client->fd, &client->ip, client->pool, client->set, &client->ssl_proxy); if (fd_ssl == -1) { - client_send_line(client, CLIENT_CMD_REPLY_BYE, - "TLS initialization failed."); + client_notify_disconnect(client, + CLIENT_DISCONNECT_INTERNAL_ERROR, + "TLS initialization failed."); client_destroy(client, - "Disconnected: TLS initialization failed."); + "Disconnected: TLS initialization failed."); return; } ssl_proxy_set_client(client->ssl_proxy, client); @@ -341,14 +370,12 @@ void client_cmd_starttls(struct client *client) { if (client->tls) { - client_send_line(client, CLIENT_CMD_REPLY_BAD, - "TLS is already active."); + client->v.notify_starttls(client, FALSE, "TLS is already active."); return; } if (!ssl_initialized) { - client_send_line(client, CLIENT_CMD_REPLY_BAD, - "TLS support isn't enabled."); + client->v.notify_starttls(client, FALSE, "TLS support isn't enabled."); return; } @@ -357,8 +384,7 @@ if (client->io != NULL) io_remove(&client->io); - client_send_line(client, CLIENT_CMD_REPLY_OK, - "Begin TLS negotiation now."); + client->v.notify_starttls(client, TRUE, "Begin TLS negotiation now."); /* uncork the old fd */ o_stream_uncork(client->output); @@ -568,29 +594,6 @@ } T_END; } -bool client_is_trusted(struct client *client) -{ - const char *const *net; - struct ip_addr net_ip; - unsigned int bits; - - if (client->set->login_trusted_networks == NULL) - return FALSE; - - net = t_strsplit_spaces(client->set->login_trusted_networks, ", "); - for (; *net != NULL; net++) { - if (net_parse_range(*net, &net_ip, &bits) < 0) { - i_error("login_trusted_networks: " - "Invalid network '%s'", *net); - break; - } - - if (net_is_in_network(&client->ip, &net_ip, bits)) - return TRUE; - } - return FALSE; -} - const char *client_get_extra_disconnect_reason(struct client *client) { unsigned int auth_secs = client->auth_first_started == 0 ? 0 : @@ -604,9 +607,9 @@ return "(client didn't send a cert)"; } - if (!client->greeting_sent) + if (!client->notified_auth_ready) return t_strdup_printf( - "(disconnected before greeting, waited %u secs)", + "(disconnected before auth was ready, waited %u secs)", (unsigned int)(ioloop_time - client->created)); if (client->auth_attempts == 0) { @@ -653,10 +656,28 @@ client->auth_attempts, auth_secs); } -void client_send_line(struct client *client, enum client_cmd_reply reply, - const char *text) +void client_notify_disconnect(struct client *client, + enum client_disconnect_reason reason, + const char *text) { - client->v.send_line(client, reply, text); + if (!client->notified_disconnect) { + client->v.notify_disconnect(client, reason, text); + client->notified_disconnect = TRUE; + } +} + +void client_notify_auth_ready(struct client *client) +{ + if (!client->notified_auth_ready) { + client->v.notify_auth_ready(client); + client->notified_auth_ready = TRUE; + } +} + +void client_notify_status(struct client *client, bool bad, const char *text) +{ + if (client->v.notify_status != NULL) + client->v.notify_status(client, bad, text); } void client_send_raw_data(struct client *client, const void *data, size_t size) @@ -683,8 +704,9 @@ switch (i_stream_read(client->input)) { case -2: /* buffer full */ - client_send_line(client, CLIENT_CMD_REPLY_BYE, - "Input buffer full, aborting"); + client_notify_disconnect(client, + CLIENT_DISCONNECT_RESOURCE_CONSTRAINT, + "Input buffer full, aborting"); client_destroy(client, "Disconnected: Input buffer full"); return FALSE; case -1:
--- a/src/login-common/client-common.h Sun May 20 03:08:01 2012 +0300 +++ b/src/login-common/client-common.h Sun May 20 03:25:04 2012 +0300 @@ -33,17 +33,23 @@ #define AUTH_MASTER_WAITING_MSG \ "Waiting for authentication master process to respond.." -enum client_cmd_reply { - CLIENT_CMD_REPLY_OK, - CLIENT_CMD_REPLY_AUTH_FAILED, - CLIENT_CMD_REPLY_AUTHZ_FAILED, - CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, - CLIENT_CMD_REPLY_AUTH_FAIL_REASON, - CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, - CLIENT_CMD_REPLY_BAD, - CLIENT_CMD_REPLY_BYE, - CLIENT_CMD_REPLY_STATUS, - CLIENT_CMD_REPLY_STATUS_BAD +enum client_disconnect_reason { + CLIENT_DISCONNECT_TIMEOUT, + CLIENT_DISCONNECT_SYSTEM_SHUTDOWN, + CLIENT_DISCONNECT_RESOURCE_CONSTRAINT, + CLIENT_DISCONNECT_INTERNAL_ERROR +}; + +enum client_auth_result { + CLIENT_AUTH_RESULT_SUCCESS, + CLIENT_AUTH_RESULT_REFERRAL_SUCCESS, + CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN, + CLIENT_AUTH_RESULT_ABORTED, + CLIENT_AUTH_RESULT_AUTHFAILED, + CLIENT_AUTH_RESULT_AUTHFAILED_REASON, + CLIENT_AUTH_RESULT_AUTHZFAILED, + CLIENT_AUTH_RESULT_TEMPFAIL, + CLIENT_AUTH_RESULT_SSL_REQUIRED }; struct client_auth_reply { @@ -65,17 +71,25 @@ struct client *(*alloc)(pool_t pool); void (*create)(struct client *client, void **other_sets); void (*destroy)(struct client *client); - void (*send_greeting)(struct client *client); + void (*notify_auth_ready)(struct client *client); + void (*notify_disconnect)(struct client *client, + enum client_disconnect_reason reason, + const char *text); + void (*notify_status)(struct client *client, + bool bad, const char *text); + void (*notify_starttls)(struct client *client, + bool success, const char *text); void (*starttls)(struct client *client); void (*input)(struct client *client); - void (*send_line)(struct client *client, enum client_cmd_reply reply, - const char *text); - bool (*auth_handle_reply)(struct client *client, - const struct client_auth_reply *reply); void (*auth_send_challenge)(struct client *client, const char *data); - int (*auth_parse_response)(struct client *client); + void (*auth_parse_response)(struct client *client); + void (*auth_result)(struct client *client, + enum client_auth_result result, + const struct client_auth_reply *reply, + const char *text); void (*proxy_reset)(struct client *client); int (*proxy_parse_line)(struct client *client, const char *line); + void (*proxy_error)(struct client *client, const char *text); }; struct client { @@ -106,6 +120,7 @@ struct login_proxy *login_proxy; char *proxy_user, *proxy_master_user, *proxy_password; unsigned int proxy_state; + unsigned int proxy_ttl; char *auth_mech_name; struct auth_client_request *auth_request; @@ -125,7 +140,6 @@ unsigned int destroyed:1; unsigned int input_blocked:1; unsigned int login_success:1; - unsigned int greeting_sent:1; unsigned int starttls:1; unsigned int tls:1; unsigned int secured:1; @@ -141,6 +155,8 @@ unsigned int auth_waiting:1; unsigned int auth_user_disabled:1; unsigned int auth_pass_expired:1; + unsigned int notified_auth_ready:1; + unsigned int notified_disconnect:1; /* ... */ }; @@ -166,21 +182,27 @@ void client_log_err(struct client *client, const char *msg); void client_log_warn(struct client *client, const char *msg); const char *client_get_extra_disconnect_reason(struct client *client); -bool client_is_trusted(struct client *client); -void client_auth_failed(struct client *client); + +void client_auth_respond(struct client *client, const char *response); +void client_auth_abort(struct client *client); +void client_auth_fail(struct client *client, const char *text); const char *client_get_session_id(struct client *client); bool client_read(struct client *client); void client_input(struct client *client); -void client_send_line(struct client *client, enum client_cmd_reply reply, - const char *text); +void client_notify_auth_ready(struct client *client); +void client_notify_status(struct client *client, bool bad, const char *text); +void client_notify_disconnect(struct client *client, + enum client_disconnect_reason reason, + const char *text); + void client_send_raw_data(struct client *client, const void *data, size_t size); void client_send_raw(struct client *client, const char *data); void client_set_auth_waiting(struct client *client); void client_auth_send_challenge(struct client *client, const char *data); -int client_auth_parse_response(struct client *client); +void client_auth_parse_response(struct client *client); int client_auth_begin(struct client *client, const char *mech_name, const char *init_resp); bool client_check_plaintext_auth(struct client *client, bool pass_sent);
--- a/src/login-common/login-proxy.c Sun May 20 03:08:01 2012 +0300 +++ b/src/login-common/login-proxy.c Sun May 20 03:25:04 2012 +0300 @@ -9,7 +9,6 @@ #include "time-util.h" #include "master-service.h" #include "ipc-server.h" -#include "dns-lookup.h" #include "mail-user-hash.h" #include "client-common.h" #include "ssl-proxy.h" @@ -19,7 +18,6 @@ #define MAX_PROXY_INPUT_SIZE 4096 #define OUTBUF_THRESHOLD 1024 #define LOGIN_PROXY_DIE_IDLE_SECS 2 -#define LOGIN_PROXY_DNS_WARN_MSECS 500 #define LOGIN_PROXY_IPC_PATH "ipc-proxy" #define LOGIN_PROXY_IPC_NAME "proxy" #define KILLED_BY_ADMIN_REASON "Killed by admin" @@ -269,32 +267,11 @@ return 0; } -static void login_proxy_dns_done(const struct dns_lookup_result *result, - struct login_proxy *proxy) -{ - if (result->ret != 0) { - i_error("proxy(%s): DNS lookup of %s failed: %s", - proxy->client->virtual_user, proxy->host, - result->error); - login_proxy_free(&proxy); - } else { - if (result->msecs > LOGIN_PROXY_DNS_WARN_MSECS) { - i_warning("proxy(%s): DNS lookup for %s took %u.%03u s", - proxy->client->virtual_user, proxy->host, - result->msecs/1000, result->msecs % 1000); - } - - proxy->ip = result->ips[0]; - (void)login_proxy_connect(proxy); - } -} - int login_proxy_new(struct client *client, const struct login_proxy_settings *set, proxy_callback_t *callback) { struct login_proxy *proxy; - struct dns_lookup_settings dns_lookup_set; i_assert(client->login_proxy == NULL); @@ -303,6 +280,12 @@ return -1; } + if (client->proxy_ttl == 0) { + i_error("proxy(%s): TTL reached zero - " + "proxies appear to be looping?", client->virtual_user); + return -1; + } + proxy = i_new(struct login_proxy, 1); proxy->client = client; proxy->client_fd = -1; @@ -316,15 +299,11 @@ proxy->ssl_flags = set->ssl_flags; client_ref(client); - memset(&dns_lookup_set, 0, sizeof(dns_lookup_set)); - dns_lookup_set.dns_client_socket_path = set->dns_client_socket_path; - dns_lookup_set.timeout_msecs = set->connect_timeout_msecs; - if (set->ip.family == 0 && net_addr2ip(set->host, &proxy->ip) < 0) { - if (dns_lookup(set->host, &dns_lookup_set, - login_proxy_dns_done, proxy) < 0) - return -1; + i_error("proxy(%s): BUG: host %s is not an IP " + "(auth should have changed it)", + client->virtual_user, set->host); } else { if (login_proxy_connect(proxy) < 0) return -1;
--- a/src/login-common/login-proxy.h Sun May 20 03:08:01 2012 +0300 +++ b/src/login-common/login-proxy.h Sun May 20 03:25:04 2012 +0300 @@ -3,6 +3,13 @@ #include "network.h" +/* Max. number of embedded proxying connections until proxying fails. + This is intended to avoid an accidental configuration where two proxies + keep connecting to each others, both thinking the other one is supposed to + handle the user. This only works if both proxies support the Dovecot + TTL extension feature. */ +#define LOGIN_PROXY_TTL 5 + struct client; struct login_proxy; @@ -18,7 +25,6 @@ struct login_proxy_settings { const char *host; struct ip_addr ip; - const char *dns_client_socket_path; unsigned int port; unsigned int connect_timeout_msecs; /* send a notification about proxy connection to proxy-notify pipe
--- a/src/plugins/imap-acl/imap-acl-plugin.c Sun May 20 03:08:01 2012 +0300 +++ b/src/plugins/imap-acl/imap-acl-plugin.c Sun May 20 03:25:04 2012 +0300 @@ -47,7 +47,7 @@ const char *imap_acl_plugin_version = DOVECOT_VERSION; static struct module *imap_acl_module; -static void (*next_hook_client_created)(struct client **client); +static imap_client_created_func_t *next_hook_client_created; static struct mailbox * acl_mailbox_open_as_admin(struct client_command_context *cmd, const char *name)
--- a/src/plugins/imap-quota/imap-quota-plugin.c Sun May 20 03:08:01 2012 +0300 +++ b/src/plugins/imap-quota/imap-quota-plugin.c Sun May 20 03:25:04 2012 +0300 @@ -17,7 +17,7 @@ const char *imap_quota_plugin_version = DOVECOT_VERSION; static struct module *imap_quota_module; -static void (*next_hook_client_created)(struct client **client); +static imap_client_created_func_t *next_hook_client_created; static const char * imap_quota_root_get_name(struct mail_user *user, struct mail_user *owner,
--- a/src/plugins/imap-zlib/imap-zlib-plugin.c Sun May 20 03:08:01 2012 +0300 +++ b/src/plugins/imap-zlib/imap-zlib-plugin.c Sun May 20 03:25:04 2012 +0300 @@ -25,7 +25,7 @@ const char *imap_zlib_plugin_version = DOVECOT_VERSION; static struct module *imap_zlib_module; -static void (*next_hook_client_created)(struct client **client); +static imap_client_created_func_t *next_hook_client_created; static MODULE_CONTEXT_DEFINE_INIT(imap_zlib_imap_module, &imap_module_register);
--- a/src/plugins/virtual/virtual-storage.c Sun May 20 03:08:01 2012 +0300 +++ b/src/plugins/virtual/virtual-storage.c Sun May 20 03:25:04 2012 +0300 @@ -391,10 +391,8 @@ if (box->notify_callback == NULL) mailbox_notify_changes_stop(bbox); - else { - mailbox_notify_changes(bbox, box->notify_min_interval, - virtual_notify_callback, box); - } + else + mailbox_notify_changes(bbox, virtual_notify_callback, box); } }
--- a/src/pop3-login/client-authenticate.c Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3-login/client-authenticate.c Sun May 20 03:25:04 2012 +0300 @@ -4,14 +4,12 @@ #include "base64.h" #include "buffer.h" #include "hex-binary.h" -#include "hostpid.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "safe-memset.h" #include "str.h" #include "str-sanitize.h" -#include "time-util.h" #include "auth-client.h" #include "../pop3/pop3-capability.h" #include "ssl-proxy.h" @@ -51,30 +49,22 @@ return TRUE; } -bool pop3_client_auth_handle_reply(struct client *client, - const struct client_auth_reply *reply) +void pop3_client_auth_result(struct client *client, + enum client_auth_result result, + const struct client_auth_reply *reply ATTR_UNUSED, + const char *text) { - const char *timestamp, *msg; - - if (!reply->nologin) - return FALSE; - - if (reply->reason != NULL) { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, - reply->reason); - } else if (reply->temp) { - timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time); - msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]", - my_hostname, timestamp); - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, msg); - } else { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); + switch (result) { + case CLIENT_AUTH_RESULT_SUCCESS: + /* nothing to be done for POP3 */ + break; + case CLIENT_AUTH_RESULT_TEMPFAIL: + client_send_reply(client, POP3_CMD_REPLY_TEMPFAIL, text); + break; + default: + client_send_reply(client, POP3_CMD_REPLY_ERROR, text); + break; } - - if (!client->destroyed) - client_auth_failed(client); - return TRUE; } bool cmd_auth(struct pop3_client *pop3_client, const char *args) @@ -134,8 +124,8 @@ if (!client_check_plaintext_auth(client, TRUE)) return TRUE; - client_send_line(client, CLIENT_CMD_REPLY_BAD, - "No username given."); + client_send_reply(client, POP3_CMD_REPLY_ERROR, + "No username given."); return TRUE; } @@ -166,8 +156,8 @@ if (pop3_client->apop_challenge == NULL) { if (client->set->auth_verbose) client_log(client, "APOP failed: APOP not enabled"); - client_send_line(client, CLIENT_CMD_REPLY_BAD, - "APOP not enabled."); + client_send_reply(client, POP3_CMD_REPLY_ERROR, + "APOP not enabled."); return TRUE; } @@ -176,8 +166,8 @@ if (p == NULL || strlen(p+1) != 32) { if (client->set->auth_verbose) client_log(client, "APOP failed: Invalid parameters"); - client_send_line(client, CLIENT_CMD_REPLY_BAD, - "Invalid parameters."); + client_send_reply(client, POP3_CMD_REPLY_ERROR, + "Invalid parameters."); return TRUE; } @@ -193,8 +183,8 @@ client_log(client, "APOP failed: " "Invalid characters in MD5 response"); } - client_send_line(client, CLIENT_CMD_REPLY_BAD, - "Invalid characters in MD5 response."); + client_send_reply(client, POP3_CMD_REPLY_ERROR, + "Invalid characters in MD5 response."); return TRUE; }
--- a/src/pop3-login/client-authenticate.h Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3-login/client-authenticate.h Sun May 20 03:25:04 2012 +0300 @@ -1,8 +1,10 @@ #ifndef CLIENT_AUTHENTICATE_H #define CLIENT_AUTHENTICATE_H -bool pop3_client_auth_handle_reply(struct client *client, - const struct client_auth_reply *reply); +void pop3_client_auth_result(struct client *client, + enum client_auth_result result, + const struct client_auth_reply *reply, + const char *text); bool cmd_capa(struct pop3_client *client, const char *args); bool cmd_user(struct pop3_client *client, const char *args);
--- a/src/pop3-login/client.c Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3-login/client.c Sun May 20 03:25:04 2012 +0300 @@ -24,13 +24,13 @@ static bool cmd_stls(struct pop3_client *client) { - client_cmd_starttls(&client->common); + client_cmd_starttls(&client->common); return TRUE; } static bool cmd_quit(struct pop3_client *client) { - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Logging out"); + client_send_reply(&client->common, POP3_CMD_REPLY_OK, "Logging out"); client_destroy(&client->common, "Aborted login"); return TRUE; } @@ -42,8 +42,8 @@ bool args_ok = TRUE; if (!client->common.trusted) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "You are not from trusted IP"); + client_send_reply(&client->common, POP3_CMD_REPLY_ERROR, + "You are not from trusted IP"); return TRUE; } for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) { @@ -59,16 +59,19 @@ } else if (strncasecmp(*tmp, "SESSION=", 8) == 0) { client->common.session_id = p_strdup(client->common.pool, *tmp + 8); + } else if (strncasecmp(*tmp, "TTL=", 4) == 0) { + if (str_to_uint(*tmp + 4, &client->common.proxy_ttl) < 0) + args_ok = FALSE; } } if (!args_ok) { - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "Invalid parameters"); + client_send_reply(&client->common, POP3_CMD_REPLY_ERROR, + "Invalid parameters"); return TRUE; } /* args ok, set them and reset the state */ - client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Updated"); + client_send_reply(&client->common, POP3_CMD_REPLY_OK, "Updated"); return TRUE; } @@ -93,8 +96,8 @@ if (strcmp(cmd, "XCLIENT") == 0) return cmd_xclient(client, args); - client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, - "Unknown command."); + client_send_reply(&client->common, POP3_CMD_REPLY_ERROR, + "Unknown command."); return FALSE; } @@ -124,7 +127,7 @@ args != NULL ? args : "")) client->bad_counter = 0; else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) { - client_send_line(client, CLIENT_CMD_REPLY_BYE, + client_send_reply(client, POP3_CMD_REPLY_ERROR, "Too many invalid bad commands."); client_destroy(client, "Disconnected: Too many bad commands"); @@ -185,7 +188,7 @@ (const char *)buf.data, my_hostname); } -static void pop3_client_send_greeting(struct client *client) +static void pop3_client_notify_auth_ready(struct client *client) { struct pop3_client *pop3_client = (struct pop3_client *)client; string_t *str; @@ -198,41 +201,41 @@ str_append(str, "[XCLIENT] "); } str_append(str, client->set->login_greeting); + pop3_client->apop_challenge = get_apop_challenge(pop3_client); if (pop3_client->apop_challenge != NULL) str_printfa(str, " %s", pop3_client->apop_challenge); - client_send_line(client, CLIENT_CMD_REPLY_OK, str_c(str)); - client->greeting_sent = TRUE; + client_send_reply(client, POP3_CMD_REPLY_OK, str_c(str)); +} + +static void +pop3_client_notify_starttls(struct client *client, + bool success, const char *text) +{ + if (success) + client_send_reply(client, POP3_CMD_REPLY_OK, text); + else + client_send_reply(client, POP3_CMD_REPLY_ERROR, text); } static void pop3_client_starttls(struct client *client ATTR_UNUSED) { } -static void -pop3_client_send_line(struct client *client, enum client_cmd_reply reply, - const char *text) +void client_send_reply(struct client *client, enum pop3_cmd_reply reply, + const char *text) { const char *prefix = "-ERR"; switch (reply) { - case CLIENT_CMD_REPLY_OK: + case POP3_CMD_REPLY_OK: prefix = "+OK"; break; - case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP: + case POP3_CMD_REPLY_TEMPFAIL: prefix = "-ERR [IN-USE]"; break; - case CLIENT_CMD_REPLY_AUTH_FAILED: - case CLIENT_CMD_REPLY_AUTHZ_FAILED: - case CLIENT_CMD_REPLY_AUTH_FAIL_REASON: - case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL: - case CLIENT_CMD_REPLY_BAD: - case CLIENT_CMD_REPLY_BYE: + case POP3_CMD_REPLY_ERROR: break; - case CLIENT_CMD_REPLY_STATUS: - case CLIENT_CMD_REPLY_STATUS_BAD: - /* can't send status notifications */ - return; } T_BEGIN { @@ -243,11 +246,21 @@ str_append(line, text); str_append(line, "\r\n"); - client_send_raw_data(client, str_data(line), - str_len(line)); + client_send_raw_data(client, str_data(line), str_len(line)); } T_END; } +static void +pop3_client_notify_disconnect(struct client *client, + enum client_disconnect_reason reason, + const char *text) +{ + if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) + client_send_reply(client, POP3_CMD_REPLY_TEMPFAIL, text); + else + client_send_reply(client, POP3_CMD_REPLY_ERROR, text); +} + static void pop3_login_die(void) { /* do nothing. pop3 connections typically die pretty quick anyway. */ @@ -273,15 +286,18 @@ pop3_client_alloc, pop3_client_create, pop3_client_destroy, - pop3_client_send_greeting, + pop3_client_notify_auth_ready, + pop3_client_notify_disconnect, + NULL, + pop3_client_notify_starttls, pop3_client_starttls, pop3_client_input, - pop3_client_send_line, - pop3_client_auth_handle_reply, NULL, NULL, + pop3_client_auth_result, pop3_proxy_reset, - pop3_proxy_parse_line + pop3_proxy_parse_line, + pop3_proxy_error }; static const struct login_binary pop3_login_binary = {
--- a/src/pop3-login/client.h Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3-login/client.h Sun May 20 03:25:04 2012 +0300 @@ -22,4 +22,13 @@ bool proxy_xclient; }; +enum pop3_cmd_reply { + POP3_CMD_REPLY_OK, + POP3_CMD_REPLY_ERROR, + POP3_CMD_REPLY_TEMPFAIL +}; + +void client_send_reply(struct client *client, enum pop3_cmd_reply reply, + const char *text); + #endif
--- a/src/pop3-login/pop3-proxy.c Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3-login/pop3-proxy.c Sun May 20 03:25:04 2012 +0300 @@ -37,13 +37,15 @@ { string_t *str; + i_assert(client->common.proxy_ttl > 0); if (client->proxy_xclient) { /* remote supports XCLIENT, send it */ (void)o_stream_send_str(output, t_strdup_printf( - "XCLIENT ADDR=%s PORT=%u SESSION=%s\r\n", + "XCLIENT ADDR=%s PORT=%u SESSION=%s TTL=%u\r\n", net_ip2addr(&client->common.ip), client->common.remote_port, - client_get_session_id(&client->common))); + client_get_session_id(&client->common), + client->common.proxy_ttl - 1)); client->common.proxy_state = POP3_PROXY_XCLIENT; } else { client->common.proxy_state = POP3_PROXY_LOGIN1; @@ -168,8 +170,8 @@ shouldn't be a real problem since of course everyone will be using only Dovecot as their backend :) */ if (strncmp(line, "-ERR ", 5) != 0) { - client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED, - AUTH_FAILED_MSG); + client_send_reply(client, POP3_CMD_REPLY_ERROR, + AUTH_FAILED_MSG); } else { client_send_raw(client, t_strconcat(line, "\r\n", NULL)); } @@ -188,3 +190,8 @@ { client->proxy_state = POP3_PROXY_BANNER; } + +void pop3_proxy_error(struct client *client, const char *text) +{ + client_send_reply(client, POP3_CMD_REPLY_ERROR, text); +}
--- a/src/pop3-login/pop3-proxy.h Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3-login/pop3-proxy.h Sun May 20 03:25:04 2012 +0300 @@ -4,4 +4,6 @@ void pop3_proxy_reset(struct client *client); int pop3_proxy_parse_line(struct client *client, const char *line); +void pop3_proxy_error(struct client *client, const char *text); + #endif
--- a/src/pop3/main.c Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3/main.c Sun May 20 03:25:04 2012 +0300 @@ -29,7 +29,16 @@ static struct mail_storage_service_ctx *storage_service; static struct master_login *master_login = NULL; -void (*hook_client_created)(struct client **client) = NULL; +pop3_client_created_func_t *hook_client_created = NULL; + +pop3_client_created_func_t * +pop3_client_created_hook_set(pop3_client_created_func_t *new_hook) +{ + pop3_client_created_func_t *old_hook = hook_client_created; + + hook_client_created = new_hook; + return old_hook; +} void pop3_refresh_proctitle(void) {
--- a/src/pop3/pop3-client.c Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3/pop3-client.c Sun May 20 03:25:04 2012 +0300 @@ -37,6 +37,10 @@ transaction. This allows the mailbox to become unlocked. */ #define CLIENT_COMMIT_TIMEOUT_MSECS (10*1000) +extern struct pop3_client_vfuncs pop3_client_vfuncs; + +struct pop3_module_register pop3_module_register = { 0 }; + struct client *pop3_clients; unsigned int pop3_client_count; @@ -284,21 +288,26 @@ enum mailbox_flags flags; const char *errmsg; enum mail_error error; + pool_t pool; /* always use nonblocking I/O */ net_set_nonblock(fd_in, TRUE); net_set_nonblock(fd_out, TRUE); - client = i_new(struct client, 1); + pool = pool_alloconly_create("pop3 client", 256); + client = p_new(pool, struct client, 1); + client->pool = pool; client->service_user = service_user; + client->v = pop3_client_vfuncs; client->set = set; - client->session_id = i_strdup(session_id); + client->session_id = p_strdup(pool, session_id); client->fd_in = fd_in; client->fd_out = fd_out; client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE); o_stream_set_flush_callback(client->output, client_output, client); + p_array_init(&client->module_contexts, client->pool, 5); client->io = io_add(fd_in, IO_READ, client_input, client); client->last_input = ioloop_time; client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS, @@ -469,6 +478,11 @@ void client_destroy(struct client *client, const char *reason) { + client->v.destroy(client, reason); +} + +static void client_default_destroy(struct client *client, const char *reason) +{ if (client->seen_change_count > 0) client_update_mails(client); @@ -524,8 +538,7 @@ if (client->fd_in != client->fd_out) net_disconnect(client->fd_out); mail_storage_service_user_free(&client->service_user); - i_free(client->session_id); - i_free(client); + pool_unref(&client->pool); master_service_client_connection_destroyed(master_service); pop3_refresh_proctitle(); @@ -735,3 +748,7 @@ client_destroy(pop3_clients, "Server shutting down."); } } + +struct pop3_client_vfuncs pop3_client_vfuncs = { + client_default_destroy +};
--- a/src/pop3/pop3-client.h Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3/pop3-client.h Sun May 20 03:25:04 2012 +0300 @@ -9,6 +9,11 @@ #define MSGS_BITMASK_SIZE(client) \ (((client)->messages_count + (CHAR_BIT-1)) / CHAR_BIT) +struct pop3_client_vfuncs { + void (*destroy)(struct client *client, const char *reason); + +}; + /* pop3_msn = 1..n in the POP3 protocol msgnum = 0..n-1 = pop3_msn-1 @@ -18,7 +23,9 @@ struct client { struct client *prev, *next; - char *session_id; + struct pop3_client_vfuncs v; + const char *session_id; + int fd_in, fd_out; struct io *io; struct istream *input; @@ -28,6 +35,7 @@ command_func_t *cmd; void *cmd_context; + pool_t pool; struct mail_storage_service_user *service_user; struct mail_user *user; struct mail_namespace *inbox_ns; @@ -67,6 +75,9 @@ pool_t uidl_pool; enum uidl_keys uidl_keymask; + /* Module-specific contexts. */ + ARRAY_DEFINE(module_contexts, union pop3_module_context *); + unsigned int disconnected:1; unsigned int deleted:1; unsigned int waiting_input:1; @@ -74,6 +85,16 @@ unsigned int message_uidls_save:1; }; +struct pop3_module_register { + unsigned int id; +}; + +union pop3_module_context { + struct pop3_client_vfuncs super; + struct pop3_module_register *reg; +}; +extern struct pop3_module_register pop3_module_register; + extern struct client *pop3_clients; extern unsigned int pop3_client_count;
--- a/src/pop3/pop3-common.h Sun May 20 03:08:01 2012 +0300 +++ b/src/pop3/pop3-common.h Sun May 20 03:25:04 2012 +0300 @@ -13,7 +13,14 @@ #include "pop3-client.h" #include "pop3-settings.h" -extern void (*hook_client_created)(struct client **client); +typedef void pop3_client_created_func_t(struct client **client); + +extern pop3_client_created_func_t *hook_client_created; + +/* Sets the hook_client_created and returns the previous hook, + which the new_hook should call if it's non-NULL. */ +pop3_client_created_func_t * +pop3_client_created_hook_set(pop3_client_created_func_t *new_hook); void pop3_refresh_proctitle(void);