void index_mail_close(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; struct message_part *parts; /* If uid == 0 but seq != 0, we came here from saving a (non-mbox) message. If that happens, don't bother checking if anything should be cached since it was already checked. Also by now the transaction may have already been rollbacked and seq point to a nonexistent message. */ if (mail->mail.mail.uid != 0) { index_mail_cache_sizes(mail); index_mail_cache_dates(mail); } if (mail->data.parser_ctx != NULL) { if (message_parser_deinit(&mail->data.parser_ctx, &parts) < 0) { mail_set_cache_corrupted(_mail, MAIL_FETCH_MESSAGE_PARTS); } } if (mail->data.filter_stream != NULL) i_stream_unref(&mail->data.filter_stream); if (mail->data.stream != NULL) { mail->data.destroying_stream = TRUE; i_stream_unref(&mail->data.stream); i_assert(!mail->data.destroying_stream); } }
static int index_mail_parse_body_finish(struct index_mail *mail, enum index_cache_field field) { if (message_parser_deinit(&mail->data.parser_ctx, &mail->data.parts) < 0) { mail_set_cache_corrupted(&mail->mail.mail, MAIL_FETCH_MESSAGE_PARTS); mail->data.parsed_bodystructure = FALSE; return -1; } if (mail->data.no_caching) { /* if we're here because we aborted parsing, don't get any further or we may crash while generating output from incomplete data */ return 0; } (void)get_cached_msgpart_sizes(mail); index_mail_body_parsed_cache_flags(mail); index_mail_body_parsed_cache_message_parts(mail); index_mail_body_parsed_cache_bodystructure(mail, field); index_mail_cache_sizes(mail); index_mail_cache_dates(mail); return 0; }
static void virtual_mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field) { struct virtual_mail *vmail = (struct virtual_mail *)mail; if (virtual_mail_handle_lost(vmail) < 0) return; mail_set_cache_corrupted(vmail->backend_mail, field); }
static int fetch_stream_continue(struct imap_fetch_context *ctx) { struct imap_fetch_state *state = &ctx->state; const char *disconnect_reason; off_t ret; o_stream_set_max_buffer_size(ctx->client->output, 0); ret = o_stream_send_istream(ctx->client->output, state->cur_input); o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1); if (ret > 0) { state->cur_offset += ret; if (ctx->state.cur_stats_sizep != NULL) *ctx->state.cur_stats_sizep += ret; } if (state->cur_offset != state->cur_size) { /* unfinished */ if (state->cur_input->stream_errno != 0) { fetch_read_error(ctx, &disconnect_reason); client_disconnect(ctx->client, disconnect_reason); return -1; } if (!i_stream_have_bytes_left(state->cur_input)) { /* Input stream gave less data than expected */ i_error("read(%s): FETCH %s for mailbox %s UID %u " "got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, i_stream_get_name(state->cur_input), state->cur_human_name, mailbox_get_vname(state->cur_mail->box), state->cur_mail->uid, state->cur_offset, state->cur_size); mail_set_cache_corrupted(state->cur_mail, state->cur_size_field); client_disconnect(ctx->client, "FETCH failed"); return -1; } if (ret < 0) { /* client probably disconnected */ return -1; } o_stream_set_flush_pending(ctx->client->output, TRUE); return 0; } return 1; }
static int fetch_stream_continue(struct imap_fetch_context *ctx) { struct imap_fetch_state *state = &ctx->state; const char *disconnect_reason; uoff_t orig_input_offset = state->cur_input->v_offset; enum ostream_send_istream_result res; o_stream_set_max_buffer_size(ctx->client->output, 0); res = o_stream_send_istream(ctx->client->output, state->cur_input); o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1); if (ctx->state.cur_stats_sizep != NULL) { *ctx->state.cur_stats_sizep += state->cur_input->v_offset - orig_input_offset; } switch (res) { case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: if (state->cur_input->v_offset != state->cur_size) { /* Input stream gave less data than expected */ mail_set_cache_corrupted(state->cur_mail, state->cur_size_field, t_strdup_printf( "read(%s): FETCH %s got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, i_stream_get_name(state->cur_input), state->cur_human_name, state->cur_input->v_offset, state->cur_size)); client_disconnect(ctx->client, "FETCH failed"); return -1; } return 1; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: i_unreached(); case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: return 0; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: fetch_read_error(ctx, &disconnect_reason); client_disconnect(ctx->client, disconnect_reason); return -1; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: /* client disconnected */ return -1; } i_unreached(); }
static int fetch_stream_send_direct(struct imap_fetch_context *ctx) { off_t ret; o_stream_set_max_buffer_size(ctx->client->output, 0); ret = o_stream_send_istream(ctx->client->output, ctx->cur_input); o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1); if (ret < 0) return -1; ctx->cur_offset += ret; if (ctx->cur_append_eoh && ctx->cur_offset + 2 == ctx->cur_size) { /* Netscape missing EOH workaround. */ if (o_stream_send(ctx->client->output, "\r\n", 2) < 0) return -1; ctx->cur_offset += 2; ctx->cur_append_eoh = FALSE; } if (ctx->cur_offset != ctx->cur_size) { /* unfinished */ if (!i_stream_have_bytes_left(ctx->cur_input)) { /* Input stream gave less data than expected */ i_error("FETCH %s for mailbox %s UID %u " "got too little data (copying): " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, ctx->cur_name, mailbox_get_vname(ctx->mail->box), ctx->mail->uid, ctx->cur_offset, ctx->cur_size); mail_set_cache_corrupted(ctx->mail, ctx->cur_size_field); client_disconnect(ctx->client, "FETCH failed"); return -1; } o_stream_set_flush_pending(ctx->client->output, TRUE); return 0; } return 1; }
static void i_stream_mail_set_size_corrupted(struct mail_istream *mstream, size_t size) { uoff_t cur_size = mstream->istream.istream.v_offset + size; const char *str; char chr; if (mstream->expected_size < cur_size) { str = "smaller"; chr = '<'; } else { str = "larger"; chr = '>'; } mail_storage_set_critical(mstream->mail->box->storage, "Cached message size %s than expected " "(%"PRIuUOFF_T" %c %"PRIuUOFF_T")", str, mstream->expected_size, chr, cur_size); mail_set_cache_corrupted(mstream->mail, MAIL_FETCH_PHYSICAL_SIZE); mstream->istream.istream.stream_errno = EINVAL; }
int index_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mail_cache_field *cache_fields = mail->ibox->cache_fields; string_t *str; switch (field) { case MAIL_FETCH_IMAP_BODY: { unsigned int body_cache_field = cache_fields[MAIL_CACHE_IMAP_BODY].idx; unsigned int bodystructure_cache_field = cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; if (data->body != NULL) { *value_r = data->body; return 0; } /* 1) use plain-7bit-ascii flag if it exists 2) get BODY if it exists 3) get it using BODYSTRUCTURE if it exists 4) parse body structure, and save BODY/BODYSTRUCTURE depending on what we want cached */ str = str_new(mail->data_pool, 128); if ((mail->data.cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0 && get_cached_parts(mail)) { index_mail_get_plain_bodystructure(mail, str, FALSE); data->body = str_c(str); } else if (index_mail_cache_lookup_field(mail, str, body_cache_field) > 0) data->body = str_c(str); else if (index_mail_cache_lookup_field(mail, str, bodystructure_cache_field) > 0) { data->bodystructure = p_strdup(mail->data_pool, str_c(str)); str_truncate(str, 0); if (imap_body_parse_from_bodystructure( data->bodystructure, str)) data->body = str_c(str); else { /* broken, continue.. */ mail_set_cache_corrupted(_mail, MAIL_FETCH_IMAP_BODYSTRUCTURE); } } if (data->body == NULL) { str_free(&str); if (index_mail_parse_bodystructure(mail, MAIL_CACHE_IMAP_BODY) < 0) return -1; } i_assert(data->body != NULL); *value_r = data->body; return 0; } case MAIL_FETCH_IMAP_BODYSTRUCTURE: { unsigned int bodystructure_cache_field = cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; if (data->bodystructure != NULL) { *value_r = data->bodystructure; return 0; } str = str_new(mail->data_pool, 128); if ((mail->data.cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0 && get_cached_parts(mail)) { index_mail_get_plain_bodystructure(mail, str, TRUE); data->bodystructure = str_c(str); } else if (index_mail_cache_lookup_field(mail, str, bodystructure_cache_field) > 0) { data->bodystructure = str_c(str); } else { str_free(&str); if (index_mail_parse_bodystructure(mail, MAIL_CACHE_IMAP_BODYSTRUCTURE) < 0) return -1; } i_assert(data->bodystructure != NULL); *value_r = data->bodystructure; return 0; } case MAIL_FETCH_IMAP_ENVELOPE: if (data->envelope == NULL) { if (index_mail_headers_get_envelope(mail) < 0) return -1; } *value_r = data->envelope; return 0; case MAIL_FETCH_FROM_ENVELOPE: case MAIL_FETCH_UIDL_FILE_NAME: case MAIL_FETCH_UIDL_BACKEND: case MAIL_FETCH_SEARCH_SCORE: case MAIL_FETCH_GUID: case MAIL_FETCH_HEADER_MD5: *value_r = ""; return 0; case MAIL_FETCH_MAILBOX_NAME: *value_r = _mail->box->vname; return 0; default: i_unreached(); return -1; } }
static off_t imap_fetch_send(struct imap_fetch_context *ctx, struct ostream *output, struct istream *input, bool cr_skipped, uoff_t virtual_size, bool add_missing_eoh, bool *last_cr) { const unsigned char *msg; size_t i, size; uoff_t vsize_left, sent; off_t ret; unsigned char add; bool blocks = FALSE; /* go through the message data and insert CRs where needed. */ sent = 0; vsize_left = virtual_size; while (vsize_left > 0 && !blocks && i_stream_read_data(input, &msg, &size, 0) > 0) { add = '\0'; for (i = 0; i < size && vsize_left > 0; i++) { vsize_left--; if (msg[i] == '\n') { if ((i > 0 && msg[i-1] != '\r') || (i == 0 && !cr_skipped)) { /* missing CR */ add = '\r'; break; } } else if (msg[i] == '\0') { add = 128; break; } } if ((ret = o_stream_send(output, msg, i)) < 0) return -1; if ((uoff_t)ret < i) { add = '\0'; blocks = TRUE; } if (ret > 0) cr_skipped = msg[ret-1] == '\r'; i_stream_skip(input, ret); sent += ret; if (add != '\0') { if ((ret = o_stream_send(output, &add, 1)) < 0) return -1; if (ret == 0) blocks = TRUE; else { sent++; cr_skipped = add == '\r'; if (add == 128) i_stream_skip(input, 1); } } } if (input->stream_errno != 0) { fetch_read_error(ctx); return -1; } if (add_missing_eoh && sent + 2 == virtual_size) { /* Netscape missing EOH workaround. */ o_stream_set_max_buffer_size(output, (size_t)-1); if (o_stream_send(output, "\r\n", 2) < 0) return -1; sent += 2; } if ((uoff_t)sent != virtual_size && !blocks) { /* Input stream gave less data than we expected. Two choices here: either we fill the missing data with spaces or we disconnect the client. We shouldn't really ever get here. One reason is if mail was deleted from NFS server while we were reading it. Another is some temporary disk error. If we filled the missing data the client could cache it, and if it was just a temporary error the message would be permanently left corrupted in client's local cache. So, we disconnect the client and hope that next try works. */ i_error("FETCH %s for mailbox %s UID %u got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, ctx->cur_name, mailbox_get_vname(ctx->mail->box), ctx->mail->uid, (uoff_t)sent, virtual_size); mail_set_cache_corrupted(ctx->mail, ctx->cur_size_field); client_disconnect(ctx->client, "FETCH failed"); return -1; } *last_cr = cr_skipped; return sent; }