imapc-save.c revision 29371e68adc180501454783b44ec8e43b4e6ddc1
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "lib.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "str.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "istream.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "istream-crlf.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "ostream.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "imap-date.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "imap-util.h"
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen#include "index-mail.h"
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen#include "mail-copy.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "imapc-client.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "imapc-storage.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen#include "imapc-sync.h"
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenstruct imapc_save_context {
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen struct mail_save_context ctx;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct imapc_mailbox *mbox;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct mail_index_transaction *trans;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen int fd;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen char *temp_path;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct istream *input;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen uint32_t dest_uid_validity;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ARRAY_TYPE(seq_range) dest_saved_uids;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen unsigned int failed:1;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen unsigned int finished:1;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen};
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenstruct imapc_save_cmd_context {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct imapc_save_context *ctx;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen int ret;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen};
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenvoid imapc_transaction_save_rollback(struct mail_save_context *_ctx);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenstruct mail_save_context *
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenimapc_save_alloc(struct mailbox_transaction_context *t)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen{
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct imapc_mailbox *mbox = (struct imapc_mailbox *)t->box;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct imapc_save_context *ctx;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (t->save_ctx == NULL) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx = i_new(struct imapc_save_context, 1);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->ctx.transaction = t;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->mbox = mbox;
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen ctx->trans = t->itrans;
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen ctx->fd = -1;
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen t->save_ctx = &ctx->ctx;
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen }
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen return t->save_ctx;
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen}
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainenint imapc_save_begin(struct mail_save_context *_ctx, struct istream *input)
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen{
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct mail_storage *storage = _ctx->transaction->box->storage;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen const char *path;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen i_assert(ctx->fd == -1);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->fd = imapc_client_create_temp_fd(ctx->mbox->storage->client, &path);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (ctx->fd == -1) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen mail_storage_set_critical(storage,
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen "Couldn't create temp file %s", path);
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen ctx->failed = TRUE;
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen return -1;
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen }
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen /* we may not know the size of the input, or be sure that it contains
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen only CRLFs. so we'll always first write the mail to a temp file and
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen upload it from there to remote server. */
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen ctx->finished = FALSE;
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen ctx->temp_path = i_strdup(path);
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen ctx->input = i_stream_create_crlf(input);
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen _ctx->output = o_stream_create_fd_file(ctx->fd, 0, FALSE);
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen o_stream_cork(_ctx->output);
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen return 0;
9d8f243a8765cb8dc0b513df7add7475affed0bbTimo Sirainen}
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainenint imapc_save_continue(struct mail_save_context *_ctx)
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen{
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx;
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen struct mail_storage *storage = _ctx->transaction->box->storage;
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen if (ctx->failed)
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen return -1;
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (o_stream_send_istream(_ctx->output, ctx->input) < 0) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen mail_storage_set_critical(storage,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen "o_stream_send_istream(%s) failed: %m",
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->temp_path);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen }
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->failed = TRUE;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen return -1;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen }
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen return 0;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen}
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
36723cf206a7b64b9d972ab0719bbfaacc9316faTimo Sirainenstatic void imapc_save_appenduid(struct imapc_save_context *ctx,
36723cf206a7b64b9d972ab0719bbfaacc9316faTimo Sirainen const struct imapc_command_reply *reply)
36723cf206a7b64b9d972ab0719bbfaacc9316faTimo Sirainen{
36723cf206a7b64b9d972ab0719bbfaacc9316faTimo Sirainen const char *const *args;
e83126866761632b437e532dfdc30be01d14039dTimo Sirainen uint32_t uid_validity, dest_uid;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* <uidvalidity> <dest uid-set> */
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen args = t_strsplit(reply->resp_text_value, " ");
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (str_array_length(args) != 2)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen return;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (str_to_uint32(args[0], &uid_validity) < 0)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen return;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (ctx->dest_uid_validity == 0)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->dest_uid_validity = uid_validity;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen else if (ctx->dest_uid_validity != uid_validity)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen return;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (str_to_uint32(args[1], &dest_uid) == 0)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen seq_range_array_add(&ctx->dest_saved_uids, 0, dest_uid);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen}
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenstatic void imapc_save_callback(const struct imapc_command_reply *reply,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen void *context)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen{
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct imapc_save_cmd_context *ctx = context;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (reply->state == IMAPC_COMMAND_STATE_OK) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (strcasecmp(reply->resp_text_key, "APPENDUID") == 0)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen imapc_save_appenduid(ctx->ctx, reply);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->ret = 0;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen } else if (reply->state == IMAPC_COMMAND_STATE_NO) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen imapc_copy_error_from_reply(ctx->ctx->mbox->storage,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen MAIL_ERROR_PARAMS, reply);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->ret = -1;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen } else {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen mail_storage_set_critical(&ctx->ctx->mbox->storage->storage,
d24f5d5563b9d444920e57d0c5cfc5797fcf687dTimo Sirainen "imapc: COPY failed: %s", reply->text_full);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx->ret = -1;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen }
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen imapc_client_stop(ctx->ctx->mbox->storage->client);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen}
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenstatic void
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenimapc_append_keywords(string_t *str, struct mail_keywords *kw)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen{
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen const ARRAY_TYPE(keywords) *kw_arr;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen const char *const *kw_p;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen unsigned int i;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen kw_arr = mail_index_get_keywords(kw->index);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen for (i = 0; i < kw->count; i++) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen kw_p = array_idx(kw_arr, kw->idx[i]);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (str_len(str) > 1)
71e88fae3be360e9a93b3398e743f99a6f05d2edTimo Sirainen str_append_c(str, ' ');
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen str_append(str, *kw_p);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen }
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen}
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenstatic int imapc_save_append(struct imapc_save_context *ctx)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen{
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct mail_save_context *_ctx = &ctx->ctx;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct imapc_save_cmd_context sctx;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen struct istream *input;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen const char *flags = "", *internaldate = "";
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (_ctx->flags != 0 || _ctx->keywords != NULL) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen string_t *str = t_str_new(64);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen str_append(str, " (");
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen imap_write_flags(str, _ctx->flags & ~MAIL_RECENT, NULL);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (_ctx->keywords != NULL)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen imapc_append_keywords(str, _ctx->keywords);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen str_append_c(str, ')');
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen flags = str_c(str);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen }
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (_ctx->received_date != (time_t)-1) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen internaldate = t_strdup_printf(" \"%s\"",
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen imap_to_datetime(_ctx->received_date));
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen }
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen input = i_stream_create_fd(ctx->fd, IO_BLOCK_SIZE, FALSE);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen sctx.ctx = ctx;
imapc_client_cmdf(ctx->mbox->storage->client,
imapc_save_callback, &sctx, "APPEND %s%1s%1s %p",
ctx->mbox->box.name, flags, internaldate, input);
i_stream_unref(&input);
imapc_client_run(ctx->mbox->storage->client);
return sctx.ret;
}
int imapc_save_finish(struct mail_save_context *_ctx)
{
struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx;
struct mail_storage *storage = _ctx->transaction->box->storage;
ctx->finished = TRUE;
if (!ctx->failed) {
if (o_stream_flush(_ctx->output) < 0) {
if (!mail_storage_set_error_from_errno(storage)) {
mail_storage_set_critical(storage,
"o_stream_flush(%s) failed: %m",
ctx->temp_path);
}
ctx->failed = TRUE;
}
}
if (!ctx->failed) {
if (imapc_save_append(ctx) < 0)
ctx->failed = TRUE;
}
if (_ctx->output != NULL)
o_stream_unref(&_ctx->output);
if (ctx->input != NULL)
i_stream_unref(&ctx->input);
if (ctx->fd != -1) {
if (close(ctx->fd) < 0)
i_error("close(%s) failed: %m", ctx->temp_path);
ctx->fd = -1;
}
i_free(ctx->temp_path);
index_save_context_free(_ctx);
return ctx->failed ? -1 : 0;
}
void imapc_save_cancel(struct mail_save_context *_ctx)
{
struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx;
ctx->failed = TRUE;
(void)imapc_save_finish(_ctx);
}
int imapc_transaction_save_commit_pre(struct mail_save_context *_ctx)
{
struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx;
struct mail_transaction_commit_changes *changes =
_ctx->transaction->changes;
i_assert(ctx->finished);
if (array_is_created(&ctx->dest_saved_uids)) {
changes->uid_validity = ctx->dest_uid_validity;
array_append_array(&changes->saved_uids, &ctx->dest_saved_uids);
}
return 0;
}
void imapc_transaction_save_commit_post(struct mail_save_context *_ctx,
struct mail_index_transaction_commit_result *result ATTR_UNUSED)
{
imapc_transaction_save_rollback(_ctx);
}
void imapc_transaction_save_rollback(struct mail_save_context *_ctx)
{
struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx;
/* FIXME: if we really want to rollback, we should expunge messages
we already saved */
if (!ctx->finished)
imapc_save_cancel(_ctx);
if (array_is_created(&ctx->dest_saved_uids))
array_free(&ctx->dest_saved_uids);
i_free(ctx);
}
static void imapc_save_copyuid(struct imapc_save_context *ctx,
const struct imapc_command_reply *reply)
{
const char *const *args;
uint32_t uid_validity, dest_uid;
/* <uidvalidity> <source uid-set> <dest uid-set> */
args = t_strsplit(reply->resp_text_value, " ");
if (str_array_length(args) != 3)
return;
if (str_to_uint32(args[0], &uid_validity) < 0)
return;
if (ctx->dest_uid_validity == 0)
ctx->dest_uid_validity = uid_validity;
else if (ctx->dest_uid_validity != uid_validity)
return;
if (str_to_uint32(args[2], &dest_uid) == 0)
seq_range_array_add(&ctx->dest_saved_uids, 0, dest_uid);
}
static void imapc_copy_callback(const struct imapc_command_reply *reply,
void *context)
{
struct imapc_save_cmd_context *ctx = context;
if (reply->state == IMAPC_COMMAND_STATE_OK) {
if (strcasecmp(reply->resp_text_key, "COPYUID") == 0)
imapc_save_copyuid(ctx->ctx, reply);
ctx->ret = 0;
} else if (reply->state == IMAPC_COMMAND_STATE_NO) {
imapc_copy_error_from_reply(ctx->ctx->mbox->storage,
MAIL_ERROR_PARAMS, reply);
ctx->ret = -1;
} else {
mail_storage_set_critical(&ctx->ctx->mbox->storage->storage,
"imapc: COPY failed: %s", reply->text_full);
ctx->ret = -1;
}
imapc_client_stop(ctx->ctx->mbox->storage->client);
}
int imapc_copy(struct mail_save_context *_ctx, struct mail *mail)
{
struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx;
struct mailbox_transaction_context *_t = _ctx->transaction;
struct imapc_mailbox *src_mbox = (struct imapc_mailbox *)mail->box;
struct imapc_client_mailbox *src_client_box;
struct imapc_save_cmd_context sctx;
i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
if (_t->box->storage == mail->box->storage) {
/* same server, we can use COPY for the mail */
if (imapc_mailbox_get_client_box(src_mbox, &src_client_box) < 0)
return -1;
sctx.ret = -2;
sctx.ctx = ctx;
imapc_client_mailbox_cmdf(src_client_box,
imapc_copy_callback, &sctx,
"UID COPY %u %s",
mail->uid, _t->box->name);
imapc_client_run(src_mbox->storage->client);
i_assert(sctx.ret != -2);
ctx->finished = TRUE;
return sctx.ret;
}
return mail_storage_copy(_ctx, mail);
}