imap-fetch.c revision 2412873209ff658bc4bd20123af2d6162464c4ff
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen/* Copyright (c) 2002-2007 Dovecot authors, see the included COPYING file */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "common.h"
70cb37c37e4dce8f57cd3f882f7444e76b918befTimo Sirainen#include "array.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "buffer.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "istream.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "ostream.h"
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen#include "str.h"
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen#include "message-send.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "message-size.h"
ba3a54872528db0eae3f36e45592219965b9faf8Timo Sirainen#include "imap-date.h"
ba3a54872528db0eae3f36e45592219965b9faf8Timo Sirainen#include "commands.h"
ba3a54872528db0eae3f36e45592219965b9faf8Timo Sirainen#include "imap-fetch.h"
ba3a54872528db0eae3f36e45592219965b9faf8Timo Sirainen#include "imap-util.h"
ba3a54872528db0eae3f36e45592219965b9faf8Timo Sirainen
ba3a54872528db0eae3f36e45592219965b9faf8Timo Sirainen#include <stdlib.h>
ba3a54872528db0eae3f36e45592219965b9faf8Timo Sirainen
ba3a54872528db0eae3f36e45592219965b9faf8Timo Sirainen#define BODY_NIL_REPLY \
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen "\"text\" \"plain\" NIL NIL NIL \"7bit\" 0 0 NIL NIL NIL"
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainenconst struct imap_fetch_handler default_handlers[7];
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainenstatic buffer_t *fetch_handlers = NULL;
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainenstatic int imap_fetch_handler_cmp(const void *p1, const void *p2)
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen{
0a00890f8ec34ee08d0d391441fca36ed42d7a0cTimo Sirainen const struct imap_fetch_handler *h1 = p1, *h2 = p2;
0a00890f8ec34ee08d0d391441fca36ed42d7a0cTimo Sirainen
0a00890f8ec34ee08d0d391441fca36ed42d7a0cTimo Sirainen return strcmp(h1->name, h2->name);
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen}
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainenvoid imap_fetch_handlers_register(const struct imap_fetch_handler *handlers,
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen size_t count)
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen{
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen void *data;
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen size_t size;
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen if (fetch_handlers == NULL)
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen fetch_handlers = buffer_create_dynamic(default_pool, 128);
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen buffer_append(fetch_handlers, handlers, sizeof(*handlers) * count);
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen data = buffer_get_modifiable_data(fetch_handlers, &size);
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen qsort(data, size / sizeof(*handlers), sizeof(*handlers),
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen imap_fetch_handler_cmp);
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen}
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainenstatic int imap_fetch_handler_bsearch(const void *name_p, const void *handler_p)
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen{
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen const char *name = name_p;
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen const struct imap_fetch_handler *h = handler_p;
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen int i;
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen for (i = 0; h->name[i] != '\0'; i++) {
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen if (h->name[i] != name[i]) {
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen if (name[i] < 'A' || name[i] >= 'Z')
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen return -1;
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen return name[i] - h->name[i];
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen }
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen }
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen return name[i] < 'A' || name[i] >= 'Z' ? 0 : -1;
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen}
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainenbool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name,
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen const struct imap_arg **args)
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen{
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen const struct imap_fetch_handler *handler;
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen handler = bsearch(name, fetch_handlers->data,
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen fetch_handlers->used /
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen sizeof(struct imap_fetch_handler),
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen sizeof(struct imap_fetch_handler),
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen imap_fetch_handler_bsearch);
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen if (handler == NULL) {
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen client_send_command_error(ctx->cmd,
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen t_strconcat("Unknown command ", name, NULL));
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen return FALSE;
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen }
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen return handler->init(ctx, name, args);
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen}
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainenstruct imap_fetch_context *imap_fetch_init(struct client_command_context *cmd)
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen{
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen struct client *client = cmd->client;
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen struct imap_fetch_context *ctx;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (fetch_handlers == NULL) {
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen imap_fetch_handlers_register(default_handlers,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen N_ELEMENTS(default_handlers));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
91e4199476cb2add8143c18583fa57e1decfea88Timo Sirainen
0727e38ac12efb8963a339daf56255e2be1f29fcTimo Sirainen ctx = p_new(cmd->pool, struct imap_fetch_context, 1);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen ctx->client = client;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen ctx->cmd = cmd;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen ctx->box = client->mailbox;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen ctx->cur_str = str_new(default_pool, 8192);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen ctx->all_headers_buf = buffer_create_dynamic(cmd->pool, 128);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen p_array_init(&ctx->handlers, cmd->pool, 16);
08aea01ef9a9d20703e0fcf8618e6195c0037a44Timo Sirainen ctx->line_finished = TRUE;
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen return ctx;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen}
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen#undef imap_fetch_add_handler
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainenvoid imap_fetch_add_handler(struct imap_fetch_context *ctx,
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen bool buffered, bool want_deinit,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen const char *name, const char *nil_reply,
98a711be68ba64e1cabf8cacc150af44421e2ac9Timo Sirainen imap_fetch_handler_t *handler, void *context)
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen{
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* partially because of broken clients, but also partially because
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen it potentially can make client implementations faster, we have a
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen buffered parameter which basically means that the handler promises
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen to write the output in ctx->cur_str. The cur_str is then sent to
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen client before calling any non-buffered handlers.
70cb37c37e4dce8f57cd3f882f7444e76b918befTimo Sirainen
70cb37c37e4dce8f57cd3f882f7444e76b918befTimo Sirainen We try to keep the handler registration order the same as the
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen client requested them. This is especially useful to get UID
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen returned first, which some clients rely on..
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen const struct imap_fetch_context_handler *handlers;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen struct imap_fetch_context_handler h;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen unsigned int i, size;
08aea01ef9a9d20703e0fcf8618e6195c0037a44Timo Sirainen
08aea01ef9a9d20703e0fcf8618e6195c0037a44Timo Sirainen if (context == NULL) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* don't allow duplicate handlers */
08aea01ef9a9d20703e0fcf8618e6195c0037a44Timo Sirainen handlers = array_get(&ctx->handlers, &size);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen for (i = 0; i < size; i++) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (handlers[i].handler == handler &&
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen handlers[i].context == NULL)
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
98a711be68ba64e1cabf8cacc150af44421e2ac9Timo Sirainen memset(&h, 0, sizeof(h));
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen h.handler = handler;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen h.context = context;
70cb37c37e4dce8f57cd3f882f7444e76b918befTimo Sirainen h.buffered = buffered;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen h.want_deinit = want_deinit;
h.name = p_strdup(ctx->cmd->pool, name);
h.nil_reply = p_strdup(ctx->cmd->pool, nil_reply);
if (!buffered)
array_append(&ctx->handlers, &h, 1);
else {
array_insert(&ctx->handlers, ctx->buffered_handlers_count,
&h, 1);
ctx->buffered_handlers_count++;
}
}
void imap_fetch_begin(struct imap_fetch_context *ctx,
struct mail_search_arg *search_arg)
{
const void *null = NULL;
const void *data;
if (ctx->flags_update_seen) {
if (mailbox_is_readonly(ctx->box))
ctx->flags_update_seen = FALSE;
else if (!ctx->flags_have_handler) {
ctx->flags_show_only_seen_changes = TRUE;
(void)imap_fetch_init_handler(ctx, "FLAGS", NULL);
}
}
if (buffer_get_used_size(ctx->all_headers_buf) != 0 &&
((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY)) == 0)) {
buffer_append(ctx->all_headers_buf, &null, sizeof(null));
data = buffer_get_data(ctx->all_headers_buf, NULL);
ctx->all_headers_ctx =
mailbox_header_lookup_init(ctx->box, data);
}
if ((ctx->fetch_data &
(MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0)
ctx->fetch_data |= MAIL_FETCH_NUL_STATE;
ctx->trans = mailbox_transaction_begin(ctx->box,
MAILBOX_TRANSACTION_FLAG_HIDE);
ctx->select_counter = ctx->client->select_counter;
ctx->mail = mail_alloc(ctx->trans, ctx->fetch_data,
ctx->all_headers_ctx);
ctx->search_ctx =
mailbox_search_init(ctx->trans, NULL, search_arg, NULL);
}
static int imap_fetch_flush_buffer(struct imap_fetch_context *ctx)
{
const unsigned char *data;
size_t len;
data = str_data(ctx->cur_str);
len = str_len(ctx->cur_str);
if (len == 0)
return 0;
/* there's an extra space at the end if we added any fetch items
to buffer */
if (data[len-1] == ' ') {
len--;
ctx->first = FALSE;
}
if (o_stream_send(ctx->client->output, data, len) < 0)
return -1;
str_truncate(ctx->cur_str, 0);
return 0;
}
static int imap_fetch_send_nil_reply(struct imap_fetch_context *ctx)
{
const struct imap_fetch_context_handler *handler;
if (!ctx->first)
str_append_c(ctx->cur_str, ' ');
handler = array_idx(&ctx->handlers, ctx->cur_handler);
str_printfa(ctx->cur_str, "%s %s ",
handler->name, handler->nil_reply);
if (!handler->buffered) {
if (imap_fetch_flush_buffer(ctx) < 0)
return -1;
}
return 0;
}
int imap_fetch(struct imap_fetch_context *ctx)
{
struct client *client = ctx->client;
const struct imap_fetch_context_handler *handlers;
unsigned int count;
int ret;
if (ctx->cont_handler != NULL) {
ret = ctx->cont_handler(ctx);
if (ret == 0)
return 0;
if (ret < 0) {
if (ctx->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++;
}
/* assume initially that we're locking it */
i_assert(client->output_lock == NULL ||
client->output_lock == ctx->cmd);
client->output_lock = ctx->cmd;
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) {
if (!ctx->line_partial) {
/* last line was fully sent */
client->output_lock = NULL;
}
return ret;
}
}
if (ctx->cur_mail == NULL) {
if (ctx->cmd->cancel)
return 1;
if (ctx->cur_input != NULL)
i_stream_unref(&ctx->cur_input);
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;
}
t_push();
ret = handlers[ctx->cur_handler].
handler(ctx, ctx->cur_mail,
handlers[ctx->cur_handler].context);
t_pop();
if (ret == 0) {
if (!ctx->line_partial) {
/* last line was fully sent */
client->output_lock = NULL;
}
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 (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;
ctx->client->last_output = ioloop_time;
ctx->cur_mail = NULL;
ctx->cur_handler = 0;
}
return 1;
}
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);
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_deinit(&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,
"(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)",
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);
keywords = mail_get_keywords(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;
}
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-01-1970 00:00:00 +0000\"",
fetch_internaldate, 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;
}
const struct imap_fetch_handler default_handlers[7] = {
{ "BODY", fetch_body_init },
{ "BODYSTRUCTURE", fetch_bodystructure_init },
{ "ENVELOPE", fetch_envelope_init },
{ "FLAGS", fetch_flags_init },
{ "INTERNALDATE", fetch_internaldate_init },
{ "RFC822", fetch_rfc822_init },
{ "UID", fetch_uid_init }
};