Mercurial > dovecot > core-2.2
changeset 11152:e019febd7eb3 HEAD
pop3: Added %u=old/new UIDL hash to pop3_logout_format.
It expands to "<old msg count>/<old hash> -> <new msg count>/<new hash>" or
if they're the same, simply "<msg count>/<hash>".
The idea is that if previous session's <new hash> doesn't match next one's
<old hash> 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.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 14 Apr 2010 18:29:15 +0300 |
parents | 093591e1110b |
children | eacb5fd16599 |
files | doc/example-config/conf.d/20-pop3.conf src/pop3/pop3-client.c src/pop3/pop3-client.h src/pop3/pop3-commands.c |
diffstat | 4 files changed, 73 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/example-config/conf.d/20-pop3.conf Wed Apr 14 16:06:59 2010 +0300 +++ b/doc/example-config/conf.d/20-pop3.conf Wed Apr 14 18:29:15 2010 +0300 @@ -57,6 +57,7 @@ # %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.
--- a/src/pop3/pop3-client.c Wed Apr 14 16:06:59 2010 +0300 +++ b/src/pop3/pop3-client.c Wed Apr 14 18:29:15 2010 +0300 @@ -258,6 +258,10 @@ 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 @@ 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 @@ { '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 @@ 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 @@ 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);
--- a/src/pop3/pop3-client.h Wed Apr 14 16:06:59 2010 +0300 +++ b/src/pop3/pop3-client.h Wed Apr 14 18:29:15 2010 +0300 @@ -34,10 +34,11 @@ 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 @@ 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;
--- a/src/pop3/pop3-commands.c Wed Apr 14 16:06:59 2010 +0300 +++ b/src/pop3/pop3-commands.c Wed Apr 14 18:29:15 2010 +0300 @@ -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 @@ 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 @@ 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 @@ 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 @@ client->cmd = NULL; + if (save_hashes) + client->message_uidl_hashes_save = FALSE; if (ctx->message == 0) client_send_line(client, "."); i_free(ctx);