static int cmd_import_box_contents(struct doveadm_mail_iter *iter, struct mail *src_mail, struct mailbox *dest_box) { struct mail_save_context *save_ctx; struct mailbox_transaction_context *dest_trans; const char *mailbox = mailbox_get_vname(dest_box); int ret = 0; dest_trans = mailbox_transaction_begin(dest_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); do { if (doveadm_debug) { i_debug("import: box=%s uid=%u", mailbox, src_mail->uid); } save_ctx = mailbox_save_alloc(dest_trans); mailbox_save_copy_flags(save_ctx, src_mail); if (mailbox_copy(&save_ctx, src_mail) < 0) { i_error("Copying box=%s uid=%u failed: %s", mailbox, src_mail->uid, mailbox_get_last_error(dest_box, NULL)); ret = -1; } } while (doveadm_mail_iter_next(iter, &src_mail)); if (mailbox_transaction_commit(&dest_trans) < 0) { i_error("Committing copied mails to %s failed: %s", mailbox, mailbox_get_last_error(dest_box, NULL)); ret = -1; } return ret; }
static int cmd_save_to_mailbox(struct save_cmd_context *ctx, struct mailbox *box, struct istream *input) { struct mail_storage *storage = mailbox_get_storage(box); struct mailbox_transaction_context *trans; struct mail_save_context *save_ctx; ssize_t ret; bool save_failed = FALSE; if (mailbox_open(box) < 0) { i_error("Failed to open mailbox %s: %s", mailbox_get_vname(box), mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); return -1; } trans = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); save_ctx = mailbox_save_alloc(trans); if (mailbox_save_begin(&save_ctx, input) < 0) { i_error("Saving failed: %s", mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); mailbox_transaction_rollback(&trans); return -1; } while ((ret = i_stream_read(input)) > 0 || ret == -2) { if (mailbox_save_continue(save_ctx) < 0) { save_failed = TRUE; ret = -1; break; } } i_assert(ret == -1); if (input->stream_errno != 0) { i_error("read(msg input) failed: %s", i_stream_get_error(input)); doveadm_mail_failed_error(&ctx->ctx, MAIL_ERROR_TEMP); } else if (save_failed) { i_error("Saving failed: %s", mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else if (mailbox_save_finish(&save_ctx) < 0) { i_error("Saving failed: %s", mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else if (mailbox_transaction_commit(&trans) < 0) { i_error("Save transaction commit failed: %s", mailbox_get_last_error(box, NULL)); doveadm_mail_failed_storage(&ctx->ctx, storage); } else { ret = 0; } if (save_ctx != NULL) mailbox_save_cancel(&save_ctx); if (trans != NULL) mailbox_transaction_rollback(&trans); i_assert(input->eof); return ret < 0 ? -1 : 0; }
static int fetch_and_copy(struct client *client, 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 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); 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 int cmd_copy_box(struct copy_cmd_context *ctx, struct mailbox *destbox, const struct mailbox_info *info) { struct doveadm_mail_iter *iter; struct mailbox_transaction_context *desttrans; struct mail_save_context *save_ctx; struct mail *mail; int ret = 0; if (doveadm_mail_iter_init(&ctx->ctx, info, ctx->ctx.search_args, 0, NULL, &iter) < 0) return -1; /* use a separately committed transaction for each mailbox. this guarantees that mails aren't expunged without actually having been copied. */ desttrans = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL); while (doveadm_mail_iter_next(iter, &mail)) { save_ctx = mailbox_save_alloc(desttrans); mailbox_save_copy_flags(save_ctx, mail); if (mailbox_copy(&save_ctx, mail) == 0) { if (ctx->move) mail_expunge(mail); } else { i_error("Copying message UID %u from '%s' failed: %s", mail->uid, info->vname, mailbox_get_last_error(destbox, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, destbox); ret = -1; } } if (mailbox_transaction_commit(&desttrans) < 0) { i_error("Committing %s mails failed: %s", ctx->move ? "moved" : "copied", mailbox_get_last_error(destbox, NULL)); doveadm_mail_failed_mailbox(&ctx->ctx, destbox); /* rollback expunges */ doveadm_mail_iter_deinit_rollback(&iter); ret = -1; } else { if (doveadm_mail_iter_deinit_sync(&iter) < 0) ret = -1; } return ret; }
static int snarf(struct mailbox *srcbox, struct mailbox *destbox) { struct mail_search_args *search_args; struct mail_search_context *search_ctx; struct mailbox_transaction_context *src_trans, *dest_trans; struct mail_save_context *save_ctx; struct mail *mail; enum mail_error error; int ret; /* make sure the destination mailbox has been opened. note that this locks the mailbox. */ if (mailbox_open(destbox) < 0) return -1; if (mailbox_sync(srcbox, MAILBOX_SYNC_FLAG_FULL_READ) < 0) return -1; src_trans = mailbox_transaction_begin(srcbox, 0); dest_trans = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL); search_args = mail_search_build_init(); mail_search_build_add_all(search_args); search_ctx = mailbox_search_init(src_trans, search_args, NULL, MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, NULL); mail_search_args_unref(&search_args); ret = 0; while (mailbox_search_next(search_ctx, &mail)) { if (mail->expunged) continue; save_ctx = mailbox_save_alloc(dest_trans); if (mailbox_copy(&save_ctx, mail) < 0 && !mail->expunged) { error = mailbox_get_last_mail_error(destbox); /* if we failed because of out of disk space, just move those messages we managed to move so far. */ if (error != MAIL_ERROR_NOQUOTA) ret = -1; break; } mail_expunge(mail); } if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; /* commit the copied messages to the destination mailbox. if we crash between that and between expunging the messages from the source mailbox, we're left with duplicates. */ if (ret < 0) mailbox_transaction_rollback(&dest_trans); else if (mailbox_transaction_commit(&dest_trans) < 0) ret = -1; if (ret < 0) mailbox_transaction_rollback(&src_trans); else { if (mailbox_transaction_commit(&src_trans) < 0) ret = -1; } if (ret == 0) { if (mailbox_sync(srcbox, 0) < 0) ret = -1; } return ret; }
static int cmd_append_handle_args(struct client_command_context *cmd, const struct imap_arg *args, bool *nonsync_r) { struct client *client = cmd->client; struct cmd_append_context *ctx = cmd->context; const struct imap_arg *flags_list; const struct imap_arg *cat_list = NULL; enum mail_flags flags; const char *const *keywords_list; struct mail_keywords *keywords; struct istream *input; const char *internal_date_str; time_t internal_date; int ret, timezone_offset; bool valid; /* [<flags>] */ if (!imap_arg_get_list(args, &flags_list)) flags_list = NULL; else args++; /* [<internal date>] */ if (args->type != IMAP_ARG_STRING) internal_date_str = NULL; else { internal_date_str = imap_arg_as_astring(args); args++; } /* <message literal> | CATENATE (..) */ valid = FALSE; *nonsync_r = FALSE; ctx->catenate = FALSE; if (imap_arg_get_literal_size(args, &ctx->literal_size)) { *nonsync_r = args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC; ctx->binary_input = args->literal8; valid = TRUE; } else if (!imap_arg_atom_equals(args, "CATENATE")) { /* invalid */ } else if (!imap_arg_get_list(++args, &cat_list)) { /* invalid */ } else { valid = TRUE; ctx->catenate = TRUE; /* We'll do BINARY conversion only if the CATENATE's first part is a literal8. If it doesn't and a literal8 is seen later we'll abort the append with UNKNOWN-CTE. */ ctx->binary_input = imap_arg_atom_equals(&cat_list[0], "TEXT") && cat_list[1].literal8; } if (!IMAP_ARG_IS_EOL(&args[1])) valid = FALSE; if (!valid) { client->input_skip_line = TRUE; if (!ctx->failed) client_send_command_error(cmd, "Invalid arguments."); return -1; } if (flags_list == NULL || ctx->failed) { flags = 0; keywords = NULL; } else { if (!client_parse_mail_flags(cmd, flags_list, &flags, &keywords_list)) return -1; if (keywords_list == NULL) keywords = NULL; else if (mailbox_keywords_create(ctx->box, keywords_list, &keywords) < 0) { /* invalid keywords - delay failure */ client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; keywords = NULL; } } if (internal_date_str == NULL || ctx->failed) { /* no time given, default to now. */ internal_date = (time_t)-1; timezone_offset = 0; } else if (!imap_parse_datetime(internal_date_str, &internal_date, &timezone_offset)) { client_send_command_error(cmd, "Invalid internal date."); if (keywords != NULL) mailbox_keywords_unref(&keywords); return -1; } if (internal_date != (time_t)-1 && internal_date > ioloop_time + INTERNALDATE_MAX_FUTURE_SECS) { /* the client specified a time in the future, set it to now. */ internal_date = (time_t)-1; timezone_offset = 0; } if (cat_list != NULL) { ctx->cat_msg_size = 0; ctx->input = i_stream_create_chain(&ctx->catchain); } else { if (ctx->literal_size == 0) { /* no message data, abort */ if (!ctx->failed) { client_send_tagline(cmd, "NO Can't save a zero byte message."); ctx->failed = TRUE; } if (!*nonsync_r) { if (keywords != NULL) mailbox_keywords_unref(&keywords); return -1; } /* {0+} used. although there isn't any point in using MULTIAPPEND here and adding more messages, it is technically valid so we'll continue parsing.. */ } ctx->litinput = i_stream_create_limit(client->input, ctx->literal_size); ctx->input = ctx->litinput; i_stream_ref(ctx->input); } if (ctx->binary_input) { input = i_stream_create_binary_converter(ctx->input); i_stream_unref(&ctx->input); ctx->input = input; } if (!ctx->failed) { /* save the mail */ ctx->save_ctx = mailbox_save_alloc(ctx->t); mailbox_save_set_flags(ctx->save_ctx, flags, keywords); mailbox_save_set_received_date(ctx->save_ctx, internal_date, timezone_offset); if (mailbox_save_begin(&ctx->save_ctx, ctx->input) < 0) { /* save initialization failed */ client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } } if (keywords != NULL) mailbox_keywords_unref(&keywords); ctx->count++; if (cat_list == NULL) { /* normal APPEND */ return 1; } else if (cat_list->type == IMAP_ARG_EOL) { /* zero parts */ if (!ctx->failed) client_send_command_error(cmd, "Empty CATENATE list."); client->input_skip_line = TRUE; return -1; } else if ((ret = cmd_append_catenate(cmd, cat_list, nonsync_r)) < 0) { /* invalid parameters, abort immediately */ return -1; } else if (ret == 0) { /* CATENATE consisted only of URLs */ return 0; } else { /* TEXT part found from CATENATE */ return 1; } }