imap-fetch.c revision 43d32cbe60fdaef2699d99f1ca259053e9350411
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen/* Copyright (C) 2002-2004 Timo Sirainen */
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen "\"text\" \"plain\" NIL NIL NIL \"7bit\" 0 0 NIL NIL NIL"
4bbb027a827d27d1816b8d60c5392cfd9c79e4bdTimo Sirainenconst struct imap_fetch_handler default_handlers[7];
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic int imap_fetch_handler_cmp(const void *p1, const void *p2)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen const struct imap_fetch_handler *h1 = p1, *h2 = p2;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenvoid imap_fetch_handlers_register(const struct imap_fetch_handler *handlers,
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen fetch_handlers = buffer_create_dynamic(default_pool, 128);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen buffer_append(fetch_handlers, handlers, sizeof(*handlers) * count);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen data = buffer_get_modifiable_data(fetch_handlers, &size);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen qsort(data, size / sizeof(*handlers), sizeof(*handlers),
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic int imap_fetch_handler_bsearch(const void *name_p, const void *handler_p)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen const struct imap_fetch_handler *h = handler_p;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return name[i] < 'A' || name[i] >= 'Z' ? 0 : -1;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name,
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainenstruct imap_fetch_context *imap_fetch_init(struct client_command_context *cmd)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen imap_fetch_handlers_register(default_handlers,
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen ctx = p_new(cmd->pool, struct imap_fetch_context, 1);
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen ctx->all_headers_buf = buffer_create_dynamic(cmd->pool, 128);
79fcd3f95a6266cc62ceaa753e56dd4456ab7c4bTimo Sirainenvoid imap_fetch_add_handler(struct imap_fetch_context *ctx,
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen /* partially because of broken clients, but also partially because
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen it potentially can make client implementations faster, we have a
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen buffered parameter which basically means that the handler promises
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen to write the output in ctx->cur_str. The cur_str is then sent to
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen client before calling any non-buffered handlers.
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen We try to keep the handler registration order the same as the
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen client requested them. This is especially useful to get UID
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen returned first, which some clients rely on..
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen const struct imap_fetch_context_handler *handlers;
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen unsigned int i, size;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* don't allow duplicate handlers */
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen for (i = 0; i < size; i++) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen memset(&h, 0, sizeof(h));
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen h.nil_reply = p_strdup(ctx->cmd->pool, nil_reply);
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen array_insert(&ctx->handlers, ctx->buffered_handlers_count,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenvoid imap_fetch_begin(struct imap_fetch_context *ctx,
4c07b08af30e1065f7022980b60474f229d8cadfTimo Sirainen (void)imap_fetch_init_handler(ctx, "FLAGS", NULL);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (buffer_get_used_size(ctx->all_headers_buf) != 0 &&
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER |
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen buffer_append(ctx->all_headers_buf, &null, sizeof(null));
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen data = buffer_get_data(ctx->all_headers_buf, NULL);
87132ebf759dc7fe1f4b8aeb68c7d44be26eba42Timo Sirainen (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen ctx->trans = mailbox_transaction_begin(ctx->box,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ctx->select_counter = ctx->client->select_counter;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen ctx->mail = mail_alloc(ctx->trans, ctx->fetch_data,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mailbox_search_init(ctx->trans, NULL, search_arg, NULL);
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainenstatic int imap_fetch_flush_buffer(struct imap_fetch_context *ctx)
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen const unsigned char *data;
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen /* there's an extra space at the end if we added any fetch items
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen if (o_stream_send(ctx->client->output, data, len) < 0)
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainenstatic int imap_fetch_send_nil_reply(struct imap_fetch_context *ctx)
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen const struct imap_fetch_context_handler *handler;
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen handler = array_idx(&ctx->handlers, ctx->cur_handler);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen const struct imap_fetch_context_handler *handlers;
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen unsigned int count;
d21f14c01d5546f4bf1b2cbb28ac1f00c24d952aTimo Sirainen /* not an error, just lost it. */
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen /* assume initially that we're locking it */
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen if (o_stream_get_buffer_used_size(client->output) >=
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen /* last line was fully sent */
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen for (; ctx->cur_handler < count; ctx->cur_handler++) {
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen /* first non-buffered handler.
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen flush the buffer. */
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen /* last line was fully sent */
d21f14c01d5546f4bf1b2cbb28ac1f00c24d952aTimo Sirainen /* not an error, just lost it. */
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen /* no non-buffered handlers */
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen if (o_stream_send(client->output, ")\r\n", 3) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenint imap_fetch_deinit(struct imap_fetch_context *ctx)
79fcd3f95a6266cc62ceaa753e56dd4456ab7c4bTimo Sirainen const struct imap_fetch_context_handler *handlers;
79fcd3f95a6266cc62ceaa753e56dd4456ab7c4bTimo Sirainen unsigned int i, count;
79fcd3f95a6266cc62ceaa753e56dd4456ab7c4bTimo Sirainen for (i = 0; i < count; i++) {
79fcd3f95a6266cc62ceaa753e56dd4456ab7c4bTimo Sirainen handlers[i].handler(ctx, NULL, handlers[i].context);
2ba4e9bedb0fa778dfbccec5370018b4d0040d9cTimo Sirainen if (o_stream_send(ctx->client->output, ")\r\n", 3) < 0)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (mailbox_search_deinit(&ctx->search_ctx) < 0)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mailbox_header_lookup_deinit(&ctx->all_headers_ctx);
5eca7b6535c3e74fb98aef5c34f096d9252a8f46Timo Sirainen /* even if something failed, we want to commit changes to
5eca7b6535c3e74fb98aef5c34f096d9252a8f46Timo Sirainen cache, as well as possible \Seen flag changes for FETCH
5eca7b6535c3e74fb98aef5c34f096d9252a8f46Timo Sirainen replies we returned so far. */
5eca7b6535c3e74fb98aef5c34f096d9252a8f46Timo Sirainen if (mailbox_transaction_commit(&ctx->trans, 0) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic int fetch_body(struct imap_fetch_context *ctx, struct mail *mail,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (mail_get_special(mail, MAIL_FETCH_IMAP_BODY, &body) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (o_stream_send(ctx->client->output, " ", 1) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (o_stream_send(ctx->client->output, "BODY (", 6) < 0 ||
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen o_stream_send_str(ctx->client->output, body) < 0 ||
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen o_stream_send(ctx->client->output, ")", 1) < 0)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic bool fetch_body_init(struct imap_fetch_context *ctx, const char *name,
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen imap_fetch_add_handler(ctx, FALSE, FALSE, name,
4c07b08af30e1065f7022980b60474f229d8cadfTimo Sirainen return fetch_body_section_init(ctx, name, args);
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainenstatic int fetch_bodystructure(struct imap_fetch_context *ctx,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (mail_get_special(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (o_stream_send(ctx->client->output, " ", 1) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (o_stream_send(ctx->client->output, "BODYSTRUCTURE (", 15) < 0 ||
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen o_stream_send_str(ctx->client->output, bodystructure) < 0 ||
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen o_stream_send(ctx->client->output, ")", 1) < 0)
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainenfetch_bodystructure_init(struct imap_fetch_context *ctx, const char *name,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ctx->fetch_data |= MAIL_FETCH_IMAP_BODYSTRUCTURE;
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen imap_fetch_add_handler(ctx, FALSE, FALSE, name,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic int fetch_envelope(struct imap_fetch_context *ctx, struct mail *mail,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (mail_get_special(mail, MAIL_FETCH_IMAP_ENVELOPE, &envelope) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (o_stream_send(ctx->client->output, " ", 1) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (o_stream_send(ctx->client->output, "ENVELOPE (", 10) < 0 ||
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen o_stream_send_str(ctx->client->output, envelope) < 0 ||
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen o_stream_send(ctx->client->output, ")", 1) < 0)
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainenfetch_envelope_init(struct imap_fetch_context *ctx, const char *name,
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen imap_fetch_add_handler(ctx, FALSE, FALSE, name,
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)",
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic int fetch_flags(struct imap_fetch_context *ctx, struct mail *mail,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen const char *const *keywords;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (ctx->flags_update_seen && (flags & MAIL_SEEN) == 0) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* Add \Seen flag */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen mail_update_flags(mail, MODIFY_ADD, MAIL_SEEN);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen } else if (ctx->flags_show_only_seen_changes) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen imap_write_flags(ctx->cur_str, flags, keywords);
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainenfetch_flags_init(struct imap_fetch_context *ctx, const char *name,
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen imap_fetch_add_handler(ctx, TRUE, FALSE, name, "()", fetch_flags, NULL);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic int fetch_internaldate(struct imap_fetch_context *ctx, struct mail *mail,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen str_printfa(ctx->cur_str, "INTERNALDATE \"%s\" ",
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainenfetch_internaldate_init(struct imap_fetch_context *ctx, const char *name,
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen imap_fetch_add_handler(ctx, TRUE, FALSE, name,
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen "\"01-01-1970 00:00:00 +0000\"",
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic int fetch_uid(struct imap_fetch_context *ctx, struct mail *mail,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen str_printfa(ctx->cur_str, "UID %u ", mail->uid);
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainenfetch_uid_init(struct imap_fetch_context *ctx ATTR_UNUSED, const char *name,
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL, fetch_uid, NULL);
4bbb027a827d27d1816b8d60c5392cfd9c79e4bdTimo Sirainenconst struct imap_fetch_handler default_handlers[7] = {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen { "BODYSTRUCTURE", fetch_bodystructure_init },