From: Timo Sirainen Date: Wed, 14 Apr 2010 15:29:15 +0000 (+0300) Subject: pop3: Added %u=old/new UIDL hash to pop3_logout_format. X-Git-Tag: 2.0.beta5~122 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=698545b00566380790ee7a4ea63e32954767a79d;p=thirdparty%2Fdovecot%2Fcore.git pop3: Added %u=old/new UIDL hash to pop3_logout_format. It expands to "/ -> /" or if they're the same, simply "/". The idea is that if previous session's doesn't match next one's and prev.new_msg_count = next.old_msg_count, it could indicate that the UIDLs changed for some reason. But if they do match and client still redownloaded messages, it's most likely a client side problem. --HG-- branch : HEAD --- diff --git a/doc/example-config/conf.d/20-pop3.conf b/doc/example-config/conf.d/20-pop3.conf index 0bc44af212..1b32fda753 100644 --- a/doc/example-config/conf.d/20-pop3.conf +++ b/doc/example-config/conf.d/20-pop3.conf @@ -57,6 +57,7 @@ protocol pop3 { # %d - number of deleted messages # %m - number of messages (before deletion) # %s - mailbox size in bytes (before deletion) + # %u - old/new UIDL hash. may help finding out if UIDLs changed unexpectedly #pop3_logout_format = top=%t/%p, retr=%r/%b, del=%d/%m, size=%s # Maximum number of POP3 connections allowed for a user from each IP address. diff --git a/src/pop3/pop3-client.c b/src/pop3/pop3-client.c index ad9b7065bd..e0bbabae22 100644 --- a/src/pop3/pop3-client.c +++ b/src/pop3/pop3-client.c @@ -258,6 +258,10 @@ struct client *client_create(int fd_in, int fd_out, struct mail_user *user, return NULL; } + if (var_has_key(set->pop3_logout_format, 'u', "uidl_change") && + client->messages_count > 0) + client->message_uidl_hashes_save = TRUE; + client->uidl_keymask = parse_uidl_keymask(client->mail_set->pop3_uidl_format); if (client->uidl_keymask == 0) @@ -281,6 +285,49 @@ struct client *client_create(int fd_in, int fd_out, struct mail_user *user, return client; } +static const char *client_build_uidl_change_string(struct client *client) +{ + uint32_t i, old_hash, new_hash; + unsigned int old_msg_count, new_msg_count; + + if (client->message_uidl_hashes == NULL) { + /* UIDL command not given or %u not actually used in format */ + return ""; + } + if (client->message_uidl_hashes_save) { + /* UIDL command not finished */ + return ""; + } + + /* 1..new-1 were probably left to mailbox by previous POP3 session */ + old_msg_count = client->lowest_retr > 0 ? + client->lowest_retr - 1 : client->messages_count; + for (i = 0, old_hash = 0; i < old_msg_count; i++) + old_hash ^= client->message_uidl_hashes[i]; + + /* assume all except deleted messages were sent to POP3 client */ + if (!client->deleted) { + for (i = 0, new_hash = 0; i < client->messages_count; i++) + new_hash ^= client->message_uidl_hashes[i]; + } else { + for (i = 0, new_hash = 0; i < client->messages_count; i++) { + if (client->deleted_bitmask[i / CHAR_BIT] & + (1 << (i % CHAR_BIT))) + continue; + new_hash ^= client->message_uidl_hashes[i]; + } + } + + new_msg_count = client->messages_count - client->deleted_count; + if (old_hash == new_hash && old_msg_count == new_msg_count) + return t_strdup_printf("%u/%08x", old_msg_count, old_hash); + else { + return t_strdup_printf("%u/%08x -> %u/%08x", + old_msg_count, old_hash, + new_msg_count, new_hash); + } +} + static const char *client_stats(struct client *client) { static struct var_expand_table static_tab[] = { @@ -293,6 +340,7 @@ static const char *client_stats(struct client *client) { 's', NULL, "message_bytes" }, { 'i', NULL, "input" }, { 'o', NULL, "output" }, + { 'u', NULL, "uidl_change" }, { '\0', NULL, NULL } }; struct var_expand_table *tab; @@ -310,6 +358,7 @@ static const char *client_stats(struct client *client) tab[6].value = dec2str(client->total_size); tab[7].value = dec2str(client->input->v_offset); tab[8].value = dec2str(client->output->offset); + tab[9].value = client_build_uidl_change_string(client); str = t_str_new(128); var_expand(str, client->set->pop3_logout_format, tab); @@ -363,6 +412,7 @@ void client_destroy(struct client *client, const char *reason) mail_user_unref(&client->user); i_free(client->message_sizes); + i_free(client->message_uidl_hashes); i_free(client->deleted_bitmask); i_free(client->seen_bitmask); diff --git a/src/pop3/pop3-client.h b/src/pop3/pop3-client.h index 97bb57cd2c..cce38ab10e 100644 --- a/src/pop3/pop3-client.h +++ b/src/pop3/pop3-client.h @@ -34,10 +34,11 @@ struct client { unsigned int uid_validity; unsigned int messages_count; unsigned int deleted_count, expunged_count, seen_change_count; + uint32_t *message_uidl_hashes; uoff_t *message_sizes; uoff_t total_size; uoff_t deleted_size; - uint32_t last_seen; + uint32_t last_seen, lowest_retr; uoff_t top_bytes; uoff_t retr_bytes; @@ -59,6 +60,7 @@ struct client { unsigned int deleted:1; unsigned int waiting_input:1; unsigned int anvil_sent:1; + unsigned int message_uidl_hashes_save:1; }; extern struct client *pop3_clients; diff --git a/src/pop3/pop3-commands.c b/src/pop3/pop3-commands.c index aed720cda3..66a97bb2c8 100644 --- a/src/pop3/pop3-commands.c +++ b/src/pop3/pop3-commands.c @@ -5,6 +5,7 @@ #include "istream.h" #include "ostream.h" #include "str.h" +#include "crc32.h" #include "var-expand.h" #include "message-size.h" #include "mail-storage.h" @@ -438,6 +439,8 @@ static int cmd_retr(struct client *client, const char *args) if (get_msgnum(client, args, &msgnum) == NULL) return -1; + if (client->lowest_retr > msgnum+1 || client->lowest_retr == 0) + client->lowest_retr = msgnum+1; if (client->last_seen <= msgnum) client->last_seen = msgnum+1; @@ -586,16 +589,23 @@ static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx) string_t *str; int ret; unsigned int uidl_pos; - bool found = FALSE; + bool save_hashes, found = FALSE; tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = t_strdup_printf("%u", client->uid_validity); + save_hashes = client->message_uidl_hashes_save && ctx->message == 0; + if (save_hashes && client->message_uidl_hashes == NULL) { + client->message_uidl_hashes = + i_new(uint32_t, client->messages_count); + } + str = t_str_new(128); while (mailbox_search_next(ctx->search_ctx, ctx->mail)) { + uint32_t idx = ctx->mail->seq - 1; + if (client->deleted) { - uint32_t idx = ctx->mail->seq - 1; if (client->deleted_bitmask[idx / CHAR_BIT] & (1 << (idx % CHAR_BIT))) continue; @@ -610,6 +620,11 @@ static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx) client->set->pop3_save_uidl) mail_update_pop3_uidl(ctx->mail, str_c(str) + uidl_pos); + if (save_hashes) { + client->message_uidl_hashes[idx] = + crc32_str(str_c(str) + uidl_pos); + } + ret = client_send_line(client, "%s", str_c(str)); if (ret < 0) break; @@ -626,6 +641,8 @@ static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx) client->cmd = NULL; + if (save_hashes) + client->message_uidl_hashes_save = FALSE; if (ctx->message == 0) client_send_line(client, "."); i_free(ctx);