Mercurial > dovecot > core-2.2
changeset 14563:3a095892242b
pop3: Added pop3_uidl_duplicates setting.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 14 May 2012 21:07:43 +0300 |
parents | 4bbc12a87a29 |
children | 7dd1dd742825 |
files | doc/example-config/conf.d/20-pop3.conf src/pop3/pop3-client.c src/pop3/pop3-client.h src/pop3/pop3-commands.c src/pop3/pop3-settings.c src/pop3/pop3-settings.h |
diffstat | 6 files changed, 220 insertions(+), 104 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/example-config/conf.d/20-pop3.conf Mon May 14 19:30:03 2012 +0300 +++ b/doc/example-config/conf.d/20-pop3.conf Mon May 14 21:07:43 2012 +0300 @@ -54,6 +54,11 @@ # won't change those UIDLs. Currently this works only with Maildir. #pop3_save_uidl = no + # What to do about duplicate UIDLs if they exist? + # allow: Show duplicates to clients. + # rename: Append a temporary -2, -3, etc. counter after the UIDL. + #pop3_uidl_duplicates = allow + # POP3 logout format string: # %i - total number of bytes read from client # %o - total number of bytes sent to client
--- a/src/pop3/pop3-client.c Mon May 14 19:30:03 2012 +0300 +++ b/src/pop3/pop3-client.c Mon May 14 21:07:43 2012 +0300 @@ -6,6 +6,7 @@ #include "network.h" #include "istream.h" #include "ostream.h" +#include "crc32.h" #include "str.h" #include "llist.h" #include "hostpid.h" @@ -344,15 +345,20 @@ 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) i_fatal("Invalid pop3_uidl_format"); + if (var_has_key(set->pop3_logout_format, 'u', "uidl_change")) { + /* logging uidl_change. we need hashes of the UIDLs */ + client->message_uidls_save = TRUE; + } else if (strcmp(set->pop3_uidl_duplicates, "allow") != 0) { + /* UIDL duplicates aren't allowed, so we'll need to + keep track of them */ + client->message_uidls_save = TRUE; + } + if (!set->pop3_no_flag_updates && client->messages_count > 0) client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client)); @@ -375,12 +381,8 @@ 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 */ + if (client->message_uidls == NULL) { + /* UIDL command not given */ return ""; } @@ -388,18 +390,18 @@ old_msg_count = client->lowest_retr_pop3_msn > 0 ? client->lowest_retr_pop3_msn - 1 : client->messages_count; for (i = 0, old_hash = 0; i < old_msg_count; i++) - old_hash ^= client->message_uidl_hashes[i]; + old_hash ^= crc32_str(client->message_uidls[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]; + new_hash ^= crc32_str(client->message_uidls[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_hash ^= crc32_str(client->message_uidls[i]); } } @@ -444,7 +446,11 @@ 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); + if (var_has_key(client->set->pop3_logout_format, + tab[9].key, tab[9].long_key)) + tab[9].value = client_build_uidl_change_string(client); + else + tab[9].value = ""; tab[10].value = client->session_id; str = t_str_new(128); @@ -498,8 +504,9 @@ } mail_user_unref(&client->user); + if (client->uidl_pool != NULL) + pool_unref(&client->uidl_pool); i_free(client->message_sizes); - i_free(client->message_uidl_hashes); i_free(client->deleted_bitmask); i_free(client->seen_bitmask); i_free(client->msgnum_to_seq_map);
--- a/src/pop3/pop3-client.h Mon May 14 19:30:03 2012 +0300 +++ b/src/pop3/pop3-client.h Mon May 14 21:07:43 2012 +0300 @@ -55,7 +55,7 @@ unsigned int retr_count; /* [msgnum] */ - uint32_t *message_uidl_hashes; + const char **message_uidls; uoff_t *message_sizes; /* [msgnum/8] & msgnum%8 */ unsigned char *deleted_bitmask; @@ -64,13 +64,14 @@ /* settings: */ const struct pop3_settings *set; const struct mail_storage_settings *mail_set; + pool_t uidl_pool; enum uidl_keys uidl_keymask; unsigned int disconnected:1; unsigned int deleted:1; unsigned int waiting_input:1; unsigned int anvil_sent:1; - unsigned int message_uidl_hashes_save:1; + unsigned int message_uidls_save:1; }; extern struct client *pop3_clients;
--- a/src/pop3/pop3-commands.c Mon May 14 19:30:03 2012 +0300 +++ b/src/pop3/pop3-commands.c Mon May 14 21:07:43 2012 +0300 @@ -4,8 +4,8 @@ #include "array.h" #include "istream.h" #include "ostream.h" +#include "hash.h" #include "str.h" -#include "crc32.h" #include "var-expand.h" #include "message-size.h" #include "mail-storage.h" @@ -550,62 +550,9 @@ bool list_all; }; -static bool pop3_get_uid(struct client *client, struct cmd_uidl_context *ctx, - struct var_expand_table *tab, string_t *str) -{ - char uid_str[MAX_INT_STRLEN]; - const char *uidl; - - if (mail_get_special(ctx->mail, MAIL_FETCH_UIDL_BACKEND, &uidl) == 0 && - *uidl != '\0') { - str_append(str, uidl); - return TRUE; - } - - if (client->set->pop3_reuse_xuidl && - mail_get_first_header(ctx->mail, "X-UIDL", &uidl) > 0) { - str_append(str, uidl); - return FALSE; - } - - if ((client->uidl_keymask & UIDL_UID) != 0) { - i_snprintf(uid_str, sizeof(uid_str), "%u", - ctx->mail->uid); - tab[1].value = uid_str; - } - if ((client->uidl_keymask & UIDL_MD5) != 0) { - if (mail_get_special(ctx->mail, MAIL_FETCH_HEADER_MD5, - &tab[2].value) < 0 || - *tab[2].value == '\0') { - /* broken */ - i_fatal("UIDL: Header MD5 not found " - "(pop3_uidl_format=%%m not supported by storage?)"); - } - } - if ((client->uidl_keymask & UIDL_FILE_NAME) != 0) { - if (mail_get_special(ctx->mail, - MAIL_FETCH_UIDL_FILE_NAME, - &tab[3].value) < 0 || - *tab[3].value == '\0') { - /* broken */ - i_fatal("UIDL: File name not found " - "(pop3_uidl_format=%%f not supported by storage?)"); - } - } - if ((client->uidl_keymask & UIDL_GUID) != 0) { - if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, - &tab[4].value) < 0 || - *tab[4].value == '\0') { - /* broken */ - i_fatal("UIDL: Message GUID not found " - "(pop3_uidl_format=%%g not supported by storage?)"); - } - } - var_expand(str, client->mail_set->pop3_uidl_format, tab); - return FALSE; -} - -static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx) +static void +pop3_get_uid(struct client *client, struct mail *mail, string_t *str, + bool *permanent_uidl_r) { static struct var_expand_table static_tab[] = { { 'v', NULL, "uidvalidity" }, @@ -616,20 +563,108 @@ { '\0', NULL, NULL } }; struct var_expand_table *tab; - string_t *str; - int ret; - unsigned int uidl_pos; - bool save_hashes, found = FALSE; + char uid_str[MAX_INT_STRLEN]; + const char *uidl; + + if (mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &uidl) == 0 && + *uidl != '\0') { + str_append(str, uidl); + /* UIDL is already permanent */ + *permanent_uidl_r = TRUE; + return; + } + + *permanent_uidl_r = FALSE; + + if (client->set->pop3_reuse_xuidl && + mail_get_first_header(mail, "X-UIDL", &uidl) > 0) { + str_append(str, uidl); + return; + } 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->list_all; - if (save_hashes && client->message_uidl_hashes == NULL) { - client->message_uidl_hashes = - i_new(uint32_t, client->messages_count); + if ((client->uidl_keymask & UIDL_UID) != 0) { + i_snprintf(uid_str, sizeof(uid_str), "%u", + mail->uid); + tab[1].value = uid_str; + } + if ((client->uidl_keymask & UIDL_MD5) != 0) { + if (mail_get_special(mail, MAIL_FETCH_HEADER_MD5, + &tab[2].value) < 0 || + *tab[2].value == '\0') { + /* broken */ + i_fatal("UIDL: Header MD5 not found " + "(pop3_uidl_format=%%m not supported by storage?)"); + } + } + if ((client->uidl_keymask & UIDL_FILE_NAME) != 0) { + if (mail_get_special(mail, MAIL_FETCH_UIDL_FILE_NAME, + &tab[3].value) < 0 || + *tab[3].value == '\0') { + /* broken */ + i_fatal("UIDL: File name not found " + "(pop3_uidl_format=%%f not supported by storage?)"); + } + } + if ((client->uidl_keymask & UIDL_GUID) != 0) { + if (mail_get_special(mail, MAIL_FETCH_GUID, + &tab[4].value) < 0 || + *tab[4].value == '\0') { + /* broken */ + i_fatal("UIDL: Message GUID not found " + "(pop3_uidl_format=%%g not supported by storage?)"); + } } + var_expand(str, client->mail_set->pop3_uidl_format, tab); +} + +static bool +list_uidls_saved_iter(struct client *client, struct cmd_uidl_context *ctx) +{ + bool found = FALSE; + int ret; + + while (ctx->msgnum < client->messages_count) { + uint32_t msgnum = ctx->msgnum++; + + if (client->deleted) { + if (client->deleted_bitmask[msgnum / CHAR_BIT] & + (1 << (msgnum % CHAR_BIT))) + continue; + } + found = TRUE; + + ret = client_send_line(client, + ctx->list_all ? "%u %s" : "+OK %u %s", + msgnum+1, client->message_uidls[msgnum]); + if (ret < 0 || !ctx->list_all) + break; + if (ret == 0) { + /* output is being buffered, continue when there's + more space */ + return FALSE; + } + } + /* finished */ + client->cmd = NULL; + + if (ctx->list_all) + client_send_line(client, "."); + i_free(ctx); + return found; +} + +static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx) +{ + string_t *str; + int ret; + bool permanent_uidl, found = FALSE; + + if (client->message_uidls != NULL) + return list_uidls_saved_iter(client, ctx); str = t_str_new(128); while (mailbox_search_next(ctx->search_ctx, &ctx->mail)) { @@ -645,18 +680,13 @@ found = TRUE; str_truncate(str, 0); - str_printfa(str, ctx->list_all ? "%u " : "+OK %u ", msgnum+1); - uidl_pos = str_len(str); - if (!pop3_get_uid(client, ctx, tab, str) && - client->set->pop3_save_uidl) - mail_update_pop3_uidl(ctx->mail, str_c(str) + uidl_pos); + pop3_get_uid(client, ctx->mail, str, &permanent_uidl); + if (client->set->pop3_save_uidl && !permanent_uidl) + mail_update_pop3_uidl(ctx->mail, str_c(str)); - if (save_hashes) { - client->message_uidl_hashes[msgnum] = - crc32_str(str_c(str) + uidl_pos); - } - - ret = client_send_line(client, "%s", str_c(str)); + ret = client_send_line(client, + ctx->list_all ? "%u %s" : "+OK %u %s", + msgnum+1, str_c(str)); if (ret < 0) break; if (ret == 0 && ctx->list_all) { @@ -671,8 +701,6 @@ client->cmd = NULL; - if (save_hashes) - client->message_uidl_hashes_save = FALSE; if (ctx->list_all) client_send_line(client, "."); i_free(ctx); @@ -686,6 +714,74 @@ (void)list_uids_iter(client, ctx); } +static void uidl_rename_duplicate(string_t *uidl, struct hash_table *prev_uidls) +{ + void *key, *value; + unsigned int counter; + + while (hash_table_lookup_full(prev_uidls, str_c(uidl), &key, &value)) { + /* duplicate. the value contains the number of duplicates. */ + counter = POINTER_CAST_TO(value, unsigned int) + 1; + hash_table_update(prev_uidls, key, POINTER_CAST(counter)); + str_printfa(uidl, "-%u", counter); + /* the second lookup really should return NULL, but just in + case of some weird UIDLs do this as many times as needed */ + } +} + +static void client_uidls_save(struct client *client) +{ + struct mail_search_context *search_ctx; + struct mail_search_args *search_args; + struct mail *mail; + struct hash_table *prev_uidls; + string_t *str; + char *uidl; + enum mail_fetch_field wanted_fields; + uint32_t msgnum; + bool permanent_uidl, uidl_duplicates_rename; + + i_assert(client->message_uidls == NULL); + + search_args = pop3_search_build(client, 0); + wanted_fields = 0; + if ((client->uidl_keymask & UIDL_MD5) != 0) + wanted_fields |= MAIL_FETCH_HEADER_MD5; + + search_ctx = mailbox_search_init(client->trans, search_args, + pop3_sort_program, + wanted_fields, NULL); + mail_search_args_unref(&search_args); + + uidl_duplicates_rename = + strcmp(client->set->pop3_uidl_duplicates, "rename") == 0; + prev_uidls = hash_table_create(default_pool, default_pool, 0, + str_hash, (hash_cmp_callback_t *)strcmp); + client->uidl_pool = pool_alloconly_create("message uidls", 1024); + client->message_uidls = p_new(client->uidl_pool, const char *, + client->messages_count); + + str = t_str_new(128); msgnum = 0; + while (mailbox_search_next(search_ctx, &mail)) { + if (client_verify_ordering(client, mail, msgnum) < 0) + i_fatal("Can't finish POP3 UIDL command"); + + str_truncate(str, 0); + pop3_get_uid(client, mail, str, &permanent_uidl); + if (client->set->pop3_save_uidl && !permanent_uidl) + mail_update_pop3_uidl(mail, str_c(str)); + + if (uidl_duplicates_rename) + uidl_rename_duplicate(str, prev_uidls); + uidl = p_strdup(client->uidl_pool, str_c(str)); + client->message_uidls[msgnum] = uidl; + hash_table_insert(prev_uidls, uidl, POINTER_CAST(1)); + msgnum++; + } + (void)mailbox_search_deinit(&search_ctx); + hash_table_destroy(&prev_uidls); +} + static struct cmd_uidl_context * cmd_uidl_init(struct client *client, uint32_t seq) { @@ -693,19 +789,23 @@ struct mail_search_args *search_args; enum mail_fetch_field wanted_fields; - search_args = pop3_search_build(client, seq); + if (client->message_uidls_save && client->message_uidls == NULL) + client_uidls_save(client); ctx = i_new(struct cmd_uidl_context, 1); ctx->list_all = seq == 0; - wanted_fields = 0; - if ((client->uidl_keymask & UIDL_MD5) != 0) - wanted_fields |= MAIL_FETCH_HEADER_MD5; + if (client->message_uidls == NULL) { + wanted_fields = 0; + if ((client->uidl_keymask & UIDL_MD5) != 0) + wanted_fields |= MAIL_FETCH_HEADER_MD5; - ctx->search_ctx = mailbox_search_init(client->trans, search_args, - pop3_sort_program, - wanted_fields, NULL); - mail_search_args_unref(&search_args); + search_args = pop3_search_build(client, seq); + ctx->search_ctx = mailbox_search_init(client->trans, search_args, + pop3_sort_program, + wanted_fields, NULL); + mail_search_args_unref(&search_args); + } if (seq == 0) { client->cmd = cmd_uidl_callback;
--- a/src/pop3/pop3-settings.c Mon May 14 19:30:03 2012 +0300 +++ b/src/pop3/pop3-settings.c Mon May 14 21:07:43 2012 +0300 @@ -70,6 +70,7 @@ DEF(SET_BOOL, pop3_fast_size_lookups), DEF(SET_STR, pop3_client_workarounds), DEF(SET_STR, pop3_logout_format), + DEF(SET_ENUM, pop3_uidl_duplicates), SETTING_DEFINE_LIST_END }; @@ -84,7 +85,8 @@ .pop3_lock_session = FALSE, .pop3_fast_size_lookups = FALSE, .pop3_client_workarounds = "", - .pop3_logout_format = "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s" + .pop3_logout_format = "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s", + .pop3_uidl_duplicates = "allow:rename" }; static const struct setting_parser_info *pop3_setting_dependencies[] = {
--- a/src/pop3/pop3-settings.h Mon May 14 19:30:03 2012 +0300 +++ b/src/pop3/pop3-settings.h Mon May 14 21:07:43 2012 +0300 @@ -22,6 +22,7 @@ bool pop3_fast_size_lookups; const char *pop3_client_workarounds; const char *pop3_logout_format; + const char *pop3_uidl_duplicates; enum pop3_client_workarounds parsed_workarounds; };