static void astream_parse_header(struct attachment_istream *astream, struct message_header_line *hdr) { if (!hdr->continued) { stream_add_data(astream, hdr->name, hdr->name_len); stream_add_data(astream, hdr->middle, hdr->middle_len); } stream_add_data(astream, hdr->value, hdr->value_len); if (!hdr->no_newline) { if (hdr->crlf_newline) stream_add_data(astream, "\r\n", 2); else stream_add_data(astream, "\n", 1); } if (hdr->continues) { hdr->use_full_value = TRUE; return; } if (strcasecmp(hdr->name, "Content-Type") == 0) parse_content_type(astream, hdr); else if (strcasecmp(hdr->name, "Content-Disposition") == 0) parse_content_disposition(astream, hdr); }
static void astream_add_body(struct attachment_istream *astream, const struct message_block *block) { struct attachment_istream_part *part = &astream->part; buffer_t *part_buf; size_t new_size; switch (part->state) { case MAIL_ATTACHMENT_STATE_NO: stream_add_data(astream, block->data, block->size); break; case MAIL_ATTACHMENT_STATE_MAYBE: /* we'll write data to in-memory buffer until we reach attachment min_size */ if (part->part_buf == NULL) { part->part_buf = buffer_create_dynamic(default_pool, astream->set.min_size); } part_buf = part->part_buf; new_size = part_buf->used + block->size; if (new_size < astream->set.min_size) { buffer_append(part_buf, block->data, block->size); break; } /* attachment is large enough. we'll first copy the buffered data from memory to temp file */ if (astream_open_output(astream) < 0) { /* failed, fallback to just saving it inline */ part->state = MAIL_ATTACHMENT_STATE_NO; stream_add_data(astream, part_buf->data, part_buf->used); stream_add_data(astream, block->data, block->size); break; } part->state = MAIL_ATTACHMENT_STATE_YES; astream_try_base64_decode(part, part_buf->data, part_buf->used); hash_format_loop(astream->set.hash_format, part_buf->data, part_buf->used); o_stream_nsend(part->temp_output, part_buf->data, part_buf->used); buffer_set_used_size(part_buf, 0); /* fall through to write the new data to temp file */ case MAIL_ATTACHMENT_STATE_YES: astream_try_base64_decode(part, block->data, block->size); hash_format_loop(astream->set.hash_format, block->data, block->size); o_stream_nsend(part->temp_output, block->data, block->size); break; } }
static int astream_end_of_part(struct attachment_istream *astream) { struct attachment_istream_part *part = &astream->part; int ret = 0; /* MIME part changed. we're now parsing the end of a boundary, possibly followed by message epilogue */ switch (part->state) { case MAIL_ATTACHMENT_STATE_NO: break; case MAIL_ATTACHMENT_STATE_MAYBE: /* MIME part wasn't large enough to be an attachment */ if (part->part_buf != NULL) { stream_add_data(astream, part->part_buf->data, part->part_buf->used); ret = part->part_buf->used > 0 ? 1 : 0; } break; case MAIL_ATTACHMENT_STATE_YES: if (astream_part_finish(astream) < 0) ret = -1; break; } part->state = MAIL_ATTACHMENT_STATE_NO; astream_part_reset(astream); return ret; }
static int astream_end_of_part(struct attachment_istream *astream, const char **error_r) { struct attachment_istream_part *part = &astream->part; size_t old_size; int ret = 0; /* MIME part changed. we're now parsing the end of a boundary, possibly followed by message epilogue */ switch (part->state) { case MAIL_ATTACHMENT_STATE_NO: break; case MAIL_ATTACHMENT_STATE_MAYBE: /* MIME part wasn't large enough to be an attachment */ if (part->part_buf != NULL) { stream_add_data(astream, part->part_buf->data, part->part_buf->used); ret = part->part_buf->used > 0 ? 1 : 0; } break; case MAIL_ATTACHMENT_STATE_YES: old_size = astream->istream.pos - astream->istream.skip; if (astream_part_finish(astream, error_r) < 0) ret = -1; else { /* finished base64 may have added a few more trailing bytes to the stream */ ret = astream->istream.pos - astream->istream.skip - old_size; } break; } part->state = MAIL_ATTACHMENT_STATE_NO; astream_part_reset(astream); return ret; }
static int astream_decode_base64(struct attachment_istream *astream) { struct attachment_istream_part *part = &astream->part; buffer_t *extra_buf = NULL; struct istream *input, *base64_input; struct ostream *output; const unsigned char *data; size_t size; ssize_t ret; buffer_t *buf; int outfd; bool failed = FALSE; if (part->base64_bytes < astream->set.min_size || part->temp_output->offset > part->base64_bytes + BASE64_ATTACHMENT_MAX_EXTRA_BYTES) { /* only a small part of the MIME part is base64-encoded. */ return -1; } if (part->base64_line_blocks == 0) { /* only one line of base64 */ part->base64_line_blocks = part->cur_base64_blocks; i_assert(part->base64_line_blocks > 0); } /* decode base64 data and write it to another temp file */ outfd = astream->set.open_temp_fd(astream->context); if (outfd == -1) return -1; buf = buffer_create_dynamic(default_pool, 1024); input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE); base64_input = i_stream_create_limit(input, part->base64_bytes); output = o_stream_create_fd_file(outfd, 0, FALSE); o_stream_cork(output); hash_format_reset(astream->set.hash_format); size_t bytes_needed = 1; while ((ret = i_stream_read_bytes(base64_input, &data, &size, bytes_needed)) > 0) { buffer_set_used_size(buf, 0); if (base64_decode(data, size, &size, buf) < 0) { i_error("istream-attachment: BUG: " "Attachment base64 data unexpectedly broke"); failed = TRUE; break; } i_stream_skip(base64_input, size); o_stream_nsend(output, buf->data, buf->used); hash_format_loop(astream->set.hash_format, buf->data, buf->used); bytes_needed = i_stream_get_data_size(base64_input) + 1; } if (ret != -1) { i_assert(failed); } else if (base64_input->stream_errno != 0) { i_error("istream-attachment: read(%s) failed: %s", i_stream_get_name(base64_input), i_stream_get_error(base64_input)); failed = TRUE; } if (o_stream_nfinish(output) < 0) { i_error("istream-attachment: write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); failed = TRUE; } buffer_free(&buf); i_stream_unref(&base64_input); o_stream_unref(&output); if (input->v_offset != part->temp_output->offset && !failed) { /* write the rest of the data to the message stream */ extra_buf = buffer_create_dynamic(default_pool, 1024); while ((ret = i_stream_read_more(input, &data, &size)) > 0) { buffer_append(extra_buf, data, size); i_stream_skip(input, size); } i_assert(ret == -1); if (input->stream_errno != 0) { i_error("istream-attachment: read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); failed = TRUE; } } i_stream_unref(&input); if (failed) { i_close_fd(&outfd); return -1; } /* successfully wrote it. switch to using it. */ o_stream_destroy(&part->temp_output); i_close_fd(&part->temp_fd); part->temp_fd = outfd; if (extra_buf != NULL) { stream_add_data(astream, extra_buf->data, extra_buf->used); buffer_free(&extra_buf); } return 0; }