enough.
--HG--
branch : HEAD
client.c \
commands.c \
commands-util.c \
+ imap-expunge.c \
imap-fetch.c \
imap-fetch-body-section.c \
imap-search.c \
commands.h \
commands-util.h \
common.h \
+ imap-expunge.h \
imap-fetch.h \
imap-search.h \
imap-sort.h \
#include "common.h"
#include "commands.h"
+#include "imap-expunge.h"
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);
}
#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);
}
}
-#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 > \
i_assert(first_seq != 0);
i_assert(first_seq <= last_seq);
+ index->expunge_counter++;
+
first_uid = first_rec->uid;
last_uid = last_rec->uid;
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;
}
/* 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;
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_. */
-/* 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;
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;
}
#include "mail-index-util.h"
#include "mail-custom-flags.h"
#include "index-storage.h"
+#include "index-expunge.h"
#include "index-mail.h"
#include <ctype.h>
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,
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);
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 */
#ifndef __INDEX_MAIL_H
#define __INDEX_MAIL_H
+#include "message-size.h"
+
+struct message_header_line;
+
struct cached_header {
struct cached_header *next;
pool_t pool;
struct index_mailbox *ibox;
+ unsigned int expunge_counter;
buffer_t *header_buf;
enum mail_fetch_field wanted_fields;
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;
}
/* 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;
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);
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);
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);
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",
-/* 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);
}
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2003 Timo Sirainen */
#include "lib.h"
#include "home-expand.h"
static void maildir_mail_init(struct index_mail *mail)
{
mail->mail.copy = maildir_storage_copy;
+ mail->mail.expunge = maildir_storage_expunge;
}
static struct mailbox *
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;
}
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,
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,
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);
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2003 Timo Sirainen */
#include "lib.h"
#include "istream.h"
#include "mbox-index.h"
#include "mbox-storage.h"
#include "mbox-lock.h"
+#include "index-expunge.h"
#include <fcntl.h>
#include <unistd.h>
-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);
}
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)
{
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;
}
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,
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,
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);
/* 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. */
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() */
/* 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
/* 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 {
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;
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);
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;
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;
}
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__)
{
}