imap-fetch-body.c revision 9097ece766086128a8922388aa07bb6b253350ab
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "imap-common.h"
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen#include "buffer.h"
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen#include "str.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "strescape.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "istream.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "ostream.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "istream-header-filter.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "message-parser.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "message-send.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "mail-storage.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "imap-parser.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "imap-fetch.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include <stdlib.h>
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include <ctype.h>
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include <unistd.h>
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainenstruct imap_fetch_body_data {
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct imap_fetch_body_data *next;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct mailbox_header_lookup_ctx *header_ctx;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen const char *section; /* NOTE: always uppercased */
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen uoff_t skip, max_size; /* if you don't want max_size,
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen set it to (uoff_t)-1 */
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen const char *const *fields;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen size_t fields_count;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int skip_set:1;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen unsigned int peek:1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen};
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstruct partial_cache {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int select_counter;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen unsigned int uid;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen uoff_t physical_start;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen bool cr_skipped;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen struct message_size pos;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen};
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic struct partial_cache last_partial = { 0, 0, 0, 0, { 0, 0, 0 } };
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void fetch_read_error(struct imap_fetch_context *ctx)
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen{
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen errno = ctx->cur_input->stream_errno;
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen i_error("FETCH for mailbox %s UID %u "
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen "failed to read message input: %m",
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen mailbox_get_vname(ctx->mail->box), ctx->mail->uid);
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen}
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenstatic int seek_partial(unsigned int select_counter, unsigned int uid,
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen struct partial_cache *partial, struct istream *stream,
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen uoff_t virtual_skip, bool *cr_skipped_r)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (select_counter == partial->select_counter && uid == partial->uid &&
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen stream->v_offset == partial->physical_start &&
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen virtual_skip >= partial->pos.virtual_size) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* we can use the cache */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen virtual_skip -= partial->pos.virtual_size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen } else {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen partial->select_counter = select_counter;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen partial->uid = uid;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen partial->physical_start = stream->v_offset;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen partial->cr_skipped = FALSE;
7db932bd4934cd967eeae643300aef5b91caeaeaTimo Sirainen memset(&partial->pos, 0, sizeof(partial->pos));
7db932bd4934cd967eeae643300aef5b91caeaeaTimo Sirainen }
74066569545099304b20e790df7c261883d1746bTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_stream_seek(stream, partial->physical_start +
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen partial->pos.physical_size);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen if (message_skip_virtual(stream, virtual_skip, &partial->pos,
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen partial->cr_skipped, cr_skipped_r) < 0)
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen return -1;
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen partial->cr_skipped = FALSE;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return 0;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic uoff_t get_send_size(const struct imap_fetch_body_data *body,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen uoff_t max_size)
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen{
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen uoff_t size;
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen if (body->skip >= max_size)
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen return 0;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen size = max_size - body->skip;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen return size <= body->max_size ? size : body->max_size;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen}
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenstatic const char *get_body_name(const struct imap_fetch_body_data *body)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen string_t *str;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen str = t_str_new(128);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen str_printfa(str, "BODY[%s]", body->section);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if (body->skip_set)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen str_printfa(str, "<%"PRIuUOFF_T">", body->skip);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return str_c(str);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen}
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic string_t *get_prefix(struct imap_fetch_context *ctx,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const struct imap_fetch_body_data *body,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen uoff_t size)
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen{
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen string_t *str;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen str = t_str_new(128);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (ctx->first)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ctx->first = FALSE;
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen else
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen str_append_c(str, ' ');
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen str_append(str, get_body_name(body));
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if (size != (uoff_t)-1)
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen str_printfa(str, " {%"PRIuUOFF_T"}\r\n", size);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen else
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen str_append(str, " NIL");
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen return str;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen}
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainenstatic off_t imap_fetch_send(struct imap_fetch_context *ctx,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen struct ostream *output, struct istream *input,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen bool cr_skipped, uoff_t virtual_size,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen bool add_missing_eoh, bool *last_cr)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen{
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen const unsigned char *msg;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen size_t i, size;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen uoff_t vsize_left, sent;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen off_t ret;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen unsigned char add;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen bool blocks = FALSE;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen /* go through the message data and insert CRs where needed. */
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen sent = 0; vsize_left = virtual_size;
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen while (vsize_left > 0 && !blocks &&
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen i_stream_read_data(input, &msg, &size, 0) > 0) {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen add = '\0';
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen for (i = 0; i < size && vsize_left > 0; i++) {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen vsize_left--;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (msg[i] == '\n') {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((i > 0 && msg[i-1] != '\r') ||
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen (i == 0 && !cr_skipped)) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* missing CR */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen add = '\r';
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen break;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen } else if (msg[i] == '\0') {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen add = 128;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen break;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if ((ret = o_stream_send(output, msg, i)) < 0)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return -1;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if ((uoff_t)ret < i) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen add = '\0';
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen blocks = TRUE;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (ret > 0)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen cr_skipped = msg[ret-1] == '\r';
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen i_stream_skip(input, ret);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen sent += ret;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (add != '\0') {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if ((ret = o_stream_send(output, &add, 1)) < 0)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return -1;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (ret == 0)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen blocks = TRUE;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen else {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen sent++;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen cr_skipped = add == '\r';
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (add == 128)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_stream_skip(input, 1);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen if (input->stream_errno != 0) {
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen fetch_read_error(ctx);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return -1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (add_missing_eoh && sent + 2 == virtual_size) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* Netscape missing EOH workaround. */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen o_stream_set_max_buffer_size(output, (size_t)-1);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (o_stream_send(output, "\r\n", 2) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen sent += 2;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen if ((uoff_t)sent != virtual_size && !blocks) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* Input stream gave less data than we expected. Two choices
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen here: either we fill the missing data with spaces or we
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen disconnect the client.
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen We shouldn't really ever get here. One reason is if mail
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen was deleted from NFS server while we were reading it.
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen Another is some temporary disk error.
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen If we filled the missing data the client could cache it,
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen and if it was just a temporary error the message would be
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen permanently left corrupted in client's local cache. So, we
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen disconnect the client and hope that next try works. */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen i_error("FETCH %s for mailbox %s UID %u got too little data: "
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen "%"PRIuUOFF_T" vs %"PRIuUOFF_T, ctx->cur_name,
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen mailbox_get_vname(ctx->mail->box), ctx->mail->uid,
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen (uoff_t)sent, virtual_size);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen mail_set_cache_corrupted(ctx->mail, ctx->cur_size_field);
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen client_disconnect(ctx->client, "FETCH failed");
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen return -1;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen *last_cr = cr_skipped;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return sent;
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen}
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainenstatic int fetch_stream_send(struct imap_fetch_context *ctx)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen off_t ret;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen o_stream_set_max_buffer_size(ctx->client->output, 4096);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen ret = imap_fetch_send(ctx, ctx->client->output, ctx->cur_input,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen ctx->skip_cr, ctx->cur_size - ctx->cur_offset,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen ctx->cur_append_eoh, &ctx->skip_cr);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1);
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen if (ret < 0)
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen return -1;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen ctx->cur_offset += ret;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen if (ctx->update_partial) {
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen last_partial.cr_skipped = ctx->skip_cr != 0;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen last_partial.pos.physical_size =
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen ctx->cur_input->v_offset - last_partial.physical_start;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen last_partial.pos.virtual_size += ret;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen }
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return ctx->cur_offset == ctx->cur_size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic int fetch_stream_send_direct(struct imap_fetch_context *ctx)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen off_t ret;
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen o_stream_set_max_buffer_size(ctx->client->output, 0);
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen ret = o_stream_send_istream(ctx->client->output, ctx->cur_input);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if (ret < 0)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return -1;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen ctx->cur_offset += ret;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (ctx->cur_append_eoh && ctx->cur_offset + 2 == ctx->cur_size) {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen /* Netscape missing EOH workaround. */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (o_stream_send(ctx->client->output, "\r\n", 2) < 0)
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen return -1;
548e394330621952db0f03dd667b70184c4a37b6Timo Sirainen ctx->cur_offset += 2;
548e394330621952db0f03dd667b70184c4a37b6Timo Sirainen ctx->cur_append_eoh = FALSE;
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (ctx->cur_offset != ctx->cur_size) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* unfinished */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (!i_stream_have_bytes_left(ctx->cur_input)) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* Input stream gave less data than expected */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen i_error("FETCH %s for mailbox %s UID %u "
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen "got too little data (copying): "
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen "%"PRIuUOFF_T" vs %"PRIuUOFF_T,
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen ctx->cur_name, mailbox_get_vname(ctx->mail->box),
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen ctx->mail->uid, ctx->cur_offset, ctx->cur_size);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen client_disconnect(ctx->client, "FETCH failed");
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return -1;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen }
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen o_stream_set_flush_pending(ctx->client->output, TRUE);
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen return 0;
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen }
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen return 1;
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen}
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainenstatic int fetch_stream(struct imap_fetch_context *ctx,
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen const struct message_size *size)
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen{
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen struct istream *input;
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen if (size->physical_size == size->virtual_size &&
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen ctx->cur_mail->has_no_nuls) {
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen /* no need to kludge with CRs, we can use sendfile() */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen input = i_stream_create_limit(ctx->cur_input, ctx->cur_size);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen i_stream_unref(&ctx->cur_input);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen ctx->cur_input = input;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen ctx->cont_handler = fetch_stream_send_direct;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen } else {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen ctx->cont_handler = fetch_stream_send;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen }
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen return ctx->cont_handler(ctx);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen}
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenstatic int fetch_data(struct imap_fetch_context *ctx,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen const struct imap_fetch_body_data *body,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen const struct message_size *size)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen{
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen string_t *str;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen ctx->cur_name = p_strconcat(ctx->cmd->pool,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen "[", body->section, "]", NULL);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen ctx->cur_size = get_send_size(body, size->virtual_size);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen str = get_prefix(ctx, body, ctx->cur_size);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (o_stream_send(ctx->client->output,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen str_data(str), str_len(str)) < 0)
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen return -1;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (!ctx->update_partial) {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (message_skip_virtual(ctx->cur_input, body->skip, NULL,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen FALSE, &ctx->skip_cr) < 0) {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen fetch_read_error(ctx);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen return -1;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen }
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen } else {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen if (seek_partial(ctx->select_counter, ctx->cur_mail->uid,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen &last_partial, ctx->cur_input, body->skip,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen &ctx->skip_cr) < 0) {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen fetch_read_error(ctx);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen return -1;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen }
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen }
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return fetch_stream(ctx, size);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen}
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int fetch_body(struct imap_fetch_context *ctx, struct mail *mail,
0611067f385a37773800225256dcd5cf6aa34212Timo Sirainen const struct imap_fetch_body_data *body)
0611067f385a37773800225256dcd5cf6aa34212Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const struct message_size *fetch_size;
0611067f385a37773800225256dcd5cf6aa34212Timo Sirainen struct istream *input;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct message_size hdr_size, body_size;
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen if (body->section[0] == '\0') {
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen if (mail_get_stream(mail, NULL, NULL, &input) < 0 ||
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mail_get_virtual_size(mail, &body_size.virtual_size) < 0 ||
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mail_get_physical_size(mail, &body_size.physical_size) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen } else {
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen if (mail_get_stream(mail, &hdr_size,
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen body->section[0] == 'H' ? NULL : &body_size,
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen &input) < 0)
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen return -1;
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen }
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen ctx->cur_input = input;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_stream_ref(ctx->cur_input);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ctx->update_partial = TRUE;
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen switch (body->section[0]) {
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen case '\0':
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen /* BODY[] - fetch everything */
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen fetch_size = &body_size;
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen ctx->cur_size_field = MAIL_FETCH_VIRTUAL_SIZE;
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen break;
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen case 'H':
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen /* BODY[HEADER] - fetch only header */
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen fetch_size = &hdr_size;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen ctx->cur_size_field = MAIL_FETCH_MESSAGE_PARTS;
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen break;
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen case 'T':
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen /* BODY[TEXT] - skip header */
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen i_stream_skip(ctx->cur_input, hdr_size.physical_size);
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen fetch_size = &body_size;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen ctx->cur_size_field = MAIL_FETCH_VIRTUAL_SIZE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen break;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen default:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_unreached();
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return fetch_data(ctx, body, fetch_size);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainenstatic void header_filter_eoh(struct message_header_line *hdr,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen bool *matched ATTR_UNUSED,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct imap_fetch_context *ctx)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen if (hdr != NULL && hdr->eoh)
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen ctx->cur_have_eoh = TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int fetch_header_partial_from(struct imap_fetch_context *ctx,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const struct imap_fetch_body_data *body,
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen const char *header_section)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct message_size msg_size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct istream *input;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen uoff_t old_offset;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* MIME, HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen ctx->cur_have_eoh = FALSE;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (strncmp(header_section, "HEADER.FIELDS ", 14) == 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen input = i_stream_create_header_filter(ctx->cur_input,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen HEADER_FILTER_INCLUDE,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen body->fields,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen body->fields_count,
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen header_filter_eoh, ctx);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen } else if (strncmp(header_section, "HEADER.FIELDS.NOT ", 18) == 0) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen input = i_stream_create_header_filter(ctx->cur_input,
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen HEADER_FILTER_EXCLUDE,
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen body->fields,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen body->fields_count,
f4bbeadda12fbd7c219063db68f3e78646d83c2cTimo Sirainen header_filter_eoh, ctx);
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen } else {
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen i_error("BUG: Accepted invalid section from user: '%s'",
abe8754852e70763e92f74caabbcc13d0917714cTimo Sirainen header_section);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen }
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen i_stream_unref(&ctx->cur_input);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen ctx->cur_input = input;
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen ctx->update_partial = FALSE;
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen old_offset = ctx->cur_input->v_offset;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (message_get_header_size(ctx->cur_input, &msg_size, NULL) < 0) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen fetch_read_error(ctx);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen return -1;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen }
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen i_stream_seek(ctx->cur_input, old_offset);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen if (!ctx->cur_have_eoh &&
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen (ctx->client->workarounds & WORKAROUND_NETSCAPE_EOH) != 0) {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen /* Netscape 4.x doesn't like if end of headers line is
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen missing. */
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen msg_size.virtual_size += 2;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ctx->cur_append_eoh = TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen ctx->cur_size_field = 0;
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen return fetch_data(ctx, body, &msg_size);
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen}
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainenstatic int
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainenfetch_body_header_partial(struct imap_fetch_context *ctx, struct mail *mail,
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen const struct imap_fetch_body_data *body)
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (mail_get_stream(mail, NULL, NULL, &ctx->cur_input) < 0)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return -1;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen i_stream_ref(ctx->cur_input);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen ctx->update_partial = FALSE;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return fetch_header_partial_from(ctx, body, body->section);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen}
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainenstatic int
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainenfetch_body_header_fields(struct imap_fetch_context *ctx, struct mail *mail,
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen struct imap_fetch_body_data *body)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen{
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen struct message_size size;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen uoff_t old_offset;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen if (mail == NULL) {
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen /* deinit */
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen mailbox_header_lookup_unref(&body->header_ctx);
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen return 0;
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen }
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen if (mail_get_header_stream(mail, body->header_ctx, &ctx->cur_input) < 0)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return -1;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen i_stream_ref(ctx->cur_input);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen ctx->update_partial = FALSE;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen old_offset = ctx->cur_input->v_offset;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen if (message_get_body_size(ctx->cur_input, &size, NULL) < 0) {
14175321ddb88619015866978c05a27786ca4814Timo Sirainen fetch_read_error(ctx);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen return -1;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen }
14175321ddb88619015866978c05a27786ca4814Timo Sirainen i_stream_seek(ctx->cur_input, old_offset);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen /* FIXME: We'll just always add the end of headers line now.
14175321ddb88619015866978c05a27786ca4814Timo Sirainen ideally mail-storage would have a way to tell us if it exists. */
14175321ddb88619015866978c05a27786ca4814Timo Sirainen size.virtual_size += 2;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen ctx->cur_append_eoh = TRUE;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen ctx->cur_size_field = 0;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen return fetch_data(ctx, body, &size);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen}
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen/* Find message_part for section (eg. 1.3.4) */
14175321ddb88619015866978c05a27786ca4814Timo Sirainenstatic int part_find(struct mail *mail, const struct imap_fetch_body_data *body,
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen const struct message_part **part_r, const char **section_r)
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen{
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen const struct message_part *part;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen const char *path;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen unsigned int num;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen if (mail_get_parts(mail, &part) < 0)
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen return -1;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen path = body->section;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen while (*path >= '0' && *path <= '9' && part != NULL) {
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen /* get part number, we have already verified its validity */
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen num = 0;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen while (*path != '\0' && *path != '.') {
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen i_assert(*path >= '0' && *path <= '9');
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen num = num*10 + (*path - '0');
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen path++;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen }
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (*path == '.')
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen path++;
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen if (part->flags & MESSAGE_PART_FLAG_MULTIPART) {
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen /* find the part */
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen part = part->children;
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen for (; num > 1 && part != NULL; num--)
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen part = part->next;
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen } else {
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen /* only 1 allowed with non-multipart messages */
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen if (num != 1)
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen part = NULL;
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen }
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (part != NULL &&
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) &&
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen (*path >= '0' && *path <= '9')) {
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen /* if we continue inside the message/rfc822, skip this
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen body part */
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen part = part->children;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen }
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen }
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen *part_r = part;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen *section_r = path;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen return 0;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen}
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainenstatic int fetch_body_mime(struct imap_fetch_context *ctx, struct mail *mail,
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen const struct imap_fetch_body_data *body)
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen{
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen const struct message_part *part;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen const char *section;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen if (part_find(mail, body, &part, &section) < 0)
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen return -1;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen if (part == NULL) {
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen /* part doesn't exist */
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen string_t *str = get_prefix(ctx, body, (uoff_t)-1);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (o_stream_send(ctx->client->output,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen str_data(str), str_len(str)) < 0)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return -1;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return 1;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen }
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (mail_get_stream(mail, NULL, NULL, &ctx->cur_input) < 0)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return -1;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen i_stream_ref(ctx->cur_input);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen ctx->update_partial = TRUE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen ctx->cur_size_field = MAIL_FETCH_MESSAGE_PARTS;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (*section == '\0') {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* fetch the whole section */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen i_stream_seek(ctx->cur_input, part->physical_pos +
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen part->header_size.physical_size);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return fetch_data(ctx, body, &part->body_size);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen }
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (strcmp(section, "MIME") == 0) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* fetch section's MIME header */
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen i_stream_seek(ctx->cur_input, part->physical_pos);
9511a40d933181045343110c8101b75887062aaeTimo Sirainen return fetch_data(ctx, body, &part->header_size);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
9511a40d933181045343110c8101b75887062aaeTimo Sirainen /* TEXT and HEADER are only for message/rfc822 parts */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0) {
9511a40d933181045343110c8101b75887062aaeTimo Sirainen string_t *str = get_prefix(ctx, body, 0);
9511a40d933181045343110c8101b75887062aaeTimo Sirainen if (o_stream_send(ctx->client->output,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen str_data(str), str_len(str)) < 0)
9511a40d933181045343110c8101b75887062aaeTimo Sirainen return -1;
9511a40d933181045343110c8101b75887062aaeTimo Sirainen return 1;
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen }
9511a40d933181045343110c8101b75887062aaeTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(part->children != NULL && part->children->next == NULL);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen part = part->children;
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen if (strcmp(section, "TEXT") == 0) {
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen i_stream_seek(ctx->cur_input, part->physical_pos +
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen part->header_size.physical_size);
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen return fetch_data(ctx, body, &part->body_size);
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen }
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (strcmp(section, "HEADER") == 0) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* all headers */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen i_stream_seek(ctx->cur_input, part->physical_pos);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen return fetch_data(ctx, body, &part->header_size);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen }
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (strncmp(section, "HEADER", 6) == 0) {
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen i_stream_seek(ctx->cur_input, part->physical_pos);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen return fetch_header_partial_from(ctx, body, section);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen }
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen i_error("BUG: Accepted invalid section from user: '%s'", body->section);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return 1;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen}
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainenstatic bool fetch_body_header_fields_check(const char *section)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (*section++ != '(')
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
if (*section == ')')
return FALSE; /* has to be at least one field */
while (*section != '\0' && *section != ')') {
if (*section == '(')
return FALSE;
section++;
}
if (*section++ != ')')
return FALSE;
if (*section != '\0')
return FALSE;
return TRUE;
}
static bool fetch_body_header_fields_init(struct imap_fetch_context *ctx,
struct imap_fetch_body_data *body,
const char *section)
{
const char *const *arr, *name;
if (!fetch_body_header_fields_check(section))
return FALSE;
name = get_body_name(body);
if ((ctx->fetch_data & (MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY)) != 0) {
/* we'll need to open the file anyway, don't try to get the
headers from cache. */
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_body_header_partial, body);
return TRUE;
}
for (arr = body->fields; *arr != NULL; arr++) {
const char *hdr = p_strdup(ctx->cmd->pool, *arr);
array_append(&ctx->all_headers, &hdr, 1);
}
body->header_ctx = mailbox_header_lookup_init(ctx->box, body->fields);
imap_fetch_add_handler(ctx, FALSE, TRUE, name, "NIL",
fetch_body_header_fields, body);
return TRUE;
}
static bool fetch_body_section_name_init(struct imap_fetch_context *ctx,
struct imap_fetch_body_data *body)
{
const char *name, *section = body->section;
name = get_body_name(body);
if (*section == '\0') {
ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY;
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_body, body);
return TRUE;
}
if (strcmp(section, "TEXT") == 0) {
ctx->fetch_data |= MAIL_FETCH_STREAM_BODY;
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_body, body);
return TRUE;
}
if (strncmp(section, "HEADER", 6) == 0) {
/* exact header matches could be cached */
if (section[6] == '\0') {
ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER;
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_body, body);
return TRUE;
}
if (strncmp(section, "HEADER.FIELDS ", 14) == 0 &&
fetch_body_header_fields_init(ctx, body, section+14))
return TRUE;
if (strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0 &&
fetch_body_header_fields_check(section+18)) {
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_body_header_partial, body);
return TRUE;
}
} else if (*section >= '0' && *section <= '9') {
ctx->fetch_data |= MAIL_FETCH_STREAM_BODY |
MAIL_FETCH_MESSAGE_PARTS;
while ((*section >= '0' && *section <= '9') ||
*section == '.') section++;
if (*section == '\0' ||
strcmp(section, "MIME") == 0 ||
strcmp(section, "TEXT") == 0 ||
strcmp(section, "HEADER") == 0 ||
(strncmp(section, "HEADER.FIELDS ", 14) == 0 &&
fetch_body_header_fields_check(section+14)) ||
(strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0 &&
fetch_body_header_fields_check(section+18))) {
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_body_mime, body);
return TRUE;
}
}
client_send_command_error(ctx->cmd,
"Invalid BODY[..] parameter: Unknown or broken section");
return FALSE;
}
/* Parse next digits in string into integer. Returns FALSE if the integer
becomes too big and wraps. */
static bool read_uoff_t(const char **p, uoff_t *value)
{
uoff_t prev;
*value = 0;
while (**p >= '0' && **p <= '9') {
prev = *value;
*value = *value * 10 + (**p - '0');
if (*value < prev)
return FALSE;
(*p)++;
}
return TRUE;
}
static bool body_section_build(struct imap_fetch_context *ctx,
struct imap_fetch_body_data *body,
const char *prefix,
const struct imap_arg *args,
unsigned int args_count)
{
string_t *str;
const char **arr;
size_t i;
str = str_new(ctx->cmd->pool, 128);
str_append(str, prefix);
str_append(str, " (");
/* @UNSAFE: NULL-terminated list of headers */
arr = p_new(ctx->cmd->pool, const char *, args_count + 1);
for (i = 0; i < args_count; i++) {
if (args[i].type != IMAP_ARG_ATOM &&
args[i].type != IMAP_ARG_STRING) {
client_send_command_error(ctx->cmd,
"Invalid BODY[..] parameter: "
"Header list contains non-strings");
return FALSE;
}
if (i != 0)
str_append_c(str, ' ');
arr[i] = t_str_ucase(IMAP_ARG_STR(&args[i]));
if (args[i].type == IMAP_ARG_ATOM)
str_append(str, arr[i]);
else {
str_append_c(str, '"');
str_append(str, str_escape(arr[i]));
str_append_c(str, '"');
}
}
str_append_c(str, ')');
qsort(arr, args_count, sizeof(*arr), i_strcasecmp_p);
body->fields = arr;
body->fields_count = args_count;
body->section = str_c(str);
return TRUE;
}
bool fetch_body_section_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args)
{
struct imap_fetch_body_data *body;
const char *partial;
const char *p = name + 4;
body = p_new(ctx->cmd->pool, struct imap_fetch_body_data, 1);
body->max_size = (uoff_t)-1;
if (strncmp(p, ".PEEK", 5) == 0) {
body->peek = TRUE;
p += 5;
} else {
ctx->flags_update_seen = TRUE;
}
if (*p != '[') {
client_send_command_error(ctx->cmd,
"Invalid BODY[..] parameter: Missing '['");
return FALSE;
}
if ((*args)[0].type == IMAP_ARG_LIST) {
/* BODY[HEADER.FIELDS.. (headers list)] */
if ((*args)[1].type != IMAP_ARG_ATOM ||
IMAP_ARG_STR(&(*args)[1])[0] != ']') {
client_send_command_error(ctx->cmd,
"Invalid BODY[..] parameter: Missing ']'");
return FALSE;
}
if (!body_section_build(ctx, body, p+1,
IMAP_ARG_LIST_ARGS(&(*args)[0]),
IMAP_ARG_LIST_COUNT(&(*args)[0])))
return FALSE;
p = IMAP_ARG_STR(&(*args)[1]);
*args += 2;
} else {
/* no headers list */
body->section = p+1;
p = strchr(body->section, ']');
if (p == NULL) {
client_send_command_error(ctx->cmd,
"Invalid BODY[..] parameter: Missing ']'");
return FALSE;
}
body->section = p_strdup_until(ctx->cmd->pool,
body->section, p);
}
if (*++p == '<') {
/* <start.end> */
partial = p;
p++;
body->skip_set = TRUE;
if (!read_uoff_t(&p, &body->skip) || body->skip > OFF_T_MAX) {
/* wrapped */
client_send_command_error(ctx->cmd,
"Invalid BODY[..] parameter: "
"Too big partial start");
return FALSE;
}
if (*p == '.') {
p++;
if (!read_uoff_t(&p, &body->max_size) ||
body->max_size > OFF_T_MAX) {
/* wrapped */
client_send_command_error(ctx->cmd,
"Invalid BODY[..] parameter: "
"Too big partial end");
return FALSE;
}
}
if (*p != '>') {
client_send_command_error(ctx->cmd,
t_strdup_printf("Invalid BODY[..] parameter: "
"Missing '>' in '%s'",
partial));
return FALSE;
}
}
return fetch_body_section_name_init(ctx, body);
}
static int fetch_rfc822_size(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
uoff_t size;
if (mail_get_virtual_size(mail, &size) < 0)
return -1;
str_printfa(ctx->cur_str, "RFC822.SIZE %"PRIuUOFF_T" ", size);
return 1;
}
static int fetch_rfc822(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
struct message_size size;
const char *str;
struct istream *input;
if (mail_get_stream(mail, NULL, NULL, &input) < 0 ||
mail_get_virtual_size(mail, &size.virtual_size) < 0 ||
mail_get_physical_size(mail, &size.physical_size) < 0)
return -1;
ctx->cur_input = input;
i_stream_ref(ctx->cur_input);
ctx->update_partial = FALSE;
if (ctx->cur_offset == 0) {
str = t_strdup_printf(" RFC822 {%"PRIuUOFF_T"}\r\n",
size.virtual_size);
if (ctx->first) {
str++; ctx->first = FALSE;
}
if (o_stream_send_str(ctx->client->output, str) < 0)
return -1;
}
ctx->cur_name = "RFC822";
ctx->cur_size = size.virtual_size;
ctx->cur_size_field = MAIL_FETCH_VIRTUAL_SIZE;
return fetch_stream(ctx, &size);
}
static int fetch_rfc822_header(struct imap_fetch_context *ctx,
struct mail *mail, void *context ATTR_UNUSED)
{
struct message_size hdr_size;
const char *str;
if (mail_get_stream(mail, &hdr_size, NULL, &ctx->cur_input) < 0)
return -1;
i_stream_ref(ctx->cur_input);
ctx->update_partial = FALSE;
str = t_strdup_printf(" RFC822.HEADER {%"PRIuUOFF_T"}\r\n",
hdr_size.virtual_size);
if (ctx->first) {
str++; ctx->first = FALSE;
}
if (o_stream_send_str(ctx->client->output, str) < 0)
return -1;
ctx->cur_name = "RFC822.HEADER";
ctx->cur_size = hdr_size.virtual_size;
ctx->cur_size_field = MAIL_FETCH_MESSAGE_PARTS;
return fetch_stream(ctx, &hdr_size);
}
static int fetch_rfc822_text(struct imap_fetch_context *ctx, struct mail *mail,
void *context ATTR_UNUSED)
{
struct message_size hdr_size, body_size;
const char *str;
if (mail_get_stream(mail, &hdr_size, &body_size, &ctx->cur_input) < 0)
return -1;
i_stream_ref(ctx->cur_input);
ctx->update_partial = FALSE;
str = t_strdup_printf(" RFC822.TEXT {%"PRIuUOFF_T"}\r\n",
body_size.virtual_size);
if (ctx->first) {
str++; ctx->first = FALSE;
}
if (o_stream_send_str(ctx->client->output, str) < 0)
return -1;
i_stream_seek(ctx->cur_input, hdr_size.physical_size);
ctx->cur_name = "RFC822.TEXT";
ctx->cur_size = body_size.virtual_size;
ctx->cur_size_field = MAIL_FETCH_VIRTUAL_SIZE;
return fetch_stream(ctx, &body_size);
}
bool fetch_rfc822_init(struct imap_fetch_context *ctx, const char *name,
const struct imap_arg **args ATTR_UNUSED)
{
if (name[6] == '\0') {
ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY;
ctx->flags_update_seen = TRUE;
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_rfc822, NULL);
return TRUE;
}
if (strcmp(name+6, ".SIZE") == 0) {
ctx->fetch_data |= MAIL_FETCH_VIRTUAL_SIZE;
imap_fetch_add_handler(ctx, TRUE, FALSE, name, "0",
fetch_rfc822_size, NULL);
return TRUE;
}
if (strcmp(name+6, ".HEADER") == 0) {
ctx->fetch_data |= MAIL_FETCH_STREAM_HEADER;
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_rfc822_header, NULL);
return TRUE;
}
if (strcmp(name+6, ".TEXT") == 0) {
ctx->fetch_data |= MAIL_FETCH_STREAM_BODY;
ctx->flags_update_seen = TRUE;
imap_fetch_add_handler(ctx, FALSE, FALSE, name, "NIL",
fetch_rfc822_text, NULL);
return TRUE;
}
client_send_command_error(ctx->cmd, t_strconcat(
"Unknown parameter ", name, NULL));
return FALSE;
}