From: Timo Sirainen Date: Sat, 26 Jul 2003 16:33:21 +0000 (+0300) Subject: API change for expunging messages. Not exactly what I wanted, but good X-Git-Tag: 1.1.alpha1~4466 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dbb1fb1c51727e2050792f8c333b212e22a36d69;p=thirdparty%2Fdovecot%2Fcore.git API change for expunging messages. Not exactly what I wanted, but good enough. --HG-- branch : HEAD --- diff --git a/src/imap/Makefile.am b/src/imap/Makefile.am index 121e03379a..a63f9eee74 100644 --- a/src/imap/Makefile.am +++ b/src/imap/Makefile.am @@ -61,6 +61,7 @@ imap_SOURCES = \ client.c \ commands.c \ commands-util.c \ + imap-expunge.c \ imap-fetch.c \ imap-fetch-body-section.c \ imap-search.c \ @@ -76,6 +77,7 @@ noinst_HEADERS = \ commands.h \ commands-util.h \ common.h \ + imap-expunge.h \ imap-fetch.h \ imap-search.h \ imap-sort.h \ diff --git a/src/imap/cmd-close.c b/src/imap/cmd-close.c index 02a364cd0d..50a541b9f9 100644 --- a/src/imap/cmd-close.c +++ b/src/imap/cmd-close.c @@ -2,6 +2,7 @@ #include "common.h" #include "commands.h" +#include "imap-expunge.h" int cmd_close(struct client *client) { @@ -13,7 +14,7 @@ int cmd_close(struct client *client) client->mailbox = NULL; if (!mailbox->readonly) { - if (!mailbox->expunge(mailbox, FALSE)) + if (!imap_expunge(mailbox, FALSE)) client_send_untagged_storage_error(client); } diff --git a/src/imap/cmd-expunge.c b/src/imap/cmd-expunge.c index 2e310662b6..719efc923f 100644 --- a/src/imap/cmd-expunge.c +++ b/src/imap/cmd-expunge.c @@ -2,13 +2,14 @@ #include "common.h" #include "commands.h" +#include "imap-expunge.h" int cmd_expunge(struct client *client) { if (!client_verify_open_mailbox(client)) return TRUE; - if (client->mailbox->expunge(client->mailbox, TRUE)) + if (imap_expunge(client->mailbox, TRUE)) client_send_tagline(client, "OK Expunge completed."); else client_send_storage_error(client); diff --git a/src/lib-index/mail-index.c b/src/lib-index/mail-index.c index c94ccc56c7..da28d9eda2 100644 --- a/src/lib-index/mail-index.c +++ b/src/lib-index/mail-index.c @@ -657,29 +657,6 @@ void mail_index_mark_flag_changes(struct mail_index *index, } } -#if 0 -static int mail_index_truncate_hole(struct mail_index *index) -{ - index->header->used_file_size = sizeof(struct mail_index_header) + - (uoff_t)index->header->first_hole_index * - sizeof(struct mail_index_record); - index->header->first_hole_index = 0; - index->header->first_hole_records = 0; - - index->mmap_used_length = index->header->used_file_size; - if (!mail_index_truncate(index)) - return FALSE; - - if (index->header->messages_count == 0) { - /* all mail was deleted, truncate data file */ - if (!mail_index_data_reset(index->data)) - return FALSE; - } - - return TRUE; -} -#endif - #define INDEX_NEED_COMPRESS(records, hdr) \ ((records) > INDEX_MIN_RECORDS_COUNT && \ (records) * (100-INDEX_COMPRESS_PERCENTAGE) / 100 > \ @@ -697,6 +674,8 @@ int mail_index_expunge(struct mail_index *index, i_assert(first_seq != 0); i_assert(first_seq <= last_seq); + index->expunge_counter++; + first_uid = first_rec->uid; last_uid = last_rec->uid; @@ -711,6 +690,12 @@ int mail_index_expunge(struct mail_index *index, return FALSE; } + if (index->header->messages_count == 0) { + /* all mail was deleted, truncate data file */ + if (!mail_index_data_reset(index->data)) + return FALSE; + } + return TRUE; } diff --git a/src/lib-index/mail-index.h b/src/lib-index/mail-index.h index 78ce3fa53c..bab2fa5d60 100644 --- a/src/lib-index/mail-index.h +++ b/src/lib-index/mail-index.h @@ -385,6 +385,8 @@ struct mail_index { /* updated whenever exclusive lock is set/unset */ unsigned int excl_lock_counter; + /* updated whenever expunge() is called */ + unsigned int expunge_counter; int mbox_fd; struct istream *mbox_stream; @@ -454,11 +456,11 @@ struct mail_index { members.. */ #define MAIL_INDEX_PRIVATE_FILL \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, { 0, 0, 0 }, 0, 0, \ + 0, 0, 0, 0, 0, 0, { 0, 0, 0 }, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0 #endif /* defaults - same as above but prefixed with mail_index_. */ diff --git a/src/lib-storage/index/index-expunge.c b/src/lib-storage/index/index-expunge.c index 7a7771f9f0..da9c0d2cc7 100644 --- a/src/lib-storage/index/index-expunge.c +++ b/src/lib-storage/index/index-expunge.c @@ -1,10 +1,12 @@ -/* Copyright (C) 2002 Timo Sirainen */ +/* Copyright (C) 2002-2003 Timo Sirainen */ #include "lib.h" #include "index-storage.h" +#include "index-expunge.h" -int index_expunge_seek_first(struct index_mailbox *ibox, unsigned int *seq, - struct mail_index_record **rec) +static int index_expunge_seek_first(struct index_mailbox *ibox, + unsigned int *seq, + struct mail_index_record **rec) { struct mail_index_header *hdr; @@ -43,70 +45,156 @@ int index_expunge_seek_first(struct index_mailbox *ibox, unsigned int *seq, return TRUE; } -int index_expunge_mails(struct index_mailbox *ibox, - struct mail_index_record *first_rec, - struct mail_index_record *last_rec, - unsigned int first_seq, unsigned int last_seq, - int notify) +struct mail_expunge_context * +index_storage_expunge_init(struct mailbox *box, + enum mail_fetch_field wanted_fields, + int expunge_all) { - unsigned int max; + struct index_mailbox *ibox = (struct index_mailbox *) box; + struct mail_expunge_context *ctx; - if (!ibox->index->expunge(ibox->index, first_rec, last_rec, - first_seq, last_seq, FALSE)) - return FALSE; + if (box->readonly) { + box->storage->callbacks-> + notify_no(box, "Mailbox is read-only, ignoring expunge", + box->storage->callback_context); + return i_new(struct mail_expunge_context, 1); + } - if (first_seq > ibox->synced_messages_count) - return TRUE; + if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE)) + return NULL; + + ctx = i_new(struct mail_expunge_context, 1); + ctx->ibox = ibox; + ctx->expunge_all = expunge_all; + index_mail_init(ibox, &ctx->mail, wanted_fields, NULL); + + do { + if (!index_storage_sync_and_lock(ibox, FALSE, TRUE, + MAIL_LOCK_EXCLUSIVE)) + break; + + /* modifylog must be marked synced before expunging + anything new */ + if (!index_storage_sync_modifylog(ibox, TRUE)) + break; + + if (expunge_all) { + ctx->seq = 1; + ctx->rec = ibox->index->lookup(ibox->index, 1); + } else { + if (!index_expunge_seek_first(ibox, &ctx->seq, + &ctx->rec)) + break; + + ctx->fetch_next = ctx->rec != NULL && + (ctx->rec->msg_flags & MAIL_DELETED) == 0; + } - max = last_seq > ibox->synced_messages_count ? - ibox->synced_messages_count : last_seq; + return ctx; + } while (0); - ibox->synced_messages_count -= max - first_seq + 1; - if (notify) { - struct mail_storage_callbacks *cb; - void *cb_ctx; + (void)index_storage_lock(ctx->ibox, MAIL_LOCK_UNLOCK); + i_free(ctx); + return NULL; +} - cb = ibox->box.storage->callbacks; - cb_ctx = ibox->box.storage->callback_context; +int index_storage_expunge_deinit(struct mail_expunge_context *ctx) +{ + int ret = !ctx->failed; + + if (ctx->first_seq != 0) { + if (!ctx->ibox->index->expunge(ctx->ibox->index, + ctx->first_rec, ctx->last_rec, + ctx->first_seq, ctx->last_seq, + FALSE)) + ret = FALSE; + } - while (max >= first_seq) { - cb->expunge(&ibox->box, first_seq, cb_ctx); - max--; - } + if (ctx->ibox != NULL) { + ctx->ibox->fetch_mail.data.rec = NULL; + + if (!index_storage_lock(ctx->ibox, MAIL_LOCK_UNLOCK)) + ret = FALSE; } - return TRUE; + i_free(ctx); + return ret; } -int index_storage_expunge(struct mailbox *box, int notify) +struct mail *index_storage_expunge_fetch_next(struct mail_expunge_context *ctx) { - struct index_mailbox *ibox = (struct index_mailbox *) box; - int failed; + struct mail_index *index = ctx->ibox->index; + + if (ctx->rec == NULL) + return NULL; + + if (ctx->fetch_next) { + do { + ctx->seq++; + ctx->rec = index->next(index, ctx->rec); + if (ctx->rec == NULL) + return NULL; + } while ((ctx->rec->msg_flags & MAIL_DELETED) == 0 && + !ctx->expunge_all); + } else { + ctx->fetch_next = TRUE; + } - if (box->readonly) { - box->storage->callbacks-> - notify_no(&ibox->box, - "Mailbox is read-only, ignoring expunge", - box->storage->callback_context); - return TRUE; + ctx->mail.expunge_counter = index->expunge_counter; + ctx->mail.mail.seq = ctx->seq; + ctx->mail.mail.uid = ctx->rec->uid; + + if (!index_mail_next(&ctx->mail, ctx->rec, ctx->seq)) { + ctx->failed = TRUE; + return NULL; } - if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE)) - return FALSE; + return &ctx->mail.mail; +} + +int index_storage_expunge(struct mail *mail, struct mail_expunge_context *ctx, + unsigned int *seq_r, int notify) +{ + struct index_mail *imail = (struct index_mail *) mail; + struct index_mailbox *ibox = imail->ibox; + unsigned int seq; + + /* currently we allow expunges only from beginning to end so we can + easily update sequence numbers */ + i_assert(ctx->last_seq < ctx->seq); + + seq = mail->seq; + if (ctx->first_seq != 0) + seq -= (ctx->last_seq - ctx->first_seq) + 1; + if (seq_r != NULL) *seq_r = seq; + + if (ctx->first_seq != 0 && ctx->seq != ctx->last_seq+1) { + if (!ibox->index->expunge(ibox->index, + ctx->first_rec, ctx->last_rec, + ctx->first_seq, ctx->last_seq, FALSE)) + return FALSE; + + ctx->seq -= (ctx->last_seq - ctx->first_seq) + 1; + ctx->rec = ibox->index->lookup(ibox->index, ctx->seq); + + ctx->first_seq = 0; + } - if (!index_storage_sync_and_lock(ibox, FALSE, TRUE, - MAIL_LOCK_EXCLUSIVE)) - return FALSE; + if (ctx->first_seq == 0) { + ctx->first_seq = ctx->seq; + ctx->first_rec = ctx->rec; + } + ctx->last_seq = ctx->seq; + ctx->last_rec = ctx->rec; - /* modifylog must be marked synced before expunging - anything new */ - if (!index_storage_sync_modifylog(ibox, TRUE)) - failed = TRUE; - else - failed = !ibox->expunge_locked(ibox, notify); + ibox->fetch_mail.data.rec = NULL; - if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK)) - return FALSE; + ibox->synced_messages_count--; + if (notify && seq <= ibox->synced_messages_count+1) { + ibox->box.storage->callbacks-> + expunge(&ibox->box, seq, + ibox->box.storage->callback_context); + } - return !failed; + return TRUE; } diff --git a/src/lib-storage/index/index-mail.c b/src/lib-storage/index/index-mail.c index faf36a3301..e4b2452696 100644 --- a/src/lib-storage/index/index-mail.c +++ b/src/lib-storage/index/index-mail.c @@ -12,6 +12,7 @@ #include "mail-index-util.h" #include "mail-custom-flags.h" #include "index-storage.h" +#include "index-expunge.h" #include "index-mail.h" #include @@ -650,7 +651,8 @@ static struct mail index_mail = { get_stream, get_special, index_storage_update_flags, - index_storage_copy + index_storage_copy, + index_storage_expunge }; void index_mail_init(struct index_mailbox *ibox, struct index_mail *mail, @@ -664,6 +666,7 @@ void index_mail_init(struct index_mailbox *ibox, struct index_mail *mail, mail->ibox = ibox; mail->wanted_fields = wanted_fields; mail->wanted_headers = wanted_headers; + mail->expunge_counter = ibox->index->expunge_counter; if (ibox->mail_init != NULL) ibox->mail_init(mail); @@ -675,6 +678,8 @@ int index_mail_next(struct index_mail *mail, struct mail_index_record *rec, struct index_mail_data *data = &mail->data; int ret, open_mail, parse_header, envelope_headers; + i_assert(mail->expunge_counter == mail->ibox->index->expunge_counter); + t_push(); /* close the old one */ diff --git a/src/lib-storage/index/index-mail.h b/src/lib-storage/index/index-mail.h index 609cdbc2b4..2396b2b31e 100644 --- a/src/lib-storage/index/index-mail.h +++ b/src/lib-storage/index/index-mail.h @@ -1,6 +1,10 @@ #ifndef __INDEX_MAIL_H #define __INDEX_MAIL_H +#include "message-size.h" + +struct message_header_line; + struct cached_header { struct cached_header *next; @@ -40,6 +44,7 @@ struct index_mail { pool_t pool; struct index_mailbox *ibox; + unsigned int expunge_counter; buffer_t *header_buf; enum mail_fetch_field wanted_fields; diff --git a/src/lib-storage/index/index-storage.c b/src/lib-storage/index/index-storage.c index efd4c00022..0bd23fe9fa 100644 --- a/src/lib-storage/index/index-storage.c +++ b/src/lib-storage/index/index-storage.c @@ -288,7 +288,7 @@ int index_storage_lock(struct index_mailbox *ibox, if (ibox->lock_type != MAILBOX_LOCK_UNLOCK) return TRUE; } else { - if (ibox->index->lock_type == MAIL_LOCK_EXCLUSIVE) + if (ibox->lock_type == MAIL_LOCK_EXCLUSIVE) return TRUE; } diff --git a/src/lib-storage/index/index-storage.h b/src/lib-storage/index/index-storage.h index afb72670cd..be044734ee 100644 --- a/src/lib-storage/index/index-storage.h +++ b/src/lib-storage/index/index-storage.h @@ -17,7 +17,6 @@ struct index_mailbox { /* expunge messages marked as deleted, requires index to be exclusively locked */ - int (*expunge_locked)(struct index_mailbox *ibox, int notify); void (*mail_init)(struct index_mail *mail); struct mail_index *index; @@ -36,6 +35,7 @@ struct index_mailbox { enum mail_lock_notify_type last_notify_type; unsigned int sent_diskspace_warning:1; + unsigned int sent_readonly_flags_warning:1; }; int mail_storage_set_index_error(struct index_mailbox *ibox); @@ -69,14 +69,6 @@ int index_mailbox_fix_custom_flags(struct index_mailbox *ibox, unsigned int index_storage_get_recent_count(struct mail_index *index); -int index_expunge_seek_first(struct index_mailbox *ibox, unsigned int *seq, - struct mail_index_record **rec); -int index_expunge_mails(struct index_mailbox *ibox, - struct mail_index_record *first_rec, - struct mail_index_record *last_rec, - unsigned int first_seq, unsigned int last_seq, - int notify); - void index_mailbox_check_add(struct index_mailbox *ibox, const char *path); void index_mailbox_check_remove_all(struct index_mailbox *ibox); @@ -84,7 +76,6 @@ void index_mailbox_check_remove_all(struct index_mailbox *ibox); void index_storage_set_callbacks(struct mail_storage *storage, struct mail_storage_callbacks *callbacks, void *context); -int index_storage_expunge(struct mailbox *box, int notify); int index_storage_get_status(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status); diff --git a/src/lib-storage/index/index-update-flags.c b/src/lib-storage/index/index-update-flags.c index 34d5c1a544..87aacc1ab4 100644 --- a/src/lib-storage/index/index-update-flags.c +++ b/src/lib-storage/index/index-update-flags.c @@ -15,6 +15,10 @@ int index_storage_update_flags(struct mail *mail, enum mail_flags modify_flags, new_flags; if (mail->box->readonly) { + if (ibox->sent_readonly_flags_warning) + return TRUE; + ibox->sent_readonly_flags_warning = TRUE; + storage->callbacks-> notify_no(&ibox->box, "Mailbox is read-only, ignoring flag changes", diff --git a/src/lib-storage/index/maildir/maildir-expunge.c b/src/lib-storage/index/maildir/maildir-expunge.c index b28f0f471e..f9a8699a28 100644 --- a/src/lib-storage/index/maildir/maildir-expunge.c +++ b/src/lib-storage/index/maildir/maildir-expunge.c @@ -1,67 +1,83 @@ -/* Copyright (C) 2002 Timo Sirainen */ +/* Copyright (C) 2002-2003 Timo Sirainen */ #include "lib.h" +#include "index-expunge.h" #include "maildir-index.h" #include "maildir-storage.h" -int maildir_expunge_locked(struct index_mailbox *ibox, int notify) +struct maildir_expunge_context { + struct mail_expunge_context *ctx; + int sent_access_warning; +}; + +struct mail_expunge_context * +maildir_storage_expunge_init(struct mailbox *box, + enum mail_fetch_field wanted_fields, + int expunge_all) +{ + struct maildir_expunge_context *ctx; + struct mail_expunge_context *mctx; + + mctx = index_storage_expunge_init(box, wanted_fields, expunge_all); + if (mctx == NULL) + return NULL; + + ctx = i_new(struct maildir_expunge_context, 1); + ctx->ctx = mctx; + return (struct mail_expunge_context *) ctx; +} + +int maildir_storage_expunge_deinit(struct mail_expunge_context *_ctx) +{ + struct maildir_expunge_context *ctx = + (struct maildir_expunge_context *) _ctx; + struct mail_expunge_context *mctx; + + mctx = ctx->ctx; + i_free(ctx); + return index_storage_expunge_deinit(mctx); +} + +struct mail * +maildir_storage_expunge_fetch_next(struct mail_expunge_context *_ctx) +{ + struct maildir_expunge_context *ctx = + (struct maildir_expunge_context *) _ctx; + + return index_storage_expunge_fetch_next(ctx->ctx); +} + +int maildir_storage_expunge(struct mail *mail, + struct mail_expunge_context *_ctx, + unsigned int *seq_r, int notify) { - struct mail_index_record *rec, *first_rec, *last_rec; - unsigned int seq, first_seq, last_seq; - int ret, no_permission = FALSE; - - if (!index_expunge_seek_first(ibox, &seq, &rec)) - return FALSE; - - first_rec = last_rec = NULL; - first_seq = last_seq = 0; - while (rec != NULL) { - if ((rec->msg_flags & MAIL_DELETED) == 0) - ret = FALSE; - else { - t_push(); - ret = maildir_expunge_mail(ibox->index, rec); - t_pop(); - - if (!ret) { - if (errno != EACCES) - return FALSE; - no_permission = TRUE; - } else { - if (first_rec == NULL) { - first_rec = rec; - first_seq = seq; - } - last_rec = rec; - last_seq = seq; - } - } - - if (!ret && first_rec != NULL) { - if (!index_expunge_mails(ibox, first_rec, last_rec, - first_seq, last_seq, notify)) - return FALSE; - first_rec = NULL; - - seq = first_seq; - rec = ibox->index->lookup(ibox->index, seq); - } else { - seq++; - rec = ibox->index->next(ibox->index, rec); - } + struct maildir_expunge_context *ctx = + (struct maildir_expunge_context *) _ctx; + struct index_mail *imail = (struct index_mail *) mail; + int ret; + + if (mail->box->readonly) { + /* send warning */ + return index_storage_expunge(mail, ctx->ctx, seq_r, notify); } - if (first_rec != NULL) { - if (!index_expunge_mails(ibox, first_rec, last_rec, - first_seq, last_seq, notify)) + t_push(); + ret = maildir_expunge_mail(imail->ibox->index, imail->data.rec); + t_pop(); + + if (!ret) { + if (errno != EACCES) return FALSE; - } - if (no_permission) { - ibox->box.storage->callbacks->notify_no(&ibox->box, + if (ctx->sent_access_warning) + return TRUE; + ctx->sent_access_warning = TRUE; + + mail->box->storage->callbacks->notify_no(mail->box, "We didn't have permission to expunge all the mails", - ibox->box.storage->callback_context); + mail->box->storage->callback_context); + return TRUE; } - return TRUE; + return index_storage_expunge(mail, ctx->ctx, seq_r, notify); } diff --git a/src/lib-storage/index/maildir/maildir-storage.c b/src/lib-storage/index/maildir/maildir-storage.c index f075842935..054b654c88 100644 --- a/src/lib-storage/index/maildir/maildir-storage.c +++ b/src/lib-storage/index/maildir/maildir-storage.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Timo Sirainen */ +/* Copyright (C) 2002-2003 Timo Sirainen */ #include "lib.h" #include "home-expand.h" @@ -322,6 +322,7 @@ static int verify_inbox(struct mail_storage *storage) static void maildir_mail_init(struct index_mail *mail) { mail->mail.copy = maildir_storage_copy; + mail->mail.expunge = maildir_storage_expunge; } static struct mailbox * @@ -344,10 +345,8 @@ maildir_open(struct mail_storage *storage, const char *name, ibox = index_storage_mailbox_init(storage, &maildir_mailbox, index, name, flags); - if (ibox != NULL) { - ibox->expunge_locked = maildir_expunge_locked; + if (ibox != NULL) ibox->mail_init = maildir_mail_init; - } return (struct mailbox *) ibox; } @@ -770,7 +769,6 @@ struct mailbox maildir_mailbox = { index_storage_get_status, index_storage_sync, maildir_storage_auto_sync, - index_storage_expunge, index_storage_fetch_init, index_storage_fetch_deinit, index_storage_fetch_next, @@ -785,6 +783,9 @@ struct mailbox maildir_mailbox = { maildir_storage_save_next, maildir_storage_copy_init, maildir_storage_copy_deinit, + maildir_storage_expunge_init, + maildir_storage_expunge_deinit, + maildir_storage_expunge_fetch_next, mail_storage_is_inconsistency_error, FALSE, diff --git a/src/lib-storage/index/maildir/maildir-storage.h b/src/lib-storage/index/maildir/maildir-storage.h index 04beaebe0e..9129333208 100644 --- a/src/lib-storage/index/maildir/maildir-storage.h +++ b/src/lib-storage/index/maildir/maildir-storage.h @@ -23,7 +23,15 @@ int maildir_list_mailbox_deinit(struct mailbox_list_context *ctx); struct mailbox_list * maildir_list_mailbox_next(struct mailbox_list_context *ctx); -int maildir_expunge_locked(struct index_mailbox *ibox, int notify); +struct mail_expunge_context * +maildir_storage_expunge_init(struct mailbox *box, + enum mail_fetch_field wanted_fields, + int expunge_all); +int maildir_storage_expunge_deinit(struct mail_expunge_context *ctx); +struct mail * +maildir_storage_expunge_fetch_next(struct mail_expunge_context *ctx); +int maildir_storage_expunge(struct mail *mail, struct mail_expunge_context *ctx, + unsigned int *seq_r, int notify); const char *maildir_get_path(struct mail_storage *storage, const char *name); diff --git a/src/lib-storage/index/mbox/mbox-expunge.c b/src/lib-storage/index/mbox/mbox-expunge.c index 5820d0298e..2013e9e476 100644 --- a/src/lib-storage/index/mbox/mbox-expunge.c +++ b/src/lib-storage/index/mbox/mbox-expunge.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Timo Sirainen */ +/* Copyright (C) 2002-2003 Timo Sirainen */ #include "lib.h" #include "istream.h" @@ -6,179 +6,204 @@ #include "mbox-index.h" #include "mbox-storage.h" #include "mbox-lock.h" +#include "index-expunge.h" #include #include -static int expunge_real(struct index_mailbox *ibox, - struct mail_index_record *rec, unsigned int seq, - struct istream *input, struct ostream *output, - int notify) -{ - struct mail_index_record *first_rec, *last_rec; - uoff_t offset, hdr_size, body_size; - uoff_t end_offset, from_offset, copy_size, old_limit; - const unsigned char *data; - size_t size; - unsigned int first_seq, last_seq; - int expunges, skip_next, deleted, failed; +struct mbox_expunge_context { + struct mail_expunge_context *ctx; - if (seq == 1) - end_offset = 0; - else { - /* we need to find offset to beginning of From-line. - not the fastest way maybe, but easiest.. */ - rec = ibox->index->lookup(ibox->index, seq-1); - - if (!mbox_mail_get_location(ibox->index, rec, &offset, - &hdr_size, &body_size)) - return FALSE; - end_offset = offset + hdr_size + body_size; + struct index_mailbox *ibox; + struct istream *input; + struct ostream *output; + int failed, expunges; - /* get back to the deleted record */ - rec = ibox->index->next(ibox->index, rec); - } + uoff_t from_offset, move_offset; +}; - old_limit = input->v_limit; +struct mail_expunge_context * +mbox_storage_expunge_init(struct mailbox *box, + enum mail_fetch_field wanted_fields, int expunge_all) +{ + struct index_mailbox *ibox = (struct index_mailbox *) box; + struct mbox_expunge_context *ctx; + struct mail_expunge_context *mctx; + struct istream *input; - first_rec = last_rec = NULL; - first_seq = last_seq = 0; + mctx = index_storage_expunge_init(box, wanted_fields, expunge_all); + if (mctx == NULL) + return NULL; - expunges = FALSE; skip_next = FALSE; - while (rec != NULL) { - if (!mbox_mail_get_location(ibox->index, rec, &offset, - &hdr_size, &body_size)) - return FALSE; + /* mbox must be already opened, synced and locked at this point. + we just want the istream. */ + input = mbox_get_stream(ibox->index, 0, MAIL_LOCK_EXCLUSIVE); + if (input == NULL) + return NULL; - from_offset = end_offset; - end_offset = offset + hdr_size + body_size; + i_assert(ibox->index->mbox_sync_counter == + ibox->index->mbox_lock_counter); - deleted = (rec->msg_flags & MAIL_DELETED) != 0; - if (deleted) { - if (first_rec == NULL) { - first_rec = rec; - first_seq = seq; - } - last_rec = rec; - last_seq = seq; - - if (!expunges) { - /* first expunged record, seek to position - where we want to begin writing */ - if (o_stream_seek(output, from_offset) < 0) - return FALSE; - expunges = TRUE; - } - } else if (first_rec != NULL) { - if (!index_expunge_mails(ibox, first_rec, last_rec, - first_seq, last_seq, notify)) - return FALSE; - first_rec = NULL; - - rec = ibox->index->lookup(ibox->index, first_seq); - seq = first_seq; - skip_next = TRUE; - } + ctx = i_new(struct mbox_expunge_context, 1); + ctx->ctx = mctx; + ctx->ibox = ibox; + ctx->input = input; + ctx->output = o_stream_create_file(ibox->index->mbox_fd, default_pool, + 4096, FALSE); + ctx->from_offset = (uoff_t)-1; + ctx->move_offset = (uoff_t)-1; + o_stream_set_blocking(ctx->output, 60000, NULL, NULL); + return (struct mail_expunge_context *) ctx; +} - if (skip_next) - skip_next = FALSE; - else { - rec = ibox->index->next(ibox->index, rec); - seq++; - } +static int mbox_move_data(struct mbox_expunge_context *ctx) +{ + const unsigned char *data; + size_t size; + uoff_t old_limit; + int failed; - if (expunges && !deleted) { - /* seek to wanted input position, and copy - this messages */ - i_assert(input->v_offset <= from_offset); - i_stream_skip(input, from_offset - input->v_offset); - - if (output->offset == 0) { - /* we're writing to beginning of mbox, so we - don't want the [\r]\n there */ - (void)i_stream_read_data(input, &data, - &size, 1); - if (size > 0 && data[0] == '\n') - i_stream_skip(input, 1); - else if (size > 1 && data[0] == '\r' && - data[1] == '\n') - i_stream_skip(input, 2); - } + i_assert(ctx->input->v_offset <= ctx->move_offset); + i_stream_skip(ctx->input, ctx->move_offset - ctx->input->v_offset); + + if (ctx->output->offset == 0) { + /* we're writing to beginning of mbox, so we + don't want the [\r]\n there */ + (void)i_stream_read_data(ctx->input, &data, &size, 1); + if (size > 0 && data[0] == '\n') + i_stream_skip(ctx->input, 1); + else if (size > 1 && data[0] == '\r' && + data[1] == '\n') + i_stream_skip(ctx->input, 2); + } + + old_limit = ctx->input->v_limit; + i_stream_set_read_limit(ctx->input, ctx->from_offset); + failed = o_stream_send_istream(ctx->output, ctx->input) < 0; + i_stream_set_read_limit(ctx->input, old_limit); - i_stream_set_read_limit(input, end_offset); - failed = o_stream_send_istream(output, input) < 0; - i_stream_set_read_limit(input, old_limit); + if (failed || ctx->input->v_offset != ctx->from_offset) + return FALSE; + return TRUE; +} - if (failed || input->v_offset != end_offset) - return FALSE; +int mbox_storage_expunge_deinit(struct mail_expunge_context *_ctx) +{ + struct mbox_expunge_context *ctx = (struct mbox_expunge_context *) _ctx; + int failed = ctx->failed; + + if (ctx->expunges) { + if (!failed && ctx->move_offset != (uoff_t)-1) { + ctx->from_offset = ctx->input->v_limit; + if (!mbox_move_data(ctx)) + failed = TRUE; + } else if (failed && ctx->output->offset > 0) { + /* we moved some of the data. move the rest as well + so there won't be invalid holes in mbox file */ + (void)o_stream_send_istream(ctx->output, ctx->input); } - } - if (first_rec != NULL) { - if (!index_expunge_mails(ibox, first_rec, last_rec, - first_seq, last_seq, notify)) - return FALSE; + if (ftruncate(ctx->ibox->index->mbox_fd, + (off_t)ctx->output->offset) < 0) { + mail_storage_set_error(ctx->ibox->box.storage, + "ftruncate() failed for mbox file %s: %m", + ctx->ibox->index->mailbox_path); + failed = TRUE; + } } - i_stream_skip(input, end_offset - input->v_offset); - - /* copy the rest as well, should be only \n but someone might - as well just appended more data.. but if we've deleted all mail, - don't write the only \n there. */ - copy_size = input->v_size - input->v_offset; - if (output->offset == 0 && copy_size == 1) - return TRUE; + if (!index_storage_expunge_deinit(ctx->ctx)) + failed = TRUE; - return o_stream_send_istream(output, input) >= 0; + o_stream_unref(ctx->output); + i_free(ctx); + return !failed; } -int mbox_expunge_locked(struct index_mailbox *ibox, int notify) +static int get_from_offset(struct mail_index *index, + struct mail_index_record *rec, uoff_t *offset_r) { - struct mail_index_record *rec; - struct istream *input; - struct ostream *output; - unsigned int seq; - int failed; + uoff_t offset, hdr_size, body_size; - if (!index_expunge_seek_first(ibox, &seq, &rec)) + if (!mbox_mail_get_location(index, rec, &offset, + &hdr_size, &body_size)) return FALSE; - if (rec == NULL) { - /* no deleted messages */ - return TRUE; + *offset_r = offset + hdr_size + body_size; + return TRUE; +} + +struct mail *mbox_storage_expunge_fetch_next(struct mail_expunge_context *_ctx) +{ + struct mbox_expunge_context *ctx = + (struct mbox_expunge_context *) _ctx; + struct mail_expunge_context *mctx = ctx->ctx; + struct mail_index *index = ctx->ibox->index; + + if (mctx->rec == NULL) + return NULL; + + if (mctx->fetch_next) { + mctx->fetch_next = FALSE; + do { + if (!get_from_offset(index, mctx->rec, + &ctx->from_offset)) { + ctx->failed = TRUE; + return NULL; + } + + mctx->seq++; + mctx->rec = index->next(index, mctx->rec); + if (mctx->rec == NULL) + return NULL; + } while ((mctx->rec->msg_flags & MAIL_DELETED) == 0 && + !mctx->expunge_all); } - /* mbox must be already opened, synced and locked at this point. - we just want the istream. */ - input = mbox_get_stream(ibox->index, 0, MAIL_LOCK_EXCLUSIVE); - if (input == NULL) - return FALSE; + return index_storage_expunge_fetch_next(ctx->ctx); +} - i_assert(ibox->index->mbox_sync_counter == - ibox->index->mbox_lock_counter); +static int get_prev_from_offset(struct mbox_expunge_context *ctx, + unsigned int seq) +{ + struct mail_index_record *rec; + + if (seq == 1) + ctx->from_offset = 0; + else { + rec = ctx->ibox->index->lookup(ctx->ibox->index, seq-1); + + if (!get_from_offset(ctx->ibox->index, rec, &ctx->from_offset)) + return FALSE; + } - t_push(); - output = o_stream_create_file(ibox->index->mbox_fd, data_stack_pool, - 4096, FALSE); - o_stream_set_blocking(output, 60000, NULL, NULL); + return TRUE; +} - failed = !expunge_real(ibox, rec, seq, input, output, notify); +int mbox_storage_expunge(struct mail *mail, struct mail_expunge_context *_ctx, + unsigned int *seq_r, int notify) +{ + struct mbox_expunge_context *ctx = (struct mbox_expunge_context *) _ctx; + struct index_mail *imail = (struct index_mail *) mail; - if (failed && output->offset > 0) { - /* we moved some of the data. move the rest as well so there - won't be invalid holes in mbox file */ - (void)o_stream_send_istream(output, input); + if (ctx->from_offset == (uoff_t)-1) { + if (!get_prev_from_offset(ctx, imail->data.idx_seq)) + return FALSE; } - if (ftruncate(ibox->index->mbox_fd, (off_t)output->offset) < 0) { - mail_storage_set_error(ibox->box.storage, "ftruncate() failed " - "for mbox file %s: %m", - ibox->index->mailbox_path); - failed = TRUE; + if (!ctx->expunges) { + /* first expunged message */ + if (o_stream_seek(ctx->output, ctx->from_offset) < 0) + return FALSE; + ctx->expunges = TRUE; + } else if (ctx->move_offset != ctx->from_offset) { + if (!mbox_move_data(ctx)) + return FALSE; } - o_stream_unref(output); - t_pop(); + if (!get_from_offset(ctx->ibox->index, imail->data.rec, + &ctx->move_offset)) + return FALSE; - return !failed; + return index_storage_expunge(mail, ctx->ctx, seq_r, notify); } diff --git a/src/lib-storage/index/mbox/mbox-storage.c b/src/lib-storage/index/mbox/mbox-storage.c index fca4f2e7e4..c540857ae6 100644 --- a/src/lib-storage/index/mbox/mbox-storage.c +++ b/src/lib-storage/index/mbox/mbox-storage.c @@ -319,6 +319,11 @@ static const char *mbox_get_path(struct mail_storage *storage, const char *name) return t_strconcat(storage->dir, "/", name, NULL); } +static void mbox_mail_init(struct index_mail *mail) +{ + mail->mail.expunge = mbox_storage_expunge; +} + static struct mailbox *mbox_open(struct mail_storage *storage, const char *name, enum mailbox_open_flags flags) { @@ -349,7 +354,7 @@ static struct mailbox *mbox_open(struct mail_storage *storage, const char *name, ibox = index_storage_mailbox_init(storage, &mbox_mailbox, index, name, flags); if (ibox != NULL) - ibox->expunge_locked = mbox_expunge_locked; + ibox->mail_init = mbox_mail_init; return (struct mailbox *) ibox; } @@ -780,7 +785,6 @@ struct mailbox mbox_mailbox = { index_storage_get_status, index_storage_sync, mbox_storage_auto_sync, - index_storage_expunge, index_storage_fetch_init, index_storage_fetch_deinit, index_storage_fetch_next, @@ -795,6 +799,9 @@ struct mailbox mbox_mailbox = { mbox_storage_save_next, index_storage_copy_init, index_storage_copy_deinit, + mbox_storage_expunge_init, + mbox_storage_expunge_deinit, + mbox_storage_expunge_fetch_next, mail_storage_is_inconsistency_error, FALSE, diff --git a/src/lib-storage/index/mbox/mbox-storage.h b/src/lib-storage/index/mbox/mbox-storage.h index 427c6282fa..7580dbeaf1 100644 --- a/src/lib-storage/index/mbox/mbox-storage.h +++ b/src/lib-storage/index/mbox/mbox-storage.h @@ -20,7 +20,13 @@ mbox_list_mailbox_init(struct mail_storage *storage, const char *mask, int mbox_list_mailbox_deinit(struct mailbox_list_context *ctx); struct mailbox_list *mbox_list_mailbox_next(struct mailbox_list_context *ctx); -int mbox_expunge_locked(struct index_mailbox *ibox, int notify); +struct mail_expunge_context * +mbox_storage_expunge_init(struct mailbox *box, + enum mail_fetch_field wanted_fields, int expunge_all); +int mbox_storage_expunge_deinit(struct mail_expunge_context *ctx); +struct mail *mbox_storage_expunge_fetch_next(struct mail_expunge_context *ctx); +int mbox_storage_expunge(struct mail *mail, struct mail_expunge_context *ctx, + unsigned int *seq_r, int notify); int mbox_is_valid_mask(const char *mask); diff --git a/src/lib-storage/mail-storage.h b/src/lib-storage/mail-storage.h index 91e70b22f1..9d442f87d8 100644 --- a/src/lib-storage/mail-storage.h +++ b/src/lib-storage/mail-storage.h @@ -235,10 +235,10 @@ struct mailbox { /* Explicitly lock the mailbox. If not used, all the methods below use the minimum locking requirements. This allows you to for - example use the expunge() and update_flags() methods in - struct mail. The mailbox stays locked until you unlock it. - Note that if you call a method which wants more locks than you've - given here, the call will fail (to avoid deadlocks). */ + example use the update_flags() method in struct mail. The mailbox + stays locked until you unlock it. Note that if you call a method + which wants more locks than you've given here, the call will fail + (to avoid deadlocks). */ int (*lock)(struct mailbox *box, enum mailbox_lock_type lock_type); /* Gets the mailbox status information. */ @@ -253,10 +253,6 @@ struct mailbox { void (*auto_sync)(struct mailbox *box, enum mailbox_sync_type sync_type, unsigned int min_newmail_notify_interval); - /* Expunge all mails with \Deleted flag. If notify is TRUE, call - expunge callbacks. Also always does full syncing. */ - int (*expunge)(struct mailbox *box, int notify); - /* Initialize new fetch request. wanted_fields isn't required, but it can be used for optimizations. update_flags must be set to TRUE, if you want to call mail->update_flags() */ @@ -324,11 +320,22 @@ struct mailbox { /* Initialize copying operation to this mailbox. The actual copying can be done by fetching or searching mails and calling mail's - expunge() method. */ + copy() method. */ struct mail_copy_context *(*copy_init)(struct mailbox *box); /* Finish copying. */ int (*copy_deinit)(struct mail_copy_context *ctx, int rollback); + /* Initialize expunging operation to this mailbox. If expunge_all + is TRUE, all messages are returned rather than just deleted. */ + struct mail_expunge_context * + (*expunge_init)(struct mailbox *box, + enum mail_fetch_field wanted_fields, + int expunge_all); + /* Finish expunging. */ + int (*expunge_deinit)(struct mail_expunge_context *ctx); + /* Fetch next mail. */ + struct mail *(*expunge_fetch_next)(struct mail_expunge_context *ctx); + /* Returns TRUE if mailbox is now in inconsistent state, meaning that the message IDs etc. may have changed - only way to recover this would be to fully close the mailbox and reopen it. With IMAP @@ -393,6 +400,21 @@ struct mail { /* Copy this message to another mailbox. */ int (*copy)(struct mail *mail, struct mail_copy_context *ctx); + + /* Expunge this message. Note that the actual message may or may not + be really expunged until expunge_deinit() is called. In any case, + after this call you must not try to access this mail, or any other + mail you've previously fetched. + + Since you can't be sure when the message is really expunged, you + can't be sure what it's sequence number is from client's point of + view. seq_r is set to that sequence number. + + This call is allowed only for mails fetched with + expunge_fetch_next(). Otherwise the sequence number updates would + get too tricky. */ + int (*expunge)(struct mail *mail, struct mail_expunge_context *ctx, + unsigned int *seq_r, int notify); }; struct mailbox_list { diff --git a/src/pop3/client.c b/src/pop3/client.c index 91968d3434..b30bf94b3f 100644 --- a/src/pop3/client.c +++ b/src/pop3/client.c @@ -63,7 +63,7 @@ static int init_mailbox(struct client *client) for (i = 0; i < 2; i++) { ctx = client->mailbox->fetch_init(client->mailbox, MAIL_FETCH_SIZE, - NULL, messageset, FALSE); + messageset, FALSE); if (ctx == NULL) { client_send_storage_error(client); return FALSE; diff --git a/src/pop3/commands.c b/src/pop3/commands.c index 1cfe0596ff..05e4c4b9de 100644 --- a/src/pop3/commands.c +++ b/src/pop3/commands.c @@ -139,63 +139,44 @@ static int cmd_noop(struct client *client, const char *args __attr_unused__) return TRUE; } -static int cmd_quit(struct client *client, const char *args __attr_unused__) +static int expunge_mails(struct client *client, struct mailbox *box) { - unsigned int first, last, msgnum, max, i, j; - struct mail_full_flags flags; - string_t *set; - - if (!client->deleted) { - client_send_line(client, "+OK Logging out."); - client_disconnect(client); - return TRUE; - } - - set = t_str_new(1024); - first = last = 0; msgnum = 1; - max = MSGS_BITMASK_SIZE(client); - for (i = 0; i < max; i++) { - if (client->deleted_bitmask[i] == 0) { - msgnum += CHAR_BIT; - continue; - } + struct mail_expunge_context *ctx; + struct mail *mail; + unsigned int i, j; + int failed = FALSE; + + /* NOTE: if there's any external expunges, they'll get synced here. + Currently we update only the deleted_bitmask[] so we don't end up + expunging wrong messages, but message_sizes[] isn't updated. */ + ctx = box->expunge_init(box, 0, TRUE); + if (ctx == NULL) + return FALSE; - for (j = 0; j < CHAR_BIT; j++, msgnum++) { - if ((client->deleted_bitmask[i] & (1 << j)) == 0) - continue; - - if (last == msgnum-1 && last != 0) - last++; - else { - if (first != 0) { - if (first == last) - str_printfa(set, ",%u", first); - else { - str_printfa(set, ",%u:%u", - first, last); - } - } - first = last = msgnum; + i = j = 0; + while ((mail = box->expunge_fetch_next(ctx)) != NULL) { + if ((client->deleted_bitmask[i] & (1 << j)) != 0) { + if (!mail->expunge(mail, ctx, NULL, FALSE)) { + failed = TRUE; + break; } } + if (++j == CHAR_BIT) { + j = 0; i++; + } } - if (first != 0) { - if (first == last) - str_printfa(set, ",%u", first); - else - str_printfa(set, ",%u:%u", first, last); - } + if (!box->expunge_deinit(ctx)) + return FALSE; - memset(&flags, 0, sizeof(flags)); - flags.flags = MAIL_DELETED; + return !failed; +} - if (str_len(set) == 0) +static int cmd_quit(struct client *client, const char *args __attr_unused__) +{ + if (!client->deleted) client_send_line(client, "+OK Logging out."); - else if (client->mailbox->update_flags(client->mailbox, str_c(set)+1, - FALSE, &flags, MODIFY_ADD, - FALSE, NULL) && - client->mailbox->expunge(client->mailbox, FALSE)) + else if (expunge_mails(client, client->mailbox)) client_send_line(client, "+OK Logging out, messages deleted."); else client_send_storage_error(client); @@ -281,8 +262,7 @@ static void fetch(struct client *client, unsigned int msgnum, ctx = client->mailbox->fetch_init(client->mailbox, MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, - NULL, dec2str(msgnum+1), - FALSE); + dec2str(msgnum+1), FALSE); if (ctx == NULL) { client_send_storage_error(client); return; @@ -366,7 +346,7 @@ static void list_uids(struct client *client, unsigned int message) t_strdup_printf("%u", message); ctx = client->mailbox->fetch_init(client->mailbox, 0, - NULL, messageset, FALSE); + messageset, FALSE); if (ctx == NULL) { client_send_storage_error(client); return; diff --git a/src/pop3/mail-storage-callbacks.c b/src/pop3/mail-storage-callbacks.c index 1a2b164521..6f8b0f1a6f 100644 --- a/src/pop3/mail-storage-callbacks.c +++ b/src/pop3/mail-storage-callbacks.c @@ -22,17 +22,40 @@ static void notify_no(struct mailbox *mailbox __attr_unused__, } static void expunge(struct mailbox *mailbox __attr_unused__, - unsigned int seq __attr_unused__, - void *context __attr_unused__) + unsigned int seq, void *context) { + struct client *client = context; + unsigned char *mask = client->deleted_bitmask; + unsigned int max, i, j; + + /* external deletes - we have to fix our internal deleted array. + this should happen only when we're doing the expunging at quit. */ + seq--; + client->messages_count--; + + if (!client->deleted) + return; + + max = client->messages_count / CHAR_BIT; + i = seq / CHAR_BIT; j = seq % CHAR_BIT; + mask[i] = (mask[i] & ((1 << j) - 1)) | + ((mask[i] >> (j+1)) << j) | + (i == max ? 0 : ((mask[i+1] & 1) << (CHAR_BIT-1))); + + if (i != max) { + for (i++; i < max-1; i++) { + mask[i] = (mask[i] >> 1) | + ((mask[i+1] & 1) << (CHAR_BIT-1)); + } + + mask[i] >>= 1; + } } static void update_flags(struct mailbox *mailbox __attr_unused__, unsigned int seq __attr_unused__, unsigned int uid __attr_unused__, - enum mail_flags flags __attr_unused__, - const char *custom_flags[] __attr_unused__, - unsigned int custom_flags_count __attr_unused__, + const struct mail_full_flags *flags __attr_unused__, void *context __attr_unused__) { }