Mercurial > dovecot > core-2.2
changeset 12990:c9b7e829c6a9
pop3: Added support for showing messages in "pop3 order".
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 04 May 2011 11:43:59 +0200 |
parents | fac2d4fe86b1 |
children | d0f54521bb3b |
files | src/pop3/pop3-client.c src/pop3/pop3-client.h src/pop3/pop3-commands.c |
diffstat | 3 files changed, 148 insertions(+), 58 deletions(-) [+] |
line wrap: on
line diff
--- a/src/pop3/pop3-client.c Wed May 04 11:43:16 2011 +0200 +++ b/src/pop3/pop3-client.c Wed May 04 11:43:59 2011 +0200 @@ -39,6 +39,11 @@ struct client *pop3_clients; unsigned int pop3_client_count; +static enum mail_sort_type pop3_sort_program[] = { + MAIL_SORT_POP3_ORDER, + MAIL_SORT_END +}; + static void client_input(struct client *client); static int client_output(struct client *client); @@ -103,6 +108,28 @@ return mail_get_virtual_size(mail, size_r); } +static void +msgnum_to_seq_map_add(ARRAY_TYPE(uint32_t) *msgnum_to_seq_map, + struct client *client, struct mail *mail, + unsigned int msgnum) +{ + uint32_t seq; + + if (mail->seq == msgnum+1) + return; + + if (!array_is_created(msgnum_to_seq_map)) + i_array_init(msgnum_to_seq_map, client->messages_count); + else { + /* add any messages between this and the previous one that had + a POP3 order defined */ + seq = array_count(msgnum_to_seq_map) + 1; + for (; seq <= msgnum; seq++) + array_append(msgnum_to_seq_map, &seq, 1); + } + array_append(msgnum_to_seq_map, &mail->seq, 1); +} + static int read_mailbox(struct client *client, uint32_t *failed_uid_r) { struct mailbox_status status; @@ -112,6 +139,8 @@ struct mail *mail; uoff_t size; ARRAY_DEFINE(message_sizes, uoff_t); + ARRAY_TYPE(uint32_t) msgnum_to_seq_map = ARRAY_INIT; + unsigned int msgnum; int ret = 1; *failed_uid_r = 0; @@ -124,13 +153,14 @@ search_args = mail_search_build_init(); mail_search_build_add_all(search_args); - ctx = mailbox_search_init(t, search_args, NULL); + ctx = mailbox_search_init(t, search_args, pop3_sort_program); mail_search_args_unref(&search_args); - client->last_seen = 0; + client->last_seen_pop3_msn = 0; client->total_size = 0; i_array_init(&message_sizes, client->messages_count); + msgnum = 0; mail = mail_alloc(t, MAIL_FETCH_VIRTUAL_SIZE, NULL); while (mailbox_search_next(ctx, mail)) { if (pop3_mail_get_size(client, mail, &size) < 0) { @@ -138,29 +168,40 @@ *failed_uid_r = mail->uid; break; } + msgnum_to_seq_map_add(&msgnum_to_seq_map, client, mail, msgnum); if ((mail_get_flags(mail) & MAIL_SEEN) != 0) - client->last_seen = mail->seq; + client->last_seen_pop3_msn = msgnum + 1; client->total_size += size; array_append(&message_sizes, &size, 1); + msgnum++; } mail_free(&mail); if (mailbox_search_deinit(&ctx) < 0) ret = -1; - if (ret > 0) { - client->trans = t; - client->message_sizes = - buffer_free_without_data(&message_sizes.arr.buffer); - } else { + if (ret <= 0) { /* commit the transaction instead of rollbacking to make sure we don't lose data (virtual sizes) added to cache file */ (void)mailbox_transaction_commit(&t); array_free(&message_sizes); + if (array_is_created(&msgnum_to_seq_map)) + array_free(&msgnum_to_seq_map); + return ret; } - return ret; + + client->trans = t; + client->message_sizes = + buffer_free_without_data(&message_sizes.arr.buffer); + if (array_is_created(&msgnum_to_seq_map)) { + client->msgnum_to_seq_map_count = + array_count(&msgnum_to_seq_map); + client->msgnum_to_seq_map = + buffer_free_without_data(&msgnum_to_seq_map.arr.buffer); + } + return 1; } static int init_mailbox(struct client *client, const char **error_r) @@ -349,8 +390,8 @@ } /* 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; + 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]; @@ -464,6 +505,7 @@ i_free(client->message_uidl_hashes); i_free(client->deleted_bitmask); i_free(client->seen_bitmask); + i_free(client->msgnum_to_seq_map); if (client->io != NULL) io_remove(&client->io);
--- a/src/pop3/pop3-client.h Wed May 04 11:43:16 2011 +0200 +++ b/src/pop3/pop3-client.h Wed May 04 11:43:59 2011 +0200 @@ -9,6 +9,12 @@ #define MSGS_BITMASK_SIZE(client) \ (((client)->messages_count + (CHAR_BIT-1)) / CHAR_BIT) +/* + pop3_msn = 1..n in the POP3 protocol + msgnum = 0..n-1 = pop3_msn-1 + seq = 1..n = mail's sequence number in lib-storage. when pop3 sort ordering + is used, msgnum_to_seq_map[] can be used for translation. +*/ struct client { struct client *prev, *next; @@ -34,17 +40,23 @@ 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, lowest_retr; + uint32_t last_seen_pop3_msn, lowest_retr_pop3_msn; + + /* [msgnum] contains mail seq. anything after it has seq = msgnum+1 */ + uint32_t *msgnum_to_seq_map; + uint32_t msgnum_to_seq_map_count; uoff_t top_bytes; uoff_t retr_bytes; unsigned int top_count; unsigned int retr_count; + /* [msgnum] */ + uint32_t *message_uidl_hashes; + uoff_t *message_sizes; + /* [msgnum/8] & msgnum%8 */ unsigned char *deleted_bitmask; unsigned char *seen_bitmask;
--- a/src/pop3/pop3-commands.c Wed May 04 11:43:16 2011 +0200 +++ b/src/pop3/pop3-commands.c Wed May 04 11:43:59 2011 +0200 @@ -14,6 +14,17 @@ #include "pop3-capability.h" #include "pop3-commands.h" +static enum mail_sort_type pop3_sort_program[] = { + MAIL_SORT_POP3_ORDER, + MAIL_SORT_END +}; + +static uint32_t msgnum_to_seq(struct client *client, uint32_t msgnum) +{ + return msgnum < client->msgnum_to_seq_map_count ? + client->msgnum_to_seq_map[msgnum] : msgnum+1; +} + static const char *get_msgnum(struct client *client, const char *args, unsigned int *msgnum) { @@ -132,8 +143,8 @@ (1 << (ctx->msgnum % CHAR_BIT))) continue; } - ret = client_send_line(client, "%u %"PRIuUOFF_T, - ctx->msgnum+1, + + ret = client_send_line(client, "%u %"PRIuUOFF_T, ctx->msgnum+1, client->message_sizes[ctx->msgnum]); if (ret < 0) break; @@ -172,7 +183,7 @@ static int cmd_last(struct client *client, const char *args ATTR_UNUSED) { - client_send_line(client, "+OK %u", client->last_seen); + client_send_line(client, "+OK %u", client->last_seen_pop3_msn); return 1; } @@ -197,12 +208,28 @@ return search_args; } +static int client_verify_ordering(struct client *client, + struct mail *mail, uint32_t msgnum) +{ + uint32_t seq; + + seq = msgnum_to_seq(client, msgnum); + if (seq != mail->seq) { + i_error("Message ordering changed unexpectedly " + "(msg #%u: storage seq %u -> %u)", + msgnum+1, seq, mail->seq); + return -1; + } + return 0; +} + bool client_update_mails(struct client *client) { struct mail_search_args *search_args; struct mail_search_context *ctx; struct mail *mail; - uint32_t idx, bit; + uint32_t msgnum, bit; + bool ret = TRUE; if (mailbox_is_readonly(client->mailbox)) { /* silently ignore */ @@ -210,26 +237,35 @@ } search_args = pop3_search_build(client, 0); - ctx = mailbox_search_init(client->trans, search_args, NULL); + ctx = mailbox_search_init(client->trans, search_args, + pop3_sort_program); mail_search_args_unref(&search_args); + msgnum = 0; mail = mail_alloc(client->trans, 0, NULL); while (mailbox_search_next(ctx, mail)) { - idx = mail->seq - 1; - bit = 1 << (idx % CHAR_BIT); + if (client_verify_ordering(client, mail, msgnum) < 0) { + ret = FALSE; + break; + } + + bit = 1 << (msgnum % CHAR_BIT); if (client->deleted_bitmask != NULL && - (client->deleted_bitmask[idx / CHAR_BIT] & bit) != 0) { + (client->deleted_bitmask[msgnum / CHAR_BIT] & bit) != 0) { mail_expunge(mail); client->expunged_count++; } else if (client->seen_bitmask != NULL && - (client->seen_bitmask[idx / CHAR_BIT] & bit) != 0) { + (client->seen_bitmask[msgnum / CHAR_BIT] & bit) != 0) { mail_update_flags(mail, MODIFY_ADD, MAIL_SEEN); } + msgnum++; } mail_free(&mail); client->seen_change_count = 0; - return mailbox_search_deinit(&ctx) == 0; + if (mailbox_search_deinit(&ctx) < 0) + ret = FALSE; + return ret; } static int cmd_quit(struct client *client, const char *args ATTR_UNUSED) @@ -260,7 +296,6 @@ } struct fetch_context { - struct mail_search_context *search_ctx; struct mail *mail; struct istream *stream; uoff_t body_lines; @@ -274,7 +309,6 @@ static void fetch_deinit(struct fetch_context *ctx) { - (void)mailbox_search_deinit(&ctx->search_ctx); mail_free(&ctx->mail); i_free(ctx); } @@ -393,23 +427,17 @@ uoff_t *byte_counter) { struct fetch_context *ctx; - struct mail_search_args *search_args; int ret; - search_args = pop3_search_build(client, msgnum+1); - ctx = i_new(struct fetch_context, 1); - ctx->search_ctx = mailbox_search_init(client->trans, search_args, NULL); - mail_search_args_unref(&search_args); - ctx->byte_counter = byte_counter; ctx->byte_counter_offset = client->output->offset; ctx->mail = mail_alloc(client->trans, MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, NULL); + mail_set_seq(ctx->mail, msgnum_to_seq(client, msgnum)); - if (!mailbox_search_next(ctx->search_ctx, ctx->mail) || - mail_get_stream(ctx->mail, NULL, NULL, &ctx->stream) < 0) { + if (mail_get_stream(ctx->mail, NULL, NULL, &ctx->stream) < 0) { ret = client_reply_msg_expunged(client, msgnum); fetch_deinit(ctx); return ret; @@ -418,7 +446,8 @@ if (body_lines == (uoff_t)-1 && client->seen_bitmask != NULL) { if ((mail_get_flags(ctx->mail) & MAIL_SEEN) == 0) { /* mark the message seen with RETR command */ - client->seen_bitmask[msgnum / CHAR_BIT] |= 1 << (msgnum % CHAR_BIT); + client->seen_bitmask[msgnum / CHAR_BIT] |= + 1 << (msgnum % CHAR_BIT); client->seen_change_count++; } } @@ -445,10 +474,11 @@ 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; + if (client->lowest_retr_pop3_msn > msgnum+1 || + client->lowest_retr_pop3_msn == 0) + client->lowest_retr_pop3_msn = msgnum+1; + if (client->last_seen_pop3_msn < msgnum+1) + client->last_seen_pop3_msn = msgnum+1; client->retr_count++; return fetch(client, msgnum, (uoff_t)-1, &client->retr_bytes); @@ -460,7 +490,7 @@ struct mail *mail; struct mail_search_args *search_args; - client->last_seen = 0; + client->last_seen_pop3_msn = 0; if (client->deleted) { client->deleted = FALSE; @@ -496,8 +526,8 @@ static int cmd_stat(struct client *client, const char *args ATTR_UNUSED) { - client_send_line(client, "+OK %u %"PRIuUOFF_T, client-> - messages_count - client->deleted_count, + client_send_line(client, "+OK %u %"PRIuUOFF_T, + client->messages_count - client->deleted_count, client->total_size - client->deleted_size); return 1; } @@ -520,7 +550,8 @@ struct cmd_uidl_context { struct mail_search_context *search_ctx; struct mail *mail; - unsigned int message; + uint32_t msgnum; + bool list_all; }; static bool pop3_get_uid(struct client *client, struct cmd_uidl_context *ctx, @@ -595,7 +626,7 @@ 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; + 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); @@ -603,32 +634,33 @@ str = t_str_new(128); while (mailbox_search_next(ctx->search_ctx, ctx->mail)) { - uint32_t idx = ctx->mail->seq - 1; + uint32_t msgnum = ctx->msgnum++; + if (client_verify_ordering(client, ctx->mail, msgnum) < 0) + i_fatal("Can't finish POP3 UIDL command"); if (client->deleted) { - if (client->deleted_bitmask[idx / CHAR_BIT] & - (1 << (idx % CHAR_BIT))) + if (client->deleted_bitmask[msgnum / CHAR_BIT] & + (1 << (msgnum % CHAR_BIT))) continue; } found = TRUE; str_truncate(str, 0); - str_printfa(str, ctx->message == 0 ? "%u " : "+OK %u ", - ctx->mail->seq); + 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); if (save_hashes) { - client->message_uidl_hashes[idx] = + client->message_uidl_hashes[msgnum] = crc32_str(str_c(str) + uidl_pos); } ret = client_send_line(client, "%s", str_c(str)); if (ret < 0) break; - if (ret == 0 && ctx->message == 0) { + if (ret == 0 && ctx->list_all) { /* output is being buffered, continue when there's more space */ return FALSE; @@ -643,7 +675,7 @@ if (save_hashes) client->message_uidl_hashes_save = FALSE; - if (ctx->message == 0) + if (ctx->list_all) client_send_line(client, "."); i_free(ctx); return found; @@ -657,26 +689,27 @@ } static struct cmd_uidl_context * -cmd_uidl_init(struct client *client, unsigned int message) +cmd_uidl_init(struct client *client, uint32_t seq) { struct cmd_uidl_context *ctx; struct mail_search_args *search_args; enum mail_fetch_field wanted_fields; - search_args = pop3_search_build(client, message); + search_args = pop3_search_build(client, seq); ctx = i_new(struct cmd_uidl_context, 1); - ctx->message = message; + ctx->list_all = seq == 0; 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, NULL); + ctx->search_ctx = mailbox_search_init(client->trans, search_args, + pop3_sort_program); mail_search_args_unref(&search_args); ctx->mail = mail_alloc(client->trans, wanted_fields, NULL); - if (message == 0) { + if (seq == 0) { client->cmd = cmd_uidl_callback; client->cmd_context = ctx; } @@ -686,6 +719,7 @@ static int cmd_uidl(struct client *client, const char *args) { struct cmd_uidl_context *ctx; + uint32_t seq; if (*args == '\0') { client_send_line(client, "+OK"); @@ -697,7 +731,9 @@ if (get_msgnum(client, args, &msgnum) == NULL) return -1; - ctx = cmd_uidl_init(client, msgnum+1); + seq = msgnum_to_seq(client, msgnum); + ctx = cmd_uidl_init(client, seq); + ctx->msgnum = msgnum; if (!list_uids_iter(client, ctx)) return client_reply_msg_expunged(client, msgnum); }