imap-fetch.c revision de7dae07a080f9f0fe70a5ace618f78823954005
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "imap-common.h"
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen#include "array.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "buffer.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "istream.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "ostream.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "str.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "message-send.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "message-size.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "imap-date.h"
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen#include "mail-search-build.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "imap-commands.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "imap-quote.h"
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen#include "imap-fetch.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include "imap-util.h"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include <stdlib.h>
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#include <ctype.h>
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#define BODY_NIL_REPLY \
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen "\"text\" \"plain\" NIL NIL NIL \"7bit\" 0 0"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#define ENVELOPE_NIL_REPLY \
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)"
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic ARRAY_DEFINE(fetch_handlers, struct imap_fetch_handler);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic int imap_fetch_handler_cmp(const struct imap_fetch_handler *h1,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const struct imap_fetch_handler *h2)
56d1345c43bbd28c36b7faa85e4163bd9e874290Timo Sirainen{
713a54f695b8ad63826d22ebbe52f55c347e8c88Timo Sirainen return strcmp(h1->name, h2->name);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenvoid imap_fetch_handlers_register(const struct imap_fetch_handler *handlers,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen size_t count)
6766440ffdd8d3ff99a1f732eef212813089bb81Timo Sirainen{
713a54f695b8ad63826d22ebbe52f55c347e8c88Timo Sirainen array_append(&fetch_handlers, handlers, count);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen array_sort(&fetch_handlers, imap_fetch_handler_cmp);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic int
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenimap_fetch_handler_bsearch(const char *name, const struct imap_fetch_handler *h)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return strcmp(name, h->name);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenbool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const struct imap_arg **args)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const struct imap_fetch_handler *handler;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const char *lookup_name, *p;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen for (p = name; i_isalnum(*p) || *p == '-'; p++) ;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen lookup_name = t_strdup_until(name, p);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen handler = array_bsearch(&fetch_handlers, lookup_name,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen imap_fetch_handler_bsearch);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (handler == NULL) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client_send_command_error(ctx->cmd,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen t_strconcat("Unknown parameter ", name, NULL));
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return FALSE;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen }
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return handler->init(ctx, name, args);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstruct imap_fetch_context *
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenimap_fetch_init(struct client_command_context *cmd, struct mailbox *box)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen struct client *client = cmd->client;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen struct imap_fetch_context *ctx;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx = p_new(cmd->pool, struct imap_fetch_context, 1);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->client = client;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->cmd = cmd;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->box = box;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->cur_str = str_new(default_pool, 8192);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen p_array_init(&ctx->all_headers, cmd->pool, 64);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen p_array_init(&ctx->handlers, cmd->pool, 16);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen p_array_init(&ctx->tmp_keywords, cmd->pool,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client->keywords.announce_count + 8);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->line_finished = TRUE;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return ctx;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenbool imap_fetch_add_changed_since(struct imap_fetch_context *ctx,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen uint64_t modseq)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen struct mail_search_arg *search_arg;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen search_arg = p_new(ctx->search_args->pool, struct mail_search_arg, 1);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen search_arg->type = SEARCH_MODSEQ;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen search_arg->value.modseq =
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen p_new(ctx->cmd->pool, struct mail_search_modseq, 1);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen search_arg->value.modseq->modseq = modseq + 1;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen search_arg->next = ctx->search_args->args->next;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->search_args->args->next = search_arg;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return imap_fetch_init_handler(ctx, "MODSEQ", NULL);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#undef imap_fetch_add_handler
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenvoid imap_fetch_add_handler(struct imap_fetch_context *ctx,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen bool buffered, bool want_deinit,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const char *name, const char *nil_reply,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen imap_fetch_handler_t *handler, void *context)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* partially because of broken clients, but also partially because
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen it potentially can make client implementations faster, we have a
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen buffered parameter which basically means that the handler promises
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen to write the output in ctx->cur_str. The cur_str is then sent to
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client before calling any non-buffered handlers.
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen We try to keep the handler registration order the same as the
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client requested them. This is especially useful to get UID
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen returned first, which some clients rely on..
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const struct imap_fetch_context_handler *handlers;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen struct imap_fetch_context_handler h;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen unsigned int i, size;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (context == NULL) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* don't allow duplicate handlers */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen handlers = array_get(&ctx->handlers, &size);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen for (i = 0; i < size; i++) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (handlers[i].handler == handler &&
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen handlers[i].context == NULL)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen }
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen }
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen memset(&h, 0, sizeof(h));
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen h.handler = handler;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen h.context = context;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen h.buffered = buffered;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen h.want_deinit = want_deinit;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen h.name = p_strdup(ctx->cmd->pool, name);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen h.nil_reply = p_strdup(ctx->cmd->pool, nil_reply);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (!buffered)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen array_append(&ctx->handlers, &h, 1);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen else {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen array_insert(&ctx->handlers, ctx->buffered_handlers_count,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen &h, 1);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->buffered_handlers_count++;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen }
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic void
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenexpunges_drop_known(struct imap_fetch_context *ctx, struct mail *mail,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ARRAY_TYPE(seq_range) *expunges)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const uint32_t *seqs, *uids;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen unsigned int i, count;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen seqs = array_get(ctx->qresync_sample_seqset, &count);
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen uids = array_idx(ctx->qresync_sample_uidset, 0);
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen i_assert(array_count(ctx->qresync_sample_uidset) == count);
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen i_assert(count > 0);
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen /* FIXME: we could do removals from the middle as well */
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen for (i = 0; i < count; i++) {
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen mail_set_seq(mail, seqs[i]);
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen if (uids[i] != mail->uid)
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen break;
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen }
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen if (i > 0)
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen seq_range_array_remove_range(expunges, 1, uids[i-1]);
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen}
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainenstatic int get_expunges_fallback(struct imap_fetch_context *ctx,
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen const ARRAY_TYPE(seq_range) *uids,
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen ARRAY_TYPE(seq_range) *expunges)
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen{
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen struct mailbox_transaction_context *trans;
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen struct mail_search_args *search_args;
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen struct mail_search_context *search_ctx;
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen struct mail *mail;
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen const struct seq_range *uid_range;
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen struct mailbox_status status;
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen unsigned int i, count;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen uint32_t next_uid;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen int ret = 0;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen uid_range = array_get(uids, &count);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen i_assert(count > 0);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen i = 0;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen next_uid = uid_range[0].seq1;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen /* search UIDs in given range */
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen search_args = mail_search_build_init();
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen search_args->args = p_new(search_args->pool, struct mail_search_arg, 1);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen search_args->args->type = SEARCH_UIDSET;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen i_array_init(&search_args->args->value.seqset, array_count(uids));
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen array_append_array(&search_args->args->value.seqset, uids);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen trans = mailbox_transaction_begin(ctx->box, 0);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen mail = mail_alloc(trans, 0, NULL);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen search_ctx = mailbox_search_init(trans, search_args, NULL);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen mail_search_args_unref(&search_args);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen while (mailbox_search_next(search_ctx, mail) > 0) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (mail->uid == next_uid) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (next_uid < uid_range[i].seq2)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen next_uid++;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen else if (++i < count)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen next_uid = uid_range[++i].seq1;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen else
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen break;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen } else {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen /* next_uid .. mail->uid-1 are expunged */
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen i_assert(mail->uid > next_uid);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen while (mail->uid > uid_range[i].seq2) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen seq_range_array_add_range(expunges, next_uid,
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen uid_range[i].seq2);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen i++;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen i_assert(i < count);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen next_uid = uid_range[i].seq1;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (next_uid != mail->uid) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen seq_range_array_add_range(expunges, next_uid,
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen mail->uid - 1);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen }
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen if (uid_range[i].seq2 == mail->uid)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen next_uid = uid_range[++i].seq1;
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen else
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen next_uid = mail->uid + 1;
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen }
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen if (i < count) {
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen i_assert(next_uid <= uid_range[i].seq2);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen seq_range_array_add_range(expunges, next_uid,
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen uid_range[i].seq2);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen i++;
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen for (; i < count; i++) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen seq_range_array_add_range(expunges, uid_range[i].seq1,
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen uid_range[i].seq2);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen }
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen mailbox_get_status(ctx->box, STATUS_UIDNEXT, &status);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen seq_range_array_remove_range(expunges, status.uidnext, (uint32_t)-1);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen if (mailbox_search_deinit(&search_ctx) < 0)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen ret = -1;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (ret == 0 && ctx->qresync_sample_seqset != NULL)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen expunges_drop_known(ctx, mail, expunges);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen mail_free(&mail);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen (void)mailbox_transaction_commit(&trans);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen return ret;
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen}
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainenstatic void
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainenmailbox_expunge_to_range(const ARRAY_TYPE(mailbox_expunge_rec) *input,
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen ARRAY_TYPE(seq_range) *output)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen{
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen const struct mailbox_expunge_rec *expunges;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen unsigned int i, count;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen expunges = array_get(input, &count);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen for (i = 0; i < count; i++)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen seq_range_array_add(output, 0, expunges[i].uid);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen}
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainenstatic int
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainenimap_fetch_send_vanished(struct imap_fetch_context *ctx)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen{
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen const struct mail_search_arg *uidarg = ctx->search_args->args;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen const struct mail_search_arg *modseqarg = uidarg->next;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen const ARRAY_TYPE(seq_range) *uids = &uidarg->value.seqset;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen uint64_t modseq = modseqarg->value.modseq->modseq - 1;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen ARRAY_TYPE(mailbox_expunge_rec) expunges;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen ARRAY_TYPE(seq_range) expunges_range;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen string_t *str;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen int ret = 0;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen i_array_init(&expunges, array_count(uids));
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen i_array_init(&expunges_range, array_count(uids));
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (mailbox_get_expunges(ctx->box, modseq, uids, &expunges))
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen mailbox_expunge_to_range(&expunges, &expunges_range);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen else {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen /* return all expunged UIDs */
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (get_expunges_fallback(ctx, uids, &expunges_range) < 0) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen array_clear(&expunges_range);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen ret = -1;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen }
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen if (array_count(&expunges_range) > 0) {
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen str = str_new(default_pool, 128);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen str_append(str, "* VANISHED (EARLIER) ");
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen imap_write_seq_range(str, &expunges_range);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen str_append(str, "\r\n");
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen o_stream_send(ctx->client->output, str_data(str), str_len(str));
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen str_free(&str);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen array_free(&expunges);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen array_free(&expunges_range);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen return ret;
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenint imap_fetch_begin(struct imap_fetch_context *ctx)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const void *data;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (ctx->send_vanished) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (imap_fetch_send_vanished(ctx) < 0) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen ctx->failed = TRUE;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen return -1;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (ctx->flags_update_seen) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (mailbox_is_readonly(ctx->box))
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen ctx->flags_update_seen = FALSE;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen else if (!ctx->flags_have_handler) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen ctx->flags_show_only_seen_changes = TRUE;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen (void)imap_fetch_init_handler(ctx, "FLAGS", NULL);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen }
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen
244d1dd1a501d1e4b53d8af9a2704640b276b7daTimo Sirainen if (array_count(&ctx->all_headers) > 0 &&
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER |
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen MAIL_FETCH_STREAM_BODY)) == 0)) {
244d1dd1a501d1e4b53d8af9a2704640b276b7daTimo Sirainen (void)array_append_space(&ctx->all_headers);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen data = array_idx(&ctx->all_headers, 0);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen ctx->all_headers_ctx =
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen mailbox_header_lookup_init(ctx->box, data);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen }
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if ((ctx->fetch_data &
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->fetch_data |= MAIL_FETCH_NUL_STATE;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->trans = mailbox_transaction_begin(ctx->box,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen MAILBOX_TRANSACTION_FLAG_HIDE);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->select_counter = ctx->client->select_counter;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->mail = mail_alloc(ctx->trans, ctx->fetch_data,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->all_headers_ctx);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* Delayed uidset -> seqset conversion. VANISHED needs the uidset. */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen mail_search_args_init(ctx->search_args, ctx->box, TRUE,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen &ctx->cmd->client->search_saved_uidset);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->search_ctx =
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen mailbox_search_init(ctx->trans, ctx->search_args, NULL);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return 0;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic int imap_fetch_flush_buffer(struct imap_fetch_context *ctx)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const unsigned char *data;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen size_t len;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen data = str_data(ctx->cur_str);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen len = str_len(ctx->cur_str);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (len == 0)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return 0;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* there's an extra space at the end if we added any fetch items
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen to buffer */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (data[len-1] == ' ') {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen len--;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ctx->first = FALSE;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen }
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (o_stream_send(ctx->client->output, data, len) < 0)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return -1;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen str_truncate(ctx->cur_str, 0);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return 0;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic int imap_fetch_send_nil_reply(struct imap_fetch_context *ctx)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const struct imap_fetch_context_handler *handler;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (!ctx->first)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen str_append_c(ctx->cur_str, ' ');
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen handler = array_idx(&ctx->handlers, ctx->cur_handler);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen str_printfa(ctx->cur_str, "%s %s ",
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen handler->name, handler->nil_reply);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (!handler->buffered) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (imap_fetch_flush_buffer(ctx) < 0)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return -1;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen }
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return 0;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen}
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic int imap_fetch_more_int(struct imap_fetch_context *ctx)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen{
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen struct client *client = ctx->client;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const struct imap_fetch_context_handler *handlers;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen unsigned int count;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen int ret;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (ctx->cont_handler != NULL) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen ret = ctx->cont_handler(ctx);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (ret == 0)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen return 0;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen
if (ret < 0) {
if (client->output->closed)
return -1;
if (ctx->cur_mail->expunged) {
/* not an error, just lost it. */
ctx->partial_fetch = TRUE;
if (imap_fetch_send_nil_reply(ctx) < 0)
return -1;
} else {
return -1;
}
}
ctx->cont_handler = NULL;
ctx->cur_offset = 0;
ctx->cur_handler++;
if (ctx->cur_input != NULL)
i_stream_unref(&ctx->cur_input);
}
handlers = array_get(&ctx->handlers, &count);
for (;;) {
if (o_stream_get_buffer_used_size(client->output) >=
CLIENT_OUTPUT_OPTIMAL_SIZE) {
ret = o_stream_flush(client->output);
if (ret <= 0)
return ret;
}
if (ctx->cur_mail == NULL) {
if (ctx->cmd->cancel)
return 1;
if (mailbox_search_next(ctx->search_ctx,
ctx->mail) <= 0)
break;
ctx->cur_mail = ctx->mail;
str_printfa(ctx->cur_str, "* %u FETCH (",
ctx->cur_mail->seq);
ctx->first = TRUE;
ctx->line_finished = FALSE;
}
for (; ctx->cur_handler < count; ctx->cur_handler++) {
if (str_len(ctx->cur_str) > 0 &&
!handlers[ctx->cur_handler].buffered) {
/* first non-buffered handler.
flush the buffer. */
ctx->line_partial = TRUE;
if (imap_fetch_flush_buffer(ctx) < 0)
return -1;
}
i_assert(ctx->cur_input == NULL);
T_BEGIN {
const struct imap_fetch_context_handler *h =
&handlers[ctx->cur_handler];
ret = h->handler(ctx, ctx->cur_mail,
h->context);
} T_END;
if (ret == 0)
return 0;
if (ret < 0) {
if (ctx->cur_mail->expunged) {
/* not an error, just lost it. */
ctx->partial_fetch = TRUE;
if (imap_fetch_send_nil_reply(ctx) < 0)
return -1;
} else {
i_assert(ret < 0 ||
ctx->cont_handler != NULL);
return -1;
}
}
ctx->cont_handler = NULL;
ctx->cur_offset = 0;
if (ctx->cur_input != NULL)
i_stream_unref(&ctx->cur_input);
}
if (str_len(ctx->cur_str) > 0) {
/* no non-buffered handlers */
if (imap_fetch_flush_buffer(ctx) < 0)
return -1;
}
ctx->line_finished = TRUE;
ctx->line_partial = FALSE;
if (o_stream_send(client->output, ")\r\n", 3) < 0)
return -1;
client->last_output = ioloop_time;
ctx->cur_mail = NULL;
ctx->cur_handler = 0;
}
return 1;
}
int imap_fetch_more(struct imap_fetch_context *ctx)
{
int ret;
i_assert(ctx->client->output_lock == NULL ||
ctx->client->output_lock == ctx->cmd);
ret = imap_fetch_more_int(ctx);
if (ret < 0)
ctx->failed = TRUE;
if (ctx->line_partial) {
/* nothing can be sent until FETCH is finished */
ctx->client->output_lock = ctx->cmd;
}
return ret;
}
int imap_fetch_deinit(struct imap_fetch_context *ctx)
{
const struct imap_fetch_context_handler *handlers;
unsigned int i, count;
handlers = array_get(&ctx->handlers, &count);
for (i = 0; i < count; i++) {
if (handlers[i].want_deinit)
handlers[i].handler(ctx, NULL, handlers[i].context);
}
if (!ctx->line_finished) {
if (imap_fetch_flush_buffer(ctx) < 0)
ctx->failed = TRUE;
if (o_stream_send(ctx->client->output, ")\r\n", 3) < 0)
ctx->failed = TRUE;
}
str_free(&ctx->cur_str);
if (ctx->cur_input != NULL)
i_stream_unref(&ctx->cur_input);
if (ctx->mail != NULL)
mail_free(&ctx->mail);
mail_search_args_unref(&ctx->search_args);
if (ctx->search_ctx != NULL) {
if (mailbox_search_deinit(&ctx->search_ctx) < 0)
ctx->failed = TRUE;
}
if (ctx->all_headers_ctx != NULL)
mailbox_header_lookup_unref(&ctx->all_headers_ctx);
if (ctx->trans != NULL) {
/* even if something failed, we want to commit changes to
cache, as well as possible \Seen flag changes for FETCH
replies we returned so far. */
if (mailbox_transaction_commit(&ctx->trans) < 0)
ctx->failed = TRUE;
}
return ctx->failed ? -1 : 0;
}
static int fetch_body(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
const char *body;
if (mail_get_special(mail, MAIL_FETCH_IMAP_BODY, &body) < 0)
return -1;
if (ctx->first)
ctx->first = FALSE;
else {
if (o_stream_send(ctx->client->output, " ", 1) < 0)
return -1;
}
if (o_stream_send(ctx->client->output, "BODY (", 6) < 0 ||
o_stream_send_str(ctx->client->output, body) < 0 ||
o_stream_send(ctx->client->output, ")", 1) < 0)
return -1;
return 1;
}
static bool fetch_body_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args)
{
if (name[4] == '\0') {
ctx->fetch_data |= MAIL_FETCH_IMAP_BODY;
imap_fetch_add_handler(ctx, FALSE, FALSE, name,
"("BODY_NIL_REPLY")", fetch_body, NULL);
return TRUE;
}
return fetch_body_section_init(ctx, name, args);
}
static int fetch_bodystructure(struct imap_fetch_context *ctx,
struct mail *mail, void *context ATTR_UNUSED)
{
const char *bodystructure;
if (mail_get_special(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE,
&bodystructure) < 0)
return -1;
if (ctx->first)
ctx->first = FALSE;
else {
if (o_stream_send(ctx->client->output, " ", 1) < 0)
return -1;
}
if (o_stream_send(ctx->client->output, "BODYSTRUCTURE (", 15) < 0 ||
o_stream_send_str(ctx->client->output, bodystructure) < 0 ||
o_stream_send(ctx->client->output, ")", 1) < 0)
return -1;
return 1;
}
static bool
fetch_bodystructure_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
ctx->fetch_data |= MAIL_FETCH_IMAP_BODYSTRUCTURE;
imap_fetch_add_handler(ctx, FALSE, FALSE, name,
"("BODY_NIL_REPLY" NIL NIL NIL NIL)",
fetch_bodystructure, NULL);
return TRUE;
}
static int fetch_envelope(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
const char *envelope;
if (mail_get_special(mail, MAIL_FETCH_IMAP_ENVELOPE, &envelope) < 0)
return -1;
if (ctx->first)
ctx->first = FALSE;
else {
if (o_stream_send(ctx->client->output, " ", 1) < 0)
return -1;
}
if (o_stream_send(ctx->client->output, "ENVELOPE (", 10) < 0 ||
o_stream_send_str(ctx->client->output, envelope) < 0 ||
o_stream_send(ctx->client->output, ")", 1) < 0)
return -1;
return 1;
}
static bool
fetch_envelope_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
ctx->fetch_data |= MAIL_FETCH_IMAP_ENVELOPE;
imap_fetch_add_handler(ctx, FALSE, FALSE, name, ENVELOPE_NIL_REPLY,
fetch_envelope, NULL);
return TRUE;
}
static int fetch_flags(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
enum mail_flags flags;
const char *const *keywords;
flags = mail_get_flags(mail);
if (ctx->flags_update_seen && (flags & MAIL_SEEN) == 0) {
/* Add \Seen flag */
ctx->seen_flags_changed = TRUE;
flags |= MAIL_SEEN;
mail_update_flags(mail, MODIFY_ADD, MAIL_SEEN);
} else if (ctx->flags_show_only_seen_changes) {
return 1;
}
keywords = client_get_keyword_names(ctx->client, &ctx->tmp_keywords,
mail_get_keyword_indexes(mail));
str_append(ctx->cur_str, "FLAGS (");
imap_write_flags(ctx->cur_str, flags, keywords);
str_append(ctx->cur_str, ") ");
return 1;
}
static bool
fetch_flags_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
ctx->flags_have_handler = TRUE;
ctx->fetch_data |= MAIL_FETCH_FLAGS;
imap_fetch_add_handler(ctx, TRUE, FALSE, name, "()", fetch_flags, NULL);
return TRUE;
}
static int fetch_internaldate(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
time_t date;
if (mail_get_received_date(mail, &date) < 0)
return -1;
str_printfa(ctx->cur_str, "INTERNALDATE \"%s\" ",
imap_to_datetime(date));
return 1;
}
static bool
fetch_internaldate_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
ctx->fetch_data |= MAIL_FETCH_RECEIVED_DATE;
imap_fetch_add_handler(ctx, TRUE, FALSE, name,
"\"01-Jan-1970 00:00:00 +0000\"",
fetch_internaldate, NULL);
return TRUE;
}
static int fetch_modseq(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
uint64_t modseq;
modseq = mail_get_modseq(mail);
if (ctx->client->highest_fetch_modseq < modseq)
ctx->client->highest_fetch_modseq = modseq;
str_printfa(ctx->cur_str, "MODSEQ (%llu) ",
(unsigned long long)modseq);
return 1;
}
static bool
fetch_modseq_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
client_enable(ctx->client, MAILBOX_FEATURE_CONDSTORE);
imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL,
fetch_modseq, NULL);
return TRUE;
}
static int fetch_uid(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
str_printfa(ctx->cur_str, "UID %u ", mail->uid);
return 1;
}
static bool
fetch_uid_init(struct imap_fetch_context *ctx ATTR_UNUSED, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL, fetch_uid, NULL);
return TRUE;
}
static int fetch_guid(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
const char *value;
if (mail_get_special(mail, MAIL_FETCH_GUID, &value) < 0)
return -1;
str_append(ctx->cur_str, "X-GUID ");
imap_quote_append_string(ctx->cur_str, value, FALSE);
return 1;
}
static bool
fetch_guid_init(struct imap_fetch_context *ctx ATTR_UNUSED, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
imap_fetch_add_handler(ctx, TRUE, FALSE, name, "", fetch_guid, NULL);
return TRUE;
}
static int fetch_x_mailbox(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
const char *str;
if (mail_get_special(mail, MAIL_FETCH_MAILBOX_NAME, &str) < 0)
i_panic("mailbox name not returned");
str_append(ctx->cur_str, "X-MAILBOX ");
imap_quote_append_string(ctx->cur_str, str, FALSE);
return 1;
}
static bool
fetch_x_mailbox_init(struct imap_fetch_context *ctx ATTR_UNUSED,
const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL,
fetch_x_mailbox, NULL);
return TRUE;
}
static int fetch_x_savedate(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
time_t date;
if (mail_get_save_date(mail, &date) < 0)
return -1;
str_printfa(ctx->cur_str, "X-SAVEDATE \"%s\" ",
imap_to_datetime(date));
return 1;
}
static bool
fetch_x_savedate_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
ctx->fetch_data |= MAIL_FETCH_SAVE_DATE;
imap_fetch_add_handler(ctx, TRUE, FALSE, name,
"\"01-Jan-1970 00:00:00 +0000\"",
fetch_x_savedate, NULL);
return TRUE;
}
static const struct imap_fetch_handler
imap_fetch_default_handlers[] = {
{ "BODY", fetch_body_init },
{ "BODYSTRUCTURE", fetch_bodystructure_init },
{ "ENVELOPE", fetch_envelope_init },
{ "FLAGS", fetch_flags_init },
{ "INTERNALDATE", fetch_internaldate_init },
{ "MODSEQ", fetch_modseq_init },
{ "RFC822", fetch_rfc822_init },
{ "UID", fetch_uid_init },
{ "X-GUID", fetch_guid_init },
{ "X-MAILBOX", fetch_x_mailbox_init },
{ "X-SAVEDATE", fetch_x_savedate_init }
};
void imap_fetch_handlers_init(void)
{
i_array_init(&fetch_handlers, 32);
imap_fetch_handlers_register(imap_fetch_default_handlers,
N_ELEMENTS(imap_fetch_default_handlers));
}
void imap_fetch_handlers_deinit(void)
{
array_free(&fetch_handlers);
}