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; }
int mail_storage_copy(struct mail_save_context *ctx, struct mail *mail) { if (ctx->data.keywords != NULL) { /* keywords gets unreferenced twice: first in mailbox_save_cancel()/_finish() and second time in mailbox_copy(). */ mailbox_keywords_ref(ctx->data.keywords); } if (mail_storage_try_copy(&ctx, mail) < 0) { if (ctx != NULL) mailbox_save_cancel(&ctx); return -1; } return mailbox_save_finish(&ctx); }
static void cmd_append_finish_catenate(struct client_command_context *cmd) { struct cmd_append_context *ctx = cmd->context; i_stream_chain_append_eof(ctx->catchain); i_stream_unref(&ctx->input); ctx->catenate = FALSE; ctx->catchain = NULL; if (ctx->failed) { /* APPEND has already failed */ if (ctx->save_ctx != NULL) mailbox_save_cancel(&ctx->save_ctx); } else { if (mailbox_save_finish(&ctx->save_ctx) < 0) { client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } } }
static bool cmd_append_continue_message(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_append_context *ctx = cmd->context; int ret = 0; if (cmd->cancel) { /* cancel the command immediately (disconnection) */ cmd_append_finish(ctx); return TRUE; } if (ctx->save_ctx != NULL) { while (ctx->litinput->v_offset != ctx->literal_size) { ret = i_stream_read(ctx->litinput); if (mailbox_save_continue(ctx->save_ctx) < 0) { /* we still have to finish reading the message from client */ mailbox_save_cancel(&ctx->save_ctx); break; } if (ret == -1 || ret == 0) break; } } if (ctx->save_ctx == NULL) { /* saving has already failed, we're just eating away the literal */ (void)i_stream_read(ctx->litinput); i_stream_skip(ctx->litinput, i_stream_get_data_size(ctx->litinput)); } if (ctx->litinput->eof || client->input->closed) { uoff_t lit_offset = ctx->litinput->v_offset; /* finished - do one more read, to make sure istream-chain unreferences its stream, which is needed for litinput's unreferencing to seek the client->input to correct position. the seek is needed to avoid trying to seek backwards in the ctx->input's parent stream. */ i_stream_seek(ctx->input, ctx->input->v_offset); (void)i_stream_read(ctx->input); i_stream_unref(&ctx->litinput); if (ctx->failed) { if (ctx->save_ctx != NULL) mailbox_save_cancel(&ctx->save_ctx); } else if (ctx->save_ctx == NULL) { /* failed above */ client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } else if (lit_offset != ctx->literal_size) { /* client disconnected before it finished sending the whole message. */ ctx->failed = TRUE; mailbox_save_cancel(&ctx->save_ctx); client_disconnect(client, get_disconnect_reason(ctx, lit_offset)); } else if (ctx->catenate) { /* CATENATE isn't finished yet */ } else if (mailbox_save_finish(&ctx->save_ctx) < 0) { client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } if (client->input->closed) { cmd_append_finish(ctx); return TRUE; } /* prepare for the next message (or its part with catenate) */ ctx->message_input = FALSE; imap_parser_reset(ctx->save_parser); if (ctx->catenate) { cmd->func = cmd_append_continue_catenate; return cmd_append_continue_catenate(cmd); } i_stream_unref(&ctx->input); cmd->func = cmd_append_parse_new_msg; return cmd_append_parse_new_msg(cmd); } return FALSE; }