Mercurial > dovecot > core-2.2
changeset 1637:a64391b49589 HEAD
API change for copying messages.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 23 Jul 2003 03:40:49 +0300 |
parents | 20212920c795 |
children | e95c0e462591 |
files | src/imap/cmd-copy.c src/lib-storage/index/index-copy.c src/lib-storage/index/index-fetch.c src/lib-storage/index/index-mail.c src/lib-storage/index/index-storage.h src/lib-storage/index/maildir/maildir-copy.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/maildir/maildir-storage.h src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/mail-storage.h |
diffstat | 10 files changed, 231 insertions(+), 206 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap/cmd-copy.c Wed Jul 23 03:29:32 2003 +0300 +++ b/src/imap/cmd-copy.c Wed Jul 23 03:40:49 2003 +0300 @@ -3,11 +3,39 @@ #include "common.h" #include "commands.h" +static int fetch_and_copy(struct mail_copy_context *copy_ctx, + struct mailbox *box, const char *messageset, + int uidset, int *all_found) +{ + struct mail_fetch_context *fetch_ctx; + struct mail *mail; + int failed = FALSE; + + fetch_ctx = box->fetch_init(box, MAIL_FETCH_STREAM_HEADER | + MAIL_FETCH_STREAM_BODY, NULL, + messageset, uidset); + if (fetch_ctx == NULL) + return FALSE; + + while ((mail = box->fetch_next(fetch_ctx)) != NULL) { + if (!mail->copy(mail, copy_ctx)) { + failed = TRUE; + break; + } + } + + if (!box->fetch_deinit(fetch_ctx, all_found)) + return FALSE; + + return !failed; +} + int cmd_copy(struct client *client) { struct mailbox *destbox; + struct mail_copy_context *copy_ctx; const char *messageset, *mailbox; - int ret; + int failed = FALSE, all_found = TRUE; /* <message set> <mailbox> */ if (!client_read_string_args(client, 2, &messageset, &mailbox)) @@ -28,17 +56,31 @@ return TRUE; } - /* copy the mail */ - ret = client->mailbox->copy(client->mailbox, destbox, - messageset, client->cmd_uid); + /* FIXME: copying from mailbox to itself is kind of kludgy here. + currently it works simply because copy_init() will lock mbox + exclusively and fetching wont drop it. */ + copy_ctx = destbox->copy_init(destbox); + if (copy_ctx == NULL) + failed = TRUE; + else { + if (!fetch_and_copy(copy_ctx, client->mailbox, + messageset, client->cmd_uid, &all_found)) + failed = TRUE; - if (ret) { + if (!destbox->copy_deinit(copy_ctx, failed || !all_found)) + failed = TRUE; + } + + if (failed) + client_send_storage_error(client); + else if (!all_found) { + /* some messages were expunged, sync them */ + client_sync_full(client); + client_send_tagline(client, + "NO Some of the requested messages no longer exist."); + } else { client_sync_full_fast(client); client_send_tagline(client, "OK Copy completed."); - } else { - /* if COPY fails because of expunges they'll get synced here */ - client_sync_full(client); - client_send_storage_error(client); } destbox->close(destbox);
--- a/src/lib-storage/index/index-copy.c Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/index/index-copy.c Wed Jul 23 03:40:49 2003 +0300 @@ -8,90 +8,52 @@ #include <unistd.h> -static int copy_messageset(struct messageset_context *msgset_ctx, - struct mail_save_context *save_ctx, - struct index_mailbox *src, - struct mailbox *dest) +struct mail_copy_context { + struct mailbox *box; + struct mail_save_context *save_ctx; +}; + +struct mail_copy_context *index_storage_copy_init(struct mailbox *box) { - const struct messageset_mail *mail; - struct mail_full_flags flags; - struct istream *input; - time_t received_date; - int failed, deleted; - - memset(&flags, 0, sizeof(flags)); - flags.custom_flags = - mail_custom_flags_list_get(src->index->custom_flags); - flags.custom_flags_count = MAIL_CUSTOM_FLAGS_COUNT; + struct mail_copy_context *ctx; + struct mail_save_context *save_ctx; - while ((mail = index_messageset_next(msgset_ctx)) != NULL) { - input = src->index->open_mail(src->index, mail->rec, - &received_date, &deleted); - if (input == NULL) - return FALSE; + save_ctx = box->save_init(box, TRUE); + if (save_ctx == NULL) + return NULL; - flags.flags = mail->rec->msg_flags; - failed = !dest->save_next(save_ctx, &flags, received_date, - 0, input); - i_stream_unref(input); + ctx = i_new(struct mail_copy_context, 1); + ctx->box = box; + ctx->save_ctx = save_ctx; - if (failed) - return FALSE; - } - - return TRUE; + return ctx; } -int index_storage_copy(struct mailbox *box, struct mailbox *destbox, - const char *messageset, int uidset) +int index_storage_copy_deinit(struct mail_copy_context *ctx, int rollback) { - struct index_mailbox *ibox = (struct index_mailbox *) box; - struct messageset_context *msgset_ctx; - struct mail_save_context *save_ctx; - int ret, ret2, copy_inside_mailbox; + int ret; - if (destbox->readonly) { - mail_storage_set_error(box->storage, - "Destination mailbox is read-only"); - return FALSE; - } - - copy_inside_mailbox = - destbox->storage == box->storage && - strcmp(destbox->name, box->name) == 0; + ret = ctx->box->save_deinit(ctx->save_ctx, rollback); + i_free(ctx); + return ret; +} - if (copy_inside_mailbox) { - if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE)) - return FALSE; - } else { - if (!index_storage_sync_and_lock(ibox, TRUE, TRUE, - MAIL_LOCK_SHARED)) - return FALSE; - } +int index_storage_copy(struct mail *mail, struct mail_copy_context *ctx) +{ + struct index_mail *imail = (struct index_mail *) mail; + struct istream *input; + time_t received_date; + int ret, deleted; - save_ctx = destbox->save_init(destbox, TRUE); - if (save_ctx == NULL) - ret = FALSE; - else { - /* abort if any of the messages are expunged */ - msgset_ctx = index_messageset_init(ibox, messageset, uidset, - FALSE); - ret = copy_messageset(msgset_ctx, save_ctx, ibox, destbox); - ret2 = index_messageset_deinit(msgset_ctx); - if (ret2 < 0) - ret = FALSE; - else if (ret2 == 0) { - mail_storage_set_error(ibox->box.storage, - "Some of the requested messages no longer exist."); - ret = FALSE; - } + input = imail->ibox->index->open_mail(imail->ibox->index, + imail->data.rec, + &received_date, &deleted); + if (input == NULL) + return FALSE; - if (!destbox->save_deinit(save_ctx, !ret)) - ret = FALSE; - } - - if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK)) - ret = FALSE; + ret = ctx->box->save_next(ctx->save_ctx, mail->get_flags(mail), + received_date, 0, input); + i_stream_unref(input); return ret; }
--- a/src/lib-storage/index/index-fetch.c Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/index/index-fetch.c Wed Jul 23 03:40:49 2003 +0300 @@ -18,6 +18,7 @@ struct index_mail mail; int update_seen; + enum mail_lock_type old_lock; }; struct mail_fetch_context * @@ -30,6 +31,7 @@ int check_mail; ctx = i_new(struct mail_fetch_context, 1); + ctx->old_lock = ibox->index->lock_type; if (box->readonly && update_seen != NULL) *update_seen = FALSE; @@ -48,7 +50,8 @@ if (update_seen != NULL && *update_seen && ibox->index->header->messages_count == - ibox->index->header->seen_messages_count) { + ibox->index->header->seen_messages_count && + ctx->old_lock != MAIL_LOCK_EXCLUSIVE) { /* if all messages are already seen, there's no point in keeping exclusive lock */ *update_seen = FALSE; @@ -73,7 +76,7 @@ if (all_found != NULL) *all_found = ret > 0; - if (!index_storage_lock(ctx->ibox, MAIL_LOCK_UNLOCK)) + if (!index_storage_lock(ctx->ibox, ctx->old_lock)) ret = -1; if (ctx->ibox->fetch_mail.pool != NULL)
--- a/src/lib-storage/index/index-mail.c Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/index/index-mail.c Wed Jul 23 03:40:49 2003 +0300 @@ -637,7 +637,7 @@ } static struct mail index_mail = { - 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, get_flags, get_parts, @@ -648,7 +648,8 @@ get_address, get_first_mailbox, get_stream, - get_special + get_special, + index_storage_copy }; void index_mail_init(struct index_mailbox *ibox, struct index_mail *mail, @@ -656,11 +657,15 @@ const char *const wanted_headers[]) { mail->mail = index_mail; + mail->mail.box = &ibox->box; + mail->pool = pool_alloconly_create("index_mail", 4096); - mail->ibox = ibox; mail->wanted_fields = wanted_fields; mail->wanted_headers = wanted_headers; + + if (ibox->mail_init != NULL) + ibox->mail_init(mail); } int index_mail_next(struct index_mail *mail, struct mail_index_record *rec)
--- a/src/lib-storage/index/index-storage.h Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/index/index-storage.h Wed Jul 23 03:40:49 2003 +0300 @@ -18,6 +18,7 @@ /* 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; @@ -82,8 +83,6 @@ void index_storage_set_callbacks(struct mail_storage *storage, struct mail_storage_callbacks *callbacks, void *context); -int index_storage_copy(struct mailbox *box, struct mailbox *destbox, - const char *messageset, int uidset); int index_storage_expunge(struct mailbox *box, int notify); int index_storage_get_status(struct mailbox *box, enum mailbox_status_items items, @@ -117,4 +116,8 @@ int index_storage_search_deinit(struct mail_search_context *ctx); struct mail *index_storage_search_next(struct mail_search_context *ctx); +struct mail_copy_context *index_storage_copy_init(struct mailbox *box); +int index_storage_copy_deinit(struct mail_copy_context *ctx, int rollback); +int index_storage_copy(struct mail *mail, struct mail_copy_context *ctx); + #endif
--- a/src/lib-storage/index/maildir/maildir-copy.c Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/index/maildir/maildir-copy.c Wed Jul 23 03:40:49 2003 +0300 @@ -11,6 +11,16 @@ #include <stdlib.h> #include <unistd.h> +struct maildir_copy_context { + struct index_mailbox *ibox; + int hardlink; + + pool_t pool; + struct rollback *rollbacks; + + struct mail_copy_context *ctx; +}; + struct rollback { struct rollback *next; const char *fname; @@ -64,139 +74,122 @@ return 0; } -static int hardlink_messageset(struct messageset_context *ctx, - struct index_mailbox *src, - struct index_mailbox *dest) +static int maildir_copy_hardlink(struct mail *mail, + struct maildir_copy_context *ctx) { - struct mail_index *index = src->index; - pool_t pool; - struct rollback *rollbacks, *rb; - const struct messageset_mail *mail; - enum mail_flags flags; - const char **custom_flags; + struct index_mail *imail = (struct index_mail *) mail; + struct rollback *rb; const char *fname, *dest_fname, *dest_path; - int ret, i, found; + enum mail_flags flags; + int i, ret, found; + + flags = mail->get_flags(mail)->flags; - pool = pool_alloconly_create("hard copy rollbacks", 2048); - rollbacks = NULL; - - custom_flags = mail_custom_flags_list_get(index->custom_flags); + /* link the file */ + dest_fname = maildir_generate_tmp_filename(&ioloop_timeval); + dest_fname = maildir_filename_set_flags(dest_fname, flags); + dest_path = t_strconcat(ctx->ibox->index->mailbox_path, "/new/", + dest_fname, NULL); - ret = 1; - while (ret > 0 && (mail = index_messageset_next(ctx)) != NULL) { - flags = mail->rec->msg_flags; - if (!index_mailbox_fix_custom_flags(dest, &flags, - custom_flags, - MAIL_CUSTOM_FLAGS_COUNT)) { + for (i = 0;; i++) { + ret = maildir_hardlink_file(imail->ibox->index, imail->data.rec, + &fname, dest_path); + if (ret != 0) + break; + + if (i == 10) { + mail_storage_set_error(mail->box->storage, + "File name keeps changing, copy failed"); + break; + } + + if (!maildir_index_sync_readonly(imail->ibox->index, fname, + &found)) { ret = -1; break; } - /* link the file */ - t_push(); - - dest_fname = maildir_generate_tmp_filename(&ioloop_timeval); - dest_fname = maildir_filename_set_flags(dest_fname, flags); - dest_path = t_strconcat(dest->index->mailbox_path, "/new/", - dest_fname, NULL); + if (!found) + break; + } - for (i = 0;; i++) { - ret = maildir_hardlink_file(index, mail->rec, &fname, - dest_path); - if (ret > 0) { - rb = p_new(pool, struct rollback, 1); - rb->fname = p_strdup(pool, dest_fname); - rb->next = rollbacks; - rollbacks = rb; - break; - } - if (ret < 0) - break; + if (ret > 0) { + if (ctx->pool == NULL) { + ctx->pool = pool_alloconly_create("hard copy rollbacks", + 2048); + } - if (i == 10) { - mail_storage_set_error(src->box.storage, - "File name keeps changing, " - "copy failed"); - break; - } - - if (!maildir_index_sync_readonly(index, fname, &found)) - break; - - if (!found) - break; - } - t_pop(); + rb = p_new(ctx->pool, struct rollback, 1); + rb->fname = p_strdup(ctx->pool, dest_fname); + rb->next = ctx->rollbacks; + ctx->rollbacks = rb; } - if (ret <= 0) { - for (rb = rollbacks; rb != NULL; rb = rb->next) { + return ret; +} + +struct mail_copy_context *maildir_storage_copy_init(struct mailbox *box) +{ + struct index_mailbox *ibox = (struct index_mailbox *) box; + struct maildir_copy_context *ctx; + + if (box->readonly) { + mail_storage_set_error(box->storage, + "Destination mailbox is read-only"); + return NULL; + } + + ctx = i_new(struct maildir_copy_context, 1); + ctx->hardlink = getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL; + ctx->ibox = ibox; + return (struct mail_copy_context *) ctx; +} + +int maildir_storage_copy_deinit(struct mail_copy_context *_ctx, int rollback) +{ + struct maildir_copy_context *ctx = (struct maildir_copy_context *) _ctx; + struct rollback *rb; + int ret = TRUE; + + if (ctx->ctx != NULL) + ret = index_storage_copy_deinit(ctx->ctx, rollback); + + if (rollback) { + for (rb = ctx->rollbacks; rb != NULL; rb = rb->next) { t_push(); - (void)unlink(t_strconcat(dest->index->mailbox_path, - "new/", rb->fname, NULL)); + (void)unlink(t_strconcat(ctx->ibox->index->mailbox_path, + "/new/", rb->fname, NULL)); t_pop(); } } - pool_unref(pool); + if (ctx->pool != NULL) + pool_unref(ctx->pool); + + i_free(ctx); return ret; } -static int copy_with_hardlinks(struct index_mailbox *src, - struct index_mailbox *dest, - const char *messageset, int uidset) +int maildir_storage_copy(struct mail *mail, struct mail_copy_context *_ctx) { - struct messageset_context *ctx; - int exdev, ret, ret2; - - if (!src->index->set_lock(src->index, MAIL_LOCK_SHARED)) - return -1; + struct maildir_copy_context *ctx = (struct maildir_copy_context *) _ctx; + int ret; - ctx = index_messageset_init(src, messageset, uidset, FALSE); - ret = hardlink_messageset(ctx, src, dest); - exdev = ret < 0 && errno == EXDEV; - ret2 = index_messageset_deinit(ctx); + if (ctx->hardlink && mail->box->storage == ctx->ibox->box.storage) { + t_push(); + ret = maildir_copy_hardlink(mail, ctx); + t_pop(); - if (ret2 < 0) - ret = -1; + if (ret > 0) + return TRUE; + if (ret < 0) + return FALSE; - if (ret == 0 || ret2 == 0) { - mail_storage_set_error(src->box.storage, - "Some of the requested messages no longer exist."); - ret = -1; + /* non-fatal hardlinking failure, try the slow way */ } - (void)index_storage_lock(src, MAIL_LOCK_UNLOCK); - - return exdev ? 0 : ret; -} - -int maildir_storage_copy(struct mailbox *box, struct mailbox *destbox, - const char *messageset, int uidset) -{ - struct index_mailbox *ibox = (struct index_mailbox *) box; - - if (destbox->readonly) { - mail_storage_set_error(box->storage, - "Destination mailbox is read-only"); - return FALSE; - } + if (ctx->ctx == NULL) + ctx->ctx = index_storage_copy_init(&ctx->ibox->box); - if (getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL && - destbox->storage == box->storage) { - /* both source and destination mailbox are in maildirs and - copy_with_hardlinks option is on, do it */ - switch (copy_with_hardlinks(ibox, - (struct index_mailbox *) destbox, messageset, uidset)) { - case 1: - return TRUE; - case 0: - /* non-fatal hardlinking failure, try the slow way */ - break; - default: - return FALSE; - } - } - - return index_storage_copy(box, destbox, messageset, uidset); + return index_storage_copy(mail, ctx->ctx); }
--- a/src/lib-storage/index/maildir/maildir-storage.c Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/index/maildir/maildir-storage.c Wed Jul 23 03:40:49 2003 +0300 @@ -319,6 +319,11 @@ create_control_dir(storage, "INBOX"); } +static void maildir_mail_init(struct index_mail *mail) +{ + mail->mail.copy = maildir_storage_copy; +} + static struct mailbox * maildir_open(struct mail_storage *storage, const char *name, enum mailbox_open_flags flags) @@ -339,8 +344,10 @@ ibox = index_storage_mailbox_init(storage, &maildir_mailbox, index, name, flags); - if (ibox != NULL) + if (ibox != NULL) { ibox->expunge_locked = maildir_expunge_locked; + ibox->mail_init = maildir_mail_init; + } return (struct mailbox *) ibox; } @@ -739,7 +746,6 @@ maildir_storage_auto_sync, index_storage_expunge, index_storage_update_flags, - maildir_storage_copy, index_storage_fetch_init, index_storage_fetch_deinit, index_storage_fetch_next, @@ -752,6 +758,8 @@ maildir_storage_save_init, maildir_storage_save_deinit, maildir_storage_save_next, + maildir_storage_copy_init, + maildir_storage_copy_deinit, mail_storage_is_inconsistency_error, FALSE,
--- a/src/lib-storage/index/maildir/maildir-storage.h Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/index/maildir/maildir-storage.h Wed Jul 23 03:40:49 2003 +0300 @@ -3,8 +3,9 @@ #include "index-storage.h" -int maildir_storage_copy(struct mailbox *box, struct mailbox *destbox, - const char *messageset, int uidset); +struct mail_copy_context *maildir_storage_copy_init(struct mailbox *box); +int maildir_storage_copy_deinit(struct mail_copy_context *ctx, int rollback); +int maildir_storage_copy(struct mail *mail, struct mail_copy_context *ctx); struct mail_save_context * maildir_storage_save_init(struct mailbox *box, int transaction);
--- a/src/lib-storage/index/mbox/mbox-storage.c Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/index/mbox/mbox-storage.c Wed Jul 23 03:40:49 2003 +0300 @@ -748,7 +748,6 @@ mbox_storage_auto_sync, index_storage_expunge, index_storage_update_flags, - index_storage_copy, index_storage_fetch_init, index_storage_fetch_deinit, index_storage_fetch_next, @@ -761,6 +760,8 @@ mbox_storage_save_init, mbox_storage_save_deinit, mbox_storage_save_next, + index_storage_copy_init, + index_storage_copy_deinit, mail_storage_is_inconsistency_error, FALSE,
--- a/src/lib-storage/mail-storage.h Wed Jul 23 03:29:32 2003 +0300 +++ b/src/lib-storage/mail-storage.h Wed Jul 23 03:40:49 2003 +0300 @@ -255,10 +255,6 @@ enum modify_type modify_type, int notify, int *all_found); - /* Copy mails to another mailbox. */ - int (*copy)(struct mailbox *box, struct mailbox *destbox, - const char *messageset, int uidset); - /* Initialize new fetch request. wanted_fields isn't required, but it can be used for optimizations. If *update_seen is TRUE, \Seen flag is set for all fetched mails. *update_seen may be changed back to @@ -327,6 +323,13 @@ time_t received_date, int timezone_offset, struct istream *data); + /* Initialize copying operation to this mailbox. The actual copying + can be done by fetching or searching mails and calling mail's + expunge() method. */ + struct mail_copy_context *(*copy_init)(struct mailbox *box); + /* Finish copying. */ + int (*copy_deinit)(struct mail_copy_context *ctx, int rollback); + /* 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 @@ -343,6 +346,7 @@ struct mail { /* always set */ + struct mailbox *box; unsigned int seq; unsigned int uid; @@ -383,6 +387,9 @@ /* Get the any of the "special" fields. */ const char *(*get_special)(struct mail *mail, enum mail_fetch_field field); + + /* Copy this mail to another mailbox. */ + int (*copy)(struct mail *mail, struct mail_copy_context *ctx); }; struct mailbox_list {