imap-fetch.c revision 4c9745326e5e53447eb308ad613e08766703292b
2e37d45867d081db150ab78dad303b9077aea24fTimo Sirainen/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include "imap-common.h"
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include "array.h"
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen#include "buffer.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "istream.h"
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen#include "ostream.h"
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen#include "str.h"
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen#include "message-send.h"
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen#include "message-size.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "imap-date.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "imap-utf7.h"
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen#include "mail-search-build.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "imap-commands.h"
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen#include "imap-quote.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "imap-fetch.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "imap-util.h"
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen#include <stdlib.h>
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include <ctype.h>
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen#define BODY_NIL_REPLY \
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen "\"text\" \"plain\" NIL NIL NIL \"7bit\" 0 0"
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#define ENVELOPE_NIL_REPLY \
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)"
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainenstatic ARRAY_DEFINE(fetch_handlers, struct imap_fetch_handler);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainenstatic int imap_fetch_handler_cmp(const struct imap_fetch_handler *h1,
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen const struct imap_fetch_handler *h2)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen{
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen return strcmp(h1->name, h2->name);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenvoid imap_fetch_handlers_register(const struct imap_fetch_handler *handlers,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen size_t count)
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen{
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen array_append(&fetch_handlers, handlers, count);
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen array_sort(&fetch_handlers, imap_fetch_handler_cmp);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen}
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainenstatic int
4c892b0d94c5b1d6853dbe8e0b38059ea5b08ecaTimo Sirainenimap_fetch_handler_bsearch(const char *name, const struct imap_fetch_handler *h)
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen{
abc79eec93e58e0152cd1d483f37be66c26811b9Timo Sirainen return strcmp(name, h->name);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen}
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainenbool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name,
4c892b0d94c5b1d6853dbe8e0b38059ea5b08ecaTimo Sirainen const struct imap_arg **args)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen{
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen const struct imap_fetch_handler *handler;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen const char *lookup_name, *p;
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen for (p = name; i_isalnum(*p) || *p == '-'; p++) ;
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen lookup_name = t_strdup_until(name, p);
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen handler = array_bsearch(&fetch_handlers, lookup_name,
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen imap_fetch_handler_bsearch);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen if (handler == NULL) {
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen client_send_command_error(ctx->cmd,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen t_strconcat("Unknown parameter ", name, NULL));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return FALSE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return handler->init(ctx, name, args);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstruct imap_fetch_context *
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenimap_fetch_init(struct client_command_context *cmd, struct mailbox *box)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct client *client = cmd->client;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct imap_fetch_context *ctx;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx = p_new(cmd->pool, struct imap_fetch_context, 1);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->client = client;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->cmd = cmd;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->box = box;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->cur_str = str_new(default_pool, 8192);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen p_array_init(&ctx->all_headers, cmd->pool, 64);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen p_array_init(&ctx->handlers, cmd->pool, 16);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen p_array_init(&ctx->tmp_keywords, cmd->pool,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen client->keywords.announce_count + 8);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->line_finished = TRUE;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return ctx;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenbool imap_fetch_add_changed_since(struct imap_fetch_context *ctx,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen uint64_t modseq)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct mail_search_arg *search_arg;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen search_arg = p_new(ctx->search_args->pool, struct mail_search_arg, 1);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen search_arg->type = SEARCH_MODSEQ;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen search_arg->value.modseq =
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen p_new(ctx->cmd->pool, struct mail_search_modseq, 1);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen search_arg->value.modseq->modseq = modseq + 1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen search_arg->next = ctx->search_args->args->next;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen ctx->search_args->args->next = search_arg;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return imap_fetch_init_handler(ctx, "MODSEQ", NULL);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#undef imap_fetch_add_handler
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenvoid imap_fetch_add_handler(struct imap_fetch_context *ctx,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen bool buffered, bool want_deinit,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *name, const char *nil_reply,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen imap_fetch_handler_t *handler, void *context)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* partially because of broken clients, but also partially because
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen it potentially can make client implementations faster, we have a
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen buffered parameter which basically means that the handler promises
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen to write the output in ctx->cur_str. The cur_str is then sent to
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen client before calling any non-buffered handlers.
65889a7d8c059e2feb159aee1633b847aba84831Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen We try to keep the handler registration order the same as the
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen client requested them. This is especially useful to get UID
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen returned first, which some clients rely on..
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen */
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen const struct imap_fetch_context_handler *ctx_handler;
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen struct imap_fetch_context_handler h;
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen if (context == NULL) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* don't allow duplicate handlers */
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen array_foreach(&ctx->handlers, ctx_handler) {
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen if (ctx_handler->handler == handler &&
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen ctx_handler->context == NULL)
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen }
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen }
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen memset(&h, 0, sizeof(h));
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen h.handler = handler;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen h.context = context;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen h.buffered = buffered;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen h.want_deinit = want_deinit;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen h.name = p_strdup(ctx->cmd->pool, name);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen h.nil_reply = p_strdup(ctx->cmd->pool, nil_reply);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen if (!buffered)
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen array_append(&ctx->handlers, &h, 1);
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen else {
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen array_insert(&ctx->handlers, ctx->buffered_handlers_count,
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen &h, 1);
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen ctx->buffered_handlers_count++;
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen }
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen}
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainenstatic void
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainenexpunges_drop_known(struct imap_fetch_context *ctx,
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen struct mailbox_transaction_context *trans,
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen ARRAY_TYPE(seq_range) *expunged_uids)
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen{
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen struct mailbox_status status;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen struct mail *mail;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen const uint32_t *seqs, *uids;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen unsigned int i, count;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen seqs = array_get(ctx->qresync_sample_seqset, &count);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen uids = array_idx(ctx->qresync_sample_uidset, 0);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen i_assert(array_count(ctx->qresync_sample_uidset) == count);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen i_assert(count > 0);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen mailbox_get_open_status(ctx->box, STATUS_MESSAGES, &status);
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen mail = mail_alloc(trans, 0, NULL);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* FIXME: we could do removals from the middle as well */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen for (i = 0; i < count && seqs[i] <= status.messages; i++) {
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen mail_set_seq(mail, seqs[i]);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (uids[i] != mail->uid)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen break;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if (i > 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen seq_range_array_remove_range(expunged_uids, 1, uids[i-1]);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_free(&mail);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic int get_expunges_fallback(struct imap_fetch_context *ctx,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const ARRAY_TYPE(seq_range) *uid_filter_arr,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen ARRAY_TYPE(seq_range) *expunged_uids)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mailbox_transaction_context *trans;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mail_search_args *search_args;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mail_search_context *search_ctx;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mail *mail;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const struct seq_range *uid_filter;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mailbox_status status;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen unsigned int i, count;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen uint32_t next_uid;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int ret = 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a2150da2dc906c26a26219cbefbe28a119aafee2Timo Sirainen uid_filter = array_get(uid_filter_arr, &count);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_assert(count > 0);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i = 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen next_uid = uid_filter[0].seq1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* search UIDs only in given range */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen search_args = mail_search_build_init();
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen search_args->args = p_new(search_args->pool, struct mail_search_arg, 1);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen search_args->args->type = SEARCH_UIDSET;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_array_init(&search_args->args->value.seqset, count);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen array_append_array(&search_args->args->value.seqset, uid_filter_arr);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen trans = mailbox_transaction_begin(ctx->box, 0);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_search_args_unref(&search_args);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen while (mailbox_search_next(search_ctx, &mail)) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (mail->uid == next_uid) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (next_uid < uid_filter[i].seq2)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen next_uid++;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen else if (++i < count)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen next_uid = uid_filter[i].seq1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen else
88b0427d90f1d3c2c5fb3171e53a505c46e2c39dTimo Sirainen break;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen } else {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* next_uid .. mail->uid-1 are expunged */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_assert(mail->uid > next_uid);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen while (mail->uid > uid_filter[i].seq2) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen seq_range_array_add_range(expunged_uids,
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen next_uid,
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen uid_filter[i].seq2);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen i++;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen i_assert(i < count);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen next_uid = uid_filter[i].seq1;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen }
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen if (next_uid != mail->uid) {
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen seq_range_array_add_range(expunged_uids,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen next_uid,
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen mail->uid - 1);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (uid_filter[i].seq2 != mail->uid)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen next_uid = mail->uid + 1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else if (++i < count)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen next_uid = uid_filter[i].seq1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen else
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen break;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (i < count) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i_assert(next_uid <= uid_filter[i].seq2);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen seq_range_array_add_range(expunged_uids, next_uid,
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen uid_filter[i].seq2);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i++;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen for (; i < count; i++) {
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen seq_range_array_add_range(expunged_uids, uid_filter[i].seq1,
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen uid_filter[i].seq2);
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen }
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen mailbox_get_open_status(ctx->box, STATUS_UIDNEXT, &status);
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen seq_range_array_remove_range(expunged_uids, status.uidnext,
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen (uint32_t)-1);
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen if (mailbox_search_deinit(&search_ctx) < 0)
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen ret = -1;
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen if (ret == 0 && ctx->qresync_sample_seqset != NULL &&
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen array_is_created(ctx->qresync_sample_seqset))
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen expunges_drop_known(ctx, trans, expunged_uids);
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen (void)mailbox_transaction_commit(&trans);
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen return ret;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen}
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainenstatic int
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenimap_fetch_send_vanished(struct imap_fetch_context *ctx)
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen{
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen const struct mail_search_arg *uidarg = ctx->search_args->args;
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen const struct mail_search_arg *modseqarg = uidarg->next;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen const ARRAY_TYPE(seq_range) *uid_filter = &uidarg->value.seqset;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen uint64_t modseq = modseqarg->value.modseq->modseq - 1;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen ARRAY_TYPE(seq_range) expunged_uids_range;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen string_t *str;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen int ret = 0;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen i_array_init(&expunged_uids_range, array_count(uid_filter));
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (!mailbox_get_expunged_uids(ctx->box, modseq, uid_filter, &expunged_uids_range)) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* return all expunged UIDs */
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen if (get_expunges_fallback(ctx, uid_filter,
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen &expunged_uids_range) < 0) {
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen array_clear(&expunged_uids_range);
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen ret = -1;
72276c90ac2a38c9db7b4458acd3a2f5b61892bbTimo Sirainen }
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (array_count(&expunged_uids_range) > 0) {
7afde4b6c600f86ef6f742ea3b01640075ce16a2Timo Sirainen str = str_new(default_pool, 128);
7afde4b6c600f86ef6f742ea3b01640075ce16a2Timo Sirainen str_append(str, "* VANISHED (EARLIER) ");
5e751dbaecf7c337abc149f328c4a13ee5c15134Timo Sirainen imap_write_seq_range(str, &expunged_uids_range);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen str_append(str, "\r\n");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen o_stream_send(ctx->client->output, str_data(str), str_len(str));
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen str_free(&str);
72276c90ac2a38c9db7b4458acd3a2f5b61892bbTimo Sirainen }
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen array_free(&expunged_uids_range);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return ret;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen}
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainenint imap_fetch_begin(struct imap_fetch_context *ctx)
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen{
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen const void *data;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen if (ctx->send_vanished) {
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen if (imap_fetch_send_vanished(ctx) < 0) {
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen ctx->failed = TRUE;
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen return -1;
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen }
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen }
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen if (ctx->flags_update_seen) {
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen if (mailbox_is_readonly(ctx->box))
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen ctx->flags_update_seen = FALSE;
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen else if (!ctx->flags_have_handler) {
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen ctx->flags_show_only_seen_changes = TRUE;
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen (void)imap_fetch_init_handler(ctx, "FLAGS", NULL);
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen }
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (array_count(&ctx->all_headers) > 0 &&
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER |
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen MAIL_FETCH_STREAM_BODY)) == 0)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen (void)array_append_space(&ctx->all_headers);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen data = array_idx(&ctx->all_headers, 0);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->all_headers_ctx =
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen mailbox_header_lookup_init(ctx->box, data);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if ((ctx->fetch_data &
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->fetch_data |= MAIL_FETCH_NUL_STATE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx->trans = mailbox_transaction_begin(ctx->box,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen MAILBOX_TRANSACTION_FLAG_HIDE |
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen MAILBOX_TRANSACTION_FLAG_REFRESH);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->select_counter = ctx->client->select_counter;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* Delayed uidset -> seqset conversion. VANISHED needs the uidset. */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen mail_search_args_init(ctx->search_args, ctx->box, TRUE,
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen &ctx->cmd->client->search_saved_uidset);
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen ctx->search_ctx =
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen mailbox_search_init(ctx->trans, ctx->search_args, NULL,
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen ctx->fetch_data, ctx->all_headers_ctx);
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen return 0;
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen}
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainenstatic int imap_fetch_flush_buffer(struct imap_fetch_context *ctx)
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen{
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen const unsigned char *data;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen size_t len;
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen data = str_data(ctx->cur_str);
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen len = str_len(ctx->cur_str);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen if (len == 0)
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen return 0;
b16ee3cbbcd18cb86f2f73b5cc163ebfb995ffafTimo Sirainen
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen /* there's an extra space at the end if we added any fetch items
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen to buffer */
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen if (data[len-1] == ' ') {
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen len--;
b16ee3cbbcd18cb86f2f73b5cc163ebfb995ffafTimo Sirainen ctx->first = FALSE;
b16ee3cbbcd18cb86f2f73b5cc163ebfb995ffafTimo Sirainen }
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen if (o_stream_send(ctx->client->output, data, len) < 0)
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen return -1;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen str_truncate(ctx->cur_str, 0);
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen return 0;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen}
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainenstatic int imap_fetch_send_nil_reply(struct imap_fetch_context *ctx)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen{
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen const struct imap_fetch_context_handler *handler;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen if (!ctx->first)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen str_append_c(ctx->cur_str, ' ');
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen handler = array_idx(&ctx->handlers, ctx->cur_handler);
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen str_printfa(ctx->cur_str, "%s %s ",
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen handler->name, handler->nil_reply);
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen if (!handler->buffered) {
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen if (imap_fetch_flush_buffer(ctx) < 0)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen return -1;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen }
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen return 0;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen}
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainenstatic int imap_fetch_more_int(struct imap_fetch_context *ctx)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen{
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen struct client *client = ctx->client;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen const struct imap_fetch_context_handler *handlers;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen unsigned int count;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen int ret;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen if (ctx->cont_handler != NULL) {
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen ret = ctx->cont_handler(ctx);
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen if (ret == 0)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen return 0;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo 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->cur_mail))
break;
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 *handler;
array_foreach(&ctx->handlers, handler) {
if (handler->want_deinit)
handler->handler(ctx, NULL, handler->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);
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)
{
(void)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);
str_append_c(ctx->cur_str, ' ');
return 1;
}
static bool
fetch_guid_init(struct imap_fetch_context *ctx ATTR_UNUSED, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
ctx->fetch_data |= MAIL_FETCH_GUID;
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 *name;
string_t *mutf7_name;
if (mail_get_special(mail, MAIL_FETCH_MAILBOX_NAME, &name) < 0)
i_panic("mailbox name not returned");
mutf7_name = t_str_new(strlen(name)*2);
if (imap_utf8_to_utf7(name, mutf7_name) < 0)
i_panic("FETCH: Mailbox name not UTF-8: %s", name);
str_append(ctx->cur_str, "X-MAILBOX ");
imap_quote_append_string(ctx->cur_str, str_c(mutf7_name), FALSE);
str_append_c(ctx->cur_str, ' ');
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_real_uid(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
str_printfa(ctx->cur_str, "X-REAL-UID %u ",
mail_get_real_mail(mail)->uid);
return 1;
}
static bool
fetch_x_real_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_x_real_uid, 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-REAL-UID", fetch_x_real_uid_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);
}