if (fields == 0)
return 0;
+ if (_mail->saving) {
+ mail_storage_set_critical(_mail->box->storage,
+ "Can't fetch data from uncommitted message");
+ return -1;
+ }
+
/* if we already know that the mail is expunged,
don't try to FETCH it */
view = mbox->delayed_sync_view != NULL ?
*input = filter_input;
}
+void imapc_mail_init_stream(struct imapc_mail *mail, bool have_body)
+{
+ struct index_mail *imail = &mail->imail;
+ struct mail *_mail = &imail->mail.mail;
+ struct istream *input;
+ uoff_t size;
+ int ret;
+
+ i_stream_set_name(imail->data.stream,
+ t_strdup_printf("imapc mail uid=%u", _mail->uid));
+ index_mail_set_read_buffer_size(_mail, imail->data.stream);
+
+ imapc_stream_filter(&imail->data.stream);
+ if (imail->mail.v.istream_opened != NULL) {
+ if (imail->mail.v.istream_opened(_mail,
+ &imail->data.stream) < 0) {
+ i_stream_unref(&imail->data.stream);
+ return;
+ }
+ } else if (have_body) {
+ ret = i_stream_get_size(imail->data.stream, TRUE, &size);
+ if (ret < 0) {
+ i_stream_unref(&imail->data.stream);
+ return;
+ }
+ i_assert(ret != 0);
+ imail->data.physical_size = size;
+ /* we'll assume that the remote server is working properly and
+ sending CRLF linefeeds */
+ imail->data.virtual_size = size;
+ }
+
+ if (index_mail_init_stream(imail, NULL, NULL, &input) < 0)
+ i_stream_unref(&imail->data.stream);
+}
+
static void
imapc_fetch_stream(struct imapc_mail *mail,
const struct imapc_untagged_reply *reply,
const struct imap_arg *arg, bool body)
{
struct index_mail *imail = &mail->imail;
- struct mail *_mail = &imail->mail.mail;
- struct istream *input;
- uoff_t size;
const char *value;
- int fd, ret;
+ int fd;
if (imail->data.stream != NULL) {
if (!body)
if (!imap_arg_get_nstring(arg, &value))
return;
if (value == NULL) {
- mail_set_expunged(_mail);
+ mail_set_expunged(&imail->mail.mail);
return;
}
if (mail->body == NULL) {
mail->body->used);
}
- i_stream_set_name(imail->data.stream,
- t_strdup_printf("imapc mail uid=%u", _mail->uid));
- index_mail_set_read_buffer_size(_mail, imail->data.stream);
-
- imapc_stream_filter(&imail->data.stream);
- if (imail->mail.v.istream_opened != NULL) {
- if (imail->mail.v.istream_opened(_mail,
- &imail->data.stream) < 0) {
- i_stream_unref(&imail->data.stream);
- return;
- }
- } else if (body) {
- ret = i_stream_get_size(imail->data.stream, TRUE, &size);
- if (ret < 0) {
- i_stream_unref(&imail->data.stream);
- return;
- }
- i_assert(ret != 0);
- imail->data.physical_size = size;
- /* we'll assume that the remote server is working properly and
- sending CRLF linefeeds */
- imail->data.virtual_size = size;
- }
-
- if (index_mail_init_stream(imail, NULL, NULL, &input) < 0)
- i_stream_unref(&imail->data.stream);
+ imapc_mail_init_stream(mail, body);
}
void imapc_mail_fetch_update(struct imapc_mail *mail,
#include "imapc-client.h"
#include "imapc-storage.h"
#include "imapc-sync.h"
+#include "imapc-mail.h"
struct imapc_save_context {
struct mail_save_context ctx;
uint32_t dest_uid_validity;
ARRAY_TYPE(seq_range) dest_saved_uids;
+ unsigned int save_count;
unsigned int failed:1;
unsigned int finished:1;
}
static void imapc_save_appenduid(struct imapc_save_context *ctx,
- const struct imapc_command_reply *reply)
+ const struct imapc_command_reply *reply,
+ uint32_t *uid_r)
{
const char *const *args;
uint32_t uid_validity, dest_uid;
+ *uid_r = 0;
+
/* <uidvalidity> <dest uid-set> */
args = t_strsplit(reply->resp_text_value, " ");
if (str_array_length(args) != 2)
else if (ctx->dest_uid_validity != uid_validity)
return;
- if (str_to_uint32(args[1], &dest_uid) == 0)
+ if (str_to_uint32(args[1], &dest_uid) == 0) {
seq_range_array_add(&ctx->dest_saved_uids, 0, dest_uid);
+ *uid_r = dest_uid;
+ }
+}
+
+static void
+imapc_save_add_to_index(struct imapc_save_context *ctx, uint32_t uid)
+{
+ struct mail *_mail = ctx->ctx.dest_mail;
+ struct index_mail *imail = (struct index_mail *)_mail;
+ uint32_t seq;
+
+ if (_mail == NULL)
+ return;
+
+ /* we'll temporarily append messages and at commit time expunge
+ them all, since we can't guarantee that no one else has saved
+ messages to remote server during our transaction */
+ mail_index_append(ctx->trans, uid, &seq);
+ mail_set_seq_saving(_mail, seq);
+ imail->data.forced_no_caching = TRUE;
+
+ if (ctx->fd != -1) {
+ imail->data.stream = i_stream_create_fd(ctx->fd, 0, TRUE);
+ imapc_mail_init_stream((struct imapc_mail *)imail, TRUE);
+ ctx->fd = -1;
+ }
+
+ ctx->save_count++;
}
static void imapc_save_callback(const struct imapc_command_reply *reply,
void *context)
{
struct imapc_save_cmd_context *ctx = context;
+ uint32_t uid = 0;
if (reply->state == IMAPC_COMMAND_STATE_OK) {
if (strcasecmp(reply->resp_text_key, "APPENDUID") == 0)
- imapc_save_appenduid(ctx->ctx, reply);
+ imapc_save_appenduid(ctx->ctx, reply, &uid);
+ imapc_save_add_to_index(ctx->ctx, uid);
ctx->ret = 0;
} else if (reply->state == IMAPC_COMMAND_STATE_NO) {
imapc_copy_error_from_reply(ctx->ctx->mbox->storage,
struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx;
struct mail_transaction_commit_changes *changes =
_ctx->transaction->changes;
+ uint32_t i, last_seq;
i_assert(ctx->finished);
+ /* expunge all added messages from index before commit */
+ last_seq = mail_index_view_get_messages_count(ctx->trans->view);
+ for (i = 0; i < ctx->save_count; i++)
+ mail_index_expunge(ctx->trans, last_seq - i);
+
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);
}
static void imapc_save_copyuid(struct imapc_save_context *ctx,
- const struct imapc_command_reply *reply)
+ const struct imapc_command_reply *reply,
+ uint32_t *uid_r)
{
const char *const *args;
uint32_t uid_validity, dest_uid;
+ *uid_r = 0;
+
/* <uidvalidity> <source uid-set> <dest uid-set> */
args = t_strsplit(reply->resp_text_value, " ");
if (str_array_length(args) != 3)
else if (ctx->dest_uid_validity != uid_validity)
return;
- if (str_to_uint32(args[2], &dest_uid) == 0)
+ if (str_to_uint32(args[2], &dest_uid) == 0) {
seq_range_array_add(&ctx->dest_saved_uids, 0, dest_uid);
+ *uid_r = dest_uid;
+ }
}
static void imapc_copy_callback(const struct imapc_command_reply *reply,
void *context)
{
struct imapc_save_cmd_context *ctx = context;
+ uint32_t uid = 0;
if (reply->state == IMAPC_COMMAND_STATE_OK) {
if (strcasecmp(reply->resp_text_key, "COPYUID") == 0)
- imapc_save_copyuid(ctx->ctx, reply);
+ imapc_save_copyuid(ctx->ctx, reply, &uid);
+ imapc_save_add_to_index(ctx->ctx, uid);
ctx->ret = 0;
} else if (reply->state == IMAPC_COMMAND_STATE_NO) {
imapc_copy_error_from_reply(ctx->ctx->mbox->storage,