Esempio n. 1
0
static int fetch_data(struct imap_fetch_context *ctx,
		      const struct imap_fetch_body_data *body,
		      const struct message_size *size)
{
	string_t *str;

	ctx->cur_name = p_strconcat(ctx->cmd->pool,
				    "[", body->section, "]", NULL);
	ctx->cur_size = get_send_size(body, size->virtual_size);

	str = get_prefix(ctx, body, ctx->cur_size);
	if (o_stream_send(ctx->client->output,
			  str_data(str), str_len(str)) < 0)
		return -1;

	if (!ctx->update_partial) {
		if (message_skip_virtual(ctx->cur_input, body->skip, NULL,
					 FALSE, &ctx->skip_cr) < 0) {
			fetch_read_error(ctx);
			return -1;
		}
	} else {
		if (seek_partial(ctx->select_counter, ctx->cur_mail->uid,
				 &last_partial, ctx->cur_input, body->skip,
				 &ctx->skip_cr) < 0) {
			fetch_read_error(ctx);
			return -1;
		}
	}

	return fetch_stream(ctx, size);
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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();
}
Esempio n. 4
0
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;
}