Mercurial > dovecot > core-2.2
view src/imap/cmd-copy.c @ 22713:cb108f786fb4
Updated copyright notices to include the year 2018.
author | Stephan Bosch <stephan.bosch@dovecot.fi> |
---|---|
date | Mon, 01 Jan 2018 22:42:08 +0100 |
parents | 8bb7ebb65d0f |
children |
line wrap: on
line source
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "str.h" #include "ostream.h" #include "imap-resp-code.h" #include "imap-util.h" #include "imap-commands.h" #include "imap-search-args.h" #include <time.h> #define COPY_CHECK_INTERVAL 100 static void client_send_sendalive_if_needed(struct client *client) { time_t now, last_io; if (o_stream_get_buffer_used_size(client->output) != 0) return; now = time(NULL); last_io = I_MAX(client->last_input, client->last_output); if (now - last_io > MAIL_STORAGE_STAYALIVE_SECS) { o_stream_nsend_str(client->output, "* OK Hang in there..\r\n"); o_stream_nflush(client->output); client->last_output = now; } } static int fetch_and_copy(struct client_command_context *cmd, bool move, struct mailbox_transaction_context *t, struct mailbox_transaction_context **src_trans_r, struct mail_search_args *search_args, const char **src_uidset_r, unsigned int *copy_count_r) { struct client *client = cmd->client; struct mail_search_context *search_ctx; struct mailbox_transaction_context *src_trans; struct mail_save_context *save_ctx; struct mail *mail; unsigned int copy_count = 0; struct msgset_generator_context srcset_ctx; string_t *src_uidset; int ret; src_uidset = t_str_new(256); msgset_generator_init(&srcset_ctx, src_uidset); src_trans = mailbox_transaction_begin(client->mailbox, 0); imap_transaction_set_cmd_reason(src_trans, cmd); search_ctx = mailbox_search_init(src_trans, search_args, NULL, 0, NULL); ret = 1; while (mailbox_search_next(search_ctx, &mail) && ret > 0) { if (mail->expunged) { ret = 0; break; } if ((++copy_count % COPY_CHECK_INTERVAL) == 0) client_send_sendalive_if_needed(client); save_ctx = mailbox_save_alloc(t); mailbox_save_copy_flags(save_ctx, mail); if (move) { if (mailbox_move(&save_ctx, mail) < 0) ret = -1; } else { if (mailbox_copy(&save_ctx, mail) < 0) ret = -1; } if (ret < 0 && mail->expunged) ret = 0; msgset_generator_next(&srcset_ctx, mail->uid); } msgset_generator_finish(&srcset_ctx); if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; *src_trans_r = src_trans; *src_uidset_r = str_c(src_uidset); *copy_count_r = copy_count; return ret; } static void copy_update_trashed(struct client *client, struct mailbox *box, unsigned int count) { const struct mailbox_settings *set; set = mailbox_settings_find(mailbox_get_namespace(box), mailbox_get_vname(box)); if (set != NULL && set->special_use[0] != '\0' && str_array_icase_find(t_strsplit_spaces(set->special_use, " "), "\\Trash")) client->trashed_count += count; } static bool cmd_copy_full(struct client_command_context *cmd, bool move) { struct client *client = cmd->client; struct mail_storage *dest_storage; struct mailbox *destbox; struct mailbox_transaction_context *t, *src_trans; struct mail_search_args *search_args; const char *messageset, *mailbox, *src_uidset; enum mailbox_sync_flags sync_flags = 0; enum imap_sync_flags imap_flags = 0; struct mail_transaction_commit_changes changes; unsigned int copy_count; string_t *msg; int ret; /* <message set> <mailbox> */ if (!client_read_string_args(cmd, 2, &messageset, &mailbox)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; ret = imap_search_get_seqset(cmd, messageset, cmd->uid, &search_args); if (ret <= 0) return ret < 0; if (client_open_save_dest_box(cmd, mailbox, &destbox) < 0) { mail_search_args_unref(&search_args); return TRUE; } t = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS); imap_transaction_set_cmd_reason(t, cmd); ret = fetch_and_copy(cmd, move, t, &src_trans, search_args, &src_uidset, ©_count); mail_search_args_unref(&search_args); msg = t_str_new(256); if (ret <= 0) mailbox_transaction_rollback(&t); else if (mailbox_transaction_commit_get_changes(&t, &changes) < 0) { if (mailbox_get_last_mail_error(destbox) == MAIL_ERROR_EXPUNGED) { /* storage backend didn't notice the expunge until at commit time. */ ret = 0; } else { ret = -1; } } else if (copy_count == 0) { str_append(msg, "OK No messages found."); pool_unref(&changes.pool); } else if (seq_range_count(&changes.saved_uids) == 0 || changes.no_read_perm) { /* not supported by backend (virtual) or no read permissions for mailbox */ str_append(msg, move ? "OK Move completed." : "OK Copy completed."); pool_unref(&changes.pool); } else if (move) { i_assert(copy_count == seq_range_count(&changes.saved_uids)); copy_update_trashed(client, destbox, copy_count); str_printfa(msg, "* OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Moved UIDs."); client_send_line(client, str_c(msg)); str_truncate(msg, 0); str_append(msg, "OK Move completed."); pool_unref(&changes.pool); } else { i_assert(copy_count == seq_range_count(&changes.saved_uids)); copy_update_trashed(client, destbox, copy_count); str_printfa(msg, "OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Copy completed."); pool_unref(&changes.pool); } if (ret <= 0 && move) { /* move failed, don't expunge anything */ mailbox_transaction_rollback(&src_trans); } else { if (mailbox_transaction_commit(&src_trans) < 0) ret = -1; } dest_storage = mailbox_get_storage(destbox); if (destbox != client->mailbox) { if (move) sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE; else sync_flags |= MAILBOX_SYNC_FLAG_FAST; imap_flags |= IMAP_SYNC_FLAG_SAFE; mailbox_free(&destbox); } else if (move) { sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE; imap_flags |= IMAP_SYNC_FLAG_SAFE; } if (ret > 0) return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg)); else if (ret == 0) { /* some messages were expunged, sync them */ return cmd_sync(cmd, 0, 0, "NO ["IMAP_RESP_CODE_EXPUNGEISSUED"] " "Some of the requested messages no longer exist."); } else { client_send_storage_error(cmd, dest_storage); return TRUE; } } bool cmd_copy(struct client_command_context *cmd) { return cmd_copy_full(cmd, FALSE); } bool cmd_move(struct client_command_context *cmd) { return cmd_copy_full(cmd, TRUE); }