]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
pop3: Added %u=old/new UIDL hash to pop3_logout_format.
authorTimo Sirainen <tss@iki.fi>
Wed, 14 Apr 2010 15:29:15 +0000 (18:29 +0300)
committerTimo Sirainen <tss@iki.fi>
Wed, 14 Apr 2010 15:29:15 +0000 (18:29 +0300)
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.

--HG--
branch : HEAD

doc/example-config/conf.d/20-pop3.conf
src/pop3/pop3-client.c
src/pop3/pop3-client.h
src/pop3/pop3-commands.c

index 0bc44af212feadec636410b006b906de668ec2e2..1b32fda753529b91da2ce2275172ec10db90cb52 100644 (file)
@@ -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.
index ad9b7065bdd12080f25b4a8723a423b653467588..e0bbabae220a82c4e7eeb27d9db259b8c44189e6 100644 (file)
@@ -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);
 
index 97bb57cd2ca9e63849bb2fc22079c9907dce3e2b..cce38ab10e6cd144ca41449d19038dd6cf5c7313 100644 (file)
@@ -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;
index aed720cda3ffc34fc847e421ad3f1b16aedf329a..66a97bb2c81220640834a1be5f6fcd1ad7c89dc2 100644 (file)
@@ -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);