Mercurial > dovecot > core-2.2
changeset 12673:2b8d7b6bcfc7
lmtp: If delivering duplicate messages to same user's INBOX, use different GUIDs.
This is to avoid POP3 clients getting confused with duplicate UIDLs, when
using GUIDs as UIDLs.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 03 Jan 2011 18:59:07 +0200 |
parents | 384d8615039f |
children | 048d7025c89f |
files | src/lda/main.c src/lib-lda/mail-deliver.c src/lib-lda/mail-deliver.h src/lmtp/commands.c |
diffstat | 4 files changed, 85 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lda/main.c Mon Jan 03 18:57:05 2011 +0200 +++ b/src/lda/main.c Mon Jan 03 18:59:07 2011 +0200 @@ -255,7 +255,8 @@ &argc, &argv, "a:d:ef:km:p:r:"); memset(&ctx, 0, sizeof(ctx)); - ctx.pool = pool_alloconly_create("mail deliver context", 256); + ctx.session = mail_deliver_session_init(); + ctx.pool = ctx.session->pool; ctx.dest_mailbox_name = "INBOX"; path = NULL; @@ -467,7 +468,7 @@ mail_user_unref(&ctx.dest_user); mail_user_unref(&raw_mail_user); - pool_unref(&ctx.pool); + mail_deliver_session_deinit(&ctx.session); mail_storage_service_user_free(&service_user); mail_storage_service_deinit(&storage_service);
--- a/src/lib-lda/mail-deliver.c Mon Jan 03 18:57:05 2011 +0200 +++ b/src/lib-lda/mail-deliver.c Mon Jan 03 18:59:07 2011 +0200 @@ -116,6 +116,25 @@ va_end(args); } +struct mail_deliver_session *mail_deliver_session_init(void) +{ + struct mail_deliver_session *session; + pool_t pool; + + pool = pool_alloconly_create("mail deliver session", 1024); + session = p_new(pool, struct mail_deliver_session, 1); + session->pool = pool; + return session; +} + +void mail_deliver_session_deinit(struct mail_deliver_session **_session) +{ + struct mail_deliver_session *session = *_session; + + *_session = NULL; + pool_unref(&session->pool); +} + static const char *mailbox_name_to_mutf7(const char *mailbox_utf8) { string_t *str = t_str_new(128); @@ -191,6 +210,43 @@ return 0; } +static bool mail_deliver_check_duplicate(struct mail_deliver_session *session, + struct mail_user *user) +{ + const char *const *usernamep, *username; + + /* there shouldn't be all that many recipients, + so just do a linear search */ + if (!array_is_created(&session->inbox_users)) + p_array_init(&session->inbox_users, session->pool, 8); + array_foreach(&session->inbox_users, usernamep) { + if (strcmp(*usernamep, user->username) == 0) + return TRUE; + } + username = p_strdup(session->pool, user->username); + array_append(&session->inbox_users, &username, 1); + return FALSE; +} + +void mail_deliver_deduplicate_guid_if_needed(struct mail_deliver_context *ctx, + struct mail_save_context *save_ctx, + const char *mailbox) +{ + uint8_t guid[MAIL_GUID_128_SIZE]; + + if (strcasecmp(mailbox, "INBOX") != 0) + return; + + /* avoid storing duplicate GUIDs to delivered mails to INBOX. this + happens if mail is delivered to same user multiple times within a + session. the problem with this is that if GUIDs are used as POP3 + UIDLs, some clients can't handle the duplicates well. */ + if (mail_deliver_check_duplicate(ctx->session, ctx->dest_user)) { + mail_generate_guid_128(guid); + mailbox_save_set_guid(save_ctx, mail_guid_128_to_string(guid)); + } +} + int mail_deliver_save(struct mail_deliver_context *ctx, const char *mailbox, enum mail_flags flags, const char *const *keywords, struct mail_storage **storage_r) @@ -247,6 +303,7 @@ ctx->dest_mail = mail_alloc(t, lda_log_wanted_fetch_fields, NULL); mailbox_header_lookup_unref(&headers_ctx); mailbox_save_set_dest_mail(save_ctx, ctx->dest_mail); + mail_deliver_deduplicate_guid_if_needed(ctx, save_ctx, mailbox); if (mailbox_copy(&save_ctx, ctx->src_mail) < 0) ret = -1;
--- a/src/lib-lda/mail-deliver.h Mon Jan 03 18:57:05 2011 +0200 +++ b/src/lib-lda/mail-deliver.h Mon Jan 03 18:59:07 2011 +0200 @@ -4,11 +4,20 @@ enum mail_flags; enum mail_error; struct mail_storage; +struct mail_save_context; struct mailbox; +struct mail_deliver_session { + pool_t pool; + + /* List of users who have already saved this mail to their INBOX */ + ARRAY_TYPE(const_string) inbox_users; +}; + struct mail_deliver_context { pool_t pool; const struct lda_settings *set; + struct mail_deliver_session *session; struct duplicate_context *dup_ctx; @@ -62,6 +71,9 @@ const char *mail_deliver_get_return_address(struct mail_deliver_context *ctx); const char *mail_deliver_get_new_message_id(struct mail_deliver_context *ctx); +struct mail_deliver_session *mail_deliver_session_init(void); +void mail_deliver_session_deinit(struct mail_deliver_session **session); + /* Try to open mailbox for saving. Returns 0 if ok, -1 if error. The box may be returned even with -1, and the caller must free it then. */ int mail_deliver_save_open(struct mail_deliver_save_open_context *ctx, @@ -70,6 +82,9 @@ int mail_deliver_save(struct mail_deliver_context *ctx, const char *mailbox, enum mail_flags flags, const char *const *keywords, struct mail_storage **storage_r); +void mail_deliver_deduplicate_guid_if_needed(struct mail_deliver_context *ctx, + struct mail_save_context *save_ctx, + const char *mailbox); int mail_deliver(struct mail_deliver_context *ctx, struct mail_storage **storage_r);
--- a/src/lmtp/commands.c Mon Jan 03 18:57:05 2011 +0200 +++ b/src/lmtp/commands.c Mon Jan 03 18:59:07 2011 +0200 @@ -446,7 +446,7 @@ static int client_deliver(struct client *client, const struct mail_recipient *rcpt, - struct mail *src_mail) + struct mail *src_mail, struct mail_deliver_session *session) { struct mail_deliver_context dctx; struct mail_storage *storage; @@ -470,7 +470,8 @@ sets = mail_storage_service_user_get_set(rcpt->service_user); memset(&dctx, 0, sizeof(dctx)); - dctx.pool = pool_alloconly_create("mail delivery", 1024); + dctx.session = session; + dctx.pool = session->pool; dctx.set = sets[1]; dctx.session_id = client->state.session_id; dctx.src_mail = src_mail; @@ -516,11 +517,11 @@ } ret = -1; } - pool_unref(&dctx.pool); return ret; } -static bool client_deliver_next(struct client *client, struct mail *src_mail) +static bool client_deliver_next(struct client *client, struct mail *src_mail, + struct mail_deliver_session *session) { const struct mail_recipient *rcpts; unsigned int count; @@ -529,7 +530,7 @@ rcpts = array_get(&client->state.rcpt_to, &count); while (client->state.rcpt_idx < count) { ret = client_deliver(client, &rcpts[client->state.rcpt_idx], - src_mail); + src_mail, session); i_set_failure_prefix(t_strdup_printf("lmtp(%s): ", my_pid)); client->state.rcpt_idx++; @@ -618,15 +619,17 @@ static void client_input_data_write_local(struct client *client, struct istream *input) { + struct mail_deliver_session *session; struct mail *src_mail; uid_t old_uid, first_uid = (uid_t)-1; if (client_open_raw_mail(client, input) < 0) return; + session = mail_deliver_session_init(); old_uid = geteuid(); src_mail = client->state.raw_mail; - while (client_deliver_next(client, src_mail)) { + while (client_deliver_next(client, src_mail, session)) { if (client->state.first_saved_mail == NULL || client->state.first_saved_mail == src_mail) mail_user_unref(&client->state.dest_user); @@ -639,6 +642,7 @@ i_assert(first_uid != 0); } } + mail_deliver_session_deinit(&session); if (client->state.first_saved_mail != NULL) { struct mail *mail = client->state.first_saved_mail;