dovecot-2.0: lmtp: If delivering duplicate messages to same user...

dovecot at dovecot.org dovecot at dovecot.org
Mon Jan 3 18:59:18 EET 2011


details:   http://hg.dovecot.org/dovecot-2.0/rev/2b8d7b6bcfc7
changeset: 12538:2b8d7b6bcfc7
user:      Timo Sirainen <tss at iki.fi>
date:      Mon Jan 03 18:59:07 2011 +0200
description:
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.

diffstat:

 src/lda/main.c             |   5 +-
 src/lib-lda/mail-deliver.c |  57 ++++++++++++++++++++++++++++
 src/lib-lda/mail-deliver.h |  15 +++++++
 src/lmtp/commands.c        |  16 +++++---
 4 files changed, 85 insertions(+), 8 deletions(-)

diffs (219 lines):

diff -r 384d8615039f -r 2b8d7b6bcfc7 src/lda/main.c
--- 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);
diff -r 384d8615039f -r 2b8d7b6bcfc7 src/lib-lda/mail-deliver.c
--- 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;
diff -r 384d8615039f -r 2b8d7b6bcfc7 src/lib-lda/mail-deliver.h
--- 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);
diff -r 384d8615039f -r 2b8d7b6bcfc7 src/lmtp/commands.c
--- 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;


More information about the dovecot-cvs mailing list