imap-fetch-body.c revision 9097ece766086128a8922388aa07bb6b253350ab
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
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 const char *const *fields;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen unsigned int uid;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic struct partial_cache last_partial = { 0, 0, 0, 0, { 0, 0, 0 } };
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void fetch_read_error(struct imap_fetch_context *ctx)
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen "failed to read message input: %m",
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen mailbox_get_vname(ctx->mail->box), ctx->mail->uid);
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenstatic int seek_partial(unsigned int select_counter, unsigned int uid,
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen struct partial_cache *partial, struct istream *stream,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (select_counter == partial->select_counter && uid == partial->uid &&
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen stream->v_offset == partial->physical_start &&
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* we can use the cache */
7db932bd4934cd967eeae643300aef5b91caeaeaTimo Sirainen memset(&partial->pos, 0, sizeof(partial->pos));
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_stream_seek(stream, partial->physical_start +
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen if (message_skip_virtual(stream, virtual_skip, &partial->pos,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic uoff_t get_send_size(const struct imap_fetch_body_data *body,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen return size <= body->max_size ? size : body->max_size;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenstatic const char *get_body_name(const struct imap_fetch_body_data *body)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen str_printfa(str, "<%"PRIuUOFF_T">", body->skip);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic string_t *get_prefix(struct imap_fetch_context *ctx,
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen str_printfa(str, " {%"PRIuUOFF_T"}\r\n", size);
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainenstatic off_t imap_fetch_send(struct imap_fetch_context *ctx,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen struct ostream *output, struct istream *input,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen const unsigned char *msg;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen unsigned char add;
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen /* go through the message data and insert CRs where needed. */
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen i_stream_read_data(input, &msg, &size, 0) > 0) {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen for (i = 0; i < size && vsize_left > 0; i++) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen (i == 0 && !cr_skipped)) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* missing CR */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if ((ret = o_stream_send(output, msg, i)) < 0)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if ((ret = o_stream_send(output, &add, 1)) < 0)
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);
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.
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.
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,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen mail_set_cache_corrupted(ctx->mail, ctx->cur_size_field);
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen client_disconnect(ctx->client, "FETCH failed");
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainenstatic int fetch_stream_send(struct imap_fetch_context *ctx)
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 o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1);
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen ctx->cur_input->v_offset - last_partial.physical_start;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic int fetch_stream_send_direct(struct imap_fetch_context *ctx)
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 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)
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 "got too little data (copying): "
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");
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen o_stream_set_flush_pending(ctx->client->output, TRUE);
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainenstatic int fetch_stream(struct imap_fetch_context *ctx,
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen if (size->physical_size == size->virtual_size &&
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 Sirainenstatic int fetch_data(struct imap_fetch_context *ctx,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen ctx->cur_size = get_send_size(body, size->virtual_size);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (message_skip_virtual(ctx->cur_input, body->skip, NULL,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen if (seek_partial(ctx->select_counter, ctx->cur_mail->uid,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int fetch_body(struct imap_fetch_context *ctx, struct mail *mail,
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)
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen /* BODY[] - fetch everything */
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen ctx->cur_size_field = MAIL_FETCH_VIRTUAL_SIZE;
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen /* BODY[HEADER] - fetch only header */
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen ctx->cur_size_field = MAIL_FETCH_MESSAGE_PARTS;
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen /* BODY[TEXT] - skip header */
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen i_stream_skip(ctx->cur_input, hdr_size.physical_size);
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen ctx->cur_size_field = MAIL_FETCH_VIRTUAL_SIZE;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainenstatic void header_filter_eoh(struct message_header_line *hdr,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int fetch_header_partial_from(struct imap_fetch_context *ctx,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* MIME, HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (strncmp(header_section, "HEADER.FIELDS ", 14) == 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen input = i_stream_create_header_filter(ctx->cur_input,
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen } else if (strncmp(header_section, "HEADER.FIELDS.NOT ", 18) == 0) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen input = i_stream_create_header_filter(ctx->cur_input,
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen i_error("BUG: Accepted invalid section from user: '%s'",
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (message_get_header_size(ctx->cur_input, &msg_size, NULL) < 0) {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen (ctx->client->workarounds & WORKAROUND_NETSCAPE_EOH) != 0) {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen /* Netscape 4.x doesn't like if end of headers line is
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainenfetch_body_header_partial(struct imap_fetch_context *ctx, struct mail *mail,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (mail_get_stream(mail, NULL, NULL, &ctx->cur_input) < 0)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return fetch_header_partial_from(ctx, body, body->section);
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainenfetch_body_header_fields(struct imap_fetch_context *ctx, struct mail *mail,
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen mailbox_header_lookup_unref(&body->header_ctx);
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen if (mail_get_header_stream(mail, body->header_ctx, &ctx->cur_input) < 0)
14175321ddb88619015866978c05a27786ca4814Timo Sirainen if (message_get_body_size(ctx->cur_input, &size, NULL) < 0) {
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/* 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 unsigned int num;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen while (*path >= '0' && *path <= '9' && part != NULL) {
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen /* get part number, we have already verified its validity */
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen if (part->flags & MESSAGE_PART_FLAG_MULTIPART) {
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen /* find the part */
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen /* only 1 allowed with non-multipart messages */
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) &&
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen /* if we continue inside the message/rfc822, skip this
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainenstatic int fetch_body_mime(struct imap_fetch_context *ctx, struct mail *mail,
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen if (part_find(mail, body, &part, §ion) < 0)
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen /* part doesn't exist */
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen string_t *str = get_prefix(ctx, body, (uoff_t)-1);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (mail_get_stream(mail, NULL, NULL, &ctx->cur_input) < 0)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen ctx->cur_size_field = MAIL_FETCH_MESSAGE_PARTS;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* fetch the whole section */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen i_stream_seek(ctx->cur_input, part->physical_pos +
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return fetch_data(ctx, body, &part->body_size);
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);
9511a40d933181045343110c8101b75887062aaeTimo Sirainen /* TEXT and HEADER are only for message/rfc822 parts */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(part->children != NULL && part->children->next == NULL);
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen i_stream_seek(ctx->cur_input, part->physical_pos +
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen return fetch_data(ctx, body, &part->body_size);
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);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen i_stream_seek(ctx->cur_input, part->physical_pos);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen return fetch_header_partial_from(ctx, body, section);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen i_error("BUG: Accepted invalid section from user: '%s'", body->section);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainenstatic bool fetch_body_header_fields_check(const char *section)
return FALSE;
section++;
return FALSE;
return FALSE;
return TRUE;
const char *section)
return FALSE;
MAIL_FETCH_STREAM_BODY)) != 0) {
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return FALSE;
*value = 0;
return FALSE;
return TRUE;
const char *prefix,
unsigned int args_count)
const char **arr;
size_t i;
for (i = 0; i < args_count; i++) {
return FALSE;
return TRUE;
const char *partial;
return FALSE;
return FALSE;
return FALSE;
if (p == NULL) {
return FALSE;
partial = p;
return FALSE;
return FALSE;
partial));
return FALSE;
const char *str;
const char *str;
const char *str;
return TRUE;
return TRUE;
return TRUE;
return TRUE;
return FALSE;