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;
	}
}
Пример #3
0
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;
}