int mbox_move(struct mbox_sync_context *sync_ctx,
	      uoff_t dest, uoff_t source, uoff_t size)
{
	struct istream *input;
	struct ostream *output;
	off_t ret;

	i_assert(size < OFF_T_MAX);

	if (size == 0 || source == dest)
		return 0;

	i_stream_sync(sync_ctx->input);

	output = o_stream_create_fd_file(sync_ctx->write_fd, (uoff_t)-1, FALSE);
	i_stream_seek(sync_ctx->file_input, source);
	if (o_stream_seek(output, dest) < 0) {
		mbox_set_syscall_error(sync_ctx->mbox,
				       "o_stream_seek()");
		o_stream_unref(&output);
		return -1;
	}

	input = i_stream_create_limit(sync_ctx->file_input, size);
	ret = o_stream_send_istream(output, input);
	i_stream_unref(&input);

        if (ret == (off_t)size)
		ret = 0;
	else if (ret >= 0) {
		mbox_sync_set_critical(sync_ctx,
			"mbox_move(%"PRIuUOFF_T", %"PRIuUOFF_T", %"PRIuUOFF_T
			") moved only %"PRIuUOFF_T" bytes",
			dest, source, size, (uoff_t)ret);
		ret = -1;
	} else if (ret < 0) {
		errno = output->stream_errno;
		mbox_set_syscall_error(sync_ctx->mbox,
				       "o_stream_send_istream()");
	}

	mbox_sync_file_updated(sync_ctx, FALSE);
	o_stream_destroy(&output);
	return (int)ret;
}
Beispiel #2
0
int mbox_sync_parse_next_mail(struct istream *input,
			      struct mbox_sync_mail_context *ctx)
{
	struct mbox_sync_context *sync_ctx = ctx->sync_ctx;
	struct message_header_parser_ctx *hdr_ctx;
	struct message_header_line *hdr;
	struct mbox_sync_header_func *func;
	struct mbox_md5_context *mbox_md5_ctx;
	size_t line_start_pos;
	int i, ret;

	ctx->hdr_offset = ctx->mail.offset;
	ctx->mail.flags = MAIL_RECENT; /* default to having recent flag */

        ctx->header_first_change = (size_t)-1;
	ctx->header_last_change = 0;

	for (i = 0; i < MBOX_HDR_COUNT; i++)
		ctx->hdr_pos[i] = (size_t)-1;

	ctx->content_length = (uoff_t)-1;
	str_truncate(ctx->header, 0);

        mbox_md5_ctx = ctx->sync_ctx->mbox->md5_v.init();

        line_start_pos = 0;
	hdr_ctx = message_parse_header_init(input, NULL, 0);
	while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) {
		if (hdr->eoh) {
			ctx->have_eoh = TRUE;
			break;
		}

		if (!hdr->continued) {
			line_start_pos = str_len(ctx->header);
			str_append(ctx->header, hdr->name);
			str_append_n(ctx->header, hdr->middle, hdr->middle_len);
		}

		func = bsearch(hdr->name, header_funcs,
			       N_ELEMENTS(header_funcs), sizeof(*header_funcs),
			       mbox_sync_bsearch_header_func_cmp);

		if (func != NULL) {
			if (hdr->continues) {
				hdr->use_full_value = TRUE;
				continue;
			}

			if (!func->func(ctx, hdr)) {
				/* this header is broken, remove it */
				ctx->need_rewrite = TRUE;
				str_truncate(ctx->header, line_start_pos);
				if (ctx->header_first_change == (size_t)-1) {
					ctx->header_first_change =
						line_start_pos;
				}
				continue;
			}
			buffer_append(ctx->header, hdr->full_value,
				      hdr->full_value_len);
		} else {
			ctx->sync_ctx->mbox->md5_v.more(mbox_md5_ctx, hdr);
			buffer_append(ctx->header, hdr->value,
				      hdr->value_len);
		}
		if (!hdr->no_newline) {
			if (hdr->crlf_newline)
				str_append_c(ctx->header, '\r');
			str_append_c(ctx->header, '\n');
		}
	}
	i_assert(ret != 0);
	message_parse_header_deinit(&hdr_ctx);

	ctx->sync_ctx->mbox->md5_v.finish(mbox_md5_ctx, ctx->hdr_md5_sum);

	if ((ctx->seq == 1 && !ctx->seen_imapbase) ||
	    (ctx->seq > 1 && sync_ctx->dest_first_mail)) {
		/* missing X-IMAPbase */
		ctx->need_rewrite = TRUE;
		if (sync_ctx->base_uid_validity == 0) {
			/* figure out a new UIDVALIDITY for us. */
			sync_ctx->base_uid_validity =
				sync_ctx->hdr->uid_validity != 0 &&
				!sync_ctx->renumber_uids ?
				sync_ctx->hdr->uid_validity :
				I_MAX((uint32_t)ioloop_time, 1);
		}
	}

	ctx->body_offset = input->v_offset;
	if (input->stream_errno != 0) {
		mbox_sync_set_critical(ctx->sync_ctx, "read(%s) failed: %s",
			i_stream_get_name(input), i_stream_get_error(input));
		return -1;
	}
	return 0;
}
static int mbox_sync_read_and_move(struct mbox_sync_context *sync_ctx,
                                   struct mbox_sync_mail_context *mail_ctx,
				   struct mbox_sync_mail *mails,
				   uint32_t seq, uint32_t idx, uint32_t padding,
				   off_t move_diff, uoff_t expunged_space,
				   uoff_t end_offset, bool first_nonexpunged)
{
	struct mbox_sync_mail_context new_mail_ctx;
	uoff_t offset, dest_offset;
	size_t need_space;

	if (mail_ctx == NULL) {
		if (mbox_sync_seek(sync_ctx, mails[idx].from_offset) < 0)
			return -1;

		mbox_sync_read_next(sync_ctx, &new_mail_ctx, mails, seq, idx,
				    expunged_space);
		mail_ctx = &new_mail_ctx;
	} else {
		i_assert(seq == mail_ctx->seq);
		if (mail_ctx->mail.space < 0)
			mail_ctx->mail.space = 0;
		i_stream_seek(sync_ctx->input, mail_ctx->body_offset);
	}

	if (mail_ctx->mail.space <= 0) {
		need_space = str_len(mail_ctx->header) - mail_ctx->mail.space -
			(mail_ctx->body_offset - mail_ctx->hdr_offset);
		if (need_space != (uoff_t)-mails[idx].space) {
			/* this check works only if we're doing the first
			   write, or if the file size was changed externally */
			mbox_sync_file_update_ext_modified(sync_ctx);

			mbox_sync_set_critical(sync_ctx,
				"seq=%u uid=%u uid_broken=%d "
				"originally needed %"PRIuUOFF_T
				" bytes, now needs %"PRIuSIZE_T" bytes",
				seq, mails[idx].uid, mails[idx].uid_broken,
				(uoff_t)-mails[idx].space, need_space);
		}
	}

	if (first_nonexpunged && expunged_space > 0) {
		/* move From-line (after parsing headers so we don't
		   overwrite them) */
		if (mbox_move(sync_ctx, mails[idx].from_offset - expunged_space,
			      mails[idx].from_offset,
			      mails[idx].offset - mails[idx].from_offset) < 0)
			return -1;
	}

	if (mails[idx].space == 0) {
		/* don't touch spacing */
	} else if (padding < (uoff_t)mail_ctx->mail.space) {
		mbox_sync_headers_remove_space(mail_ctx, mail_ctx->mail.space -
					       padding);
	} else {
		mbox_sync_headers_add_space(mail_ctx, padding -
					    mail_ctx->mail.space);
	}

	/* move the body of this message and headers of next message forward,
	   then write the headers */
	offset = sync_ctx->input->v_offset;
	dest_offset = offset + move_diff;
	i_assert(offset <= end_offset);
	if (mbox_move(sync_ctx, dest_offset, offset, end_offset - offset) < 0)
		return -1;

	/* the header may actually be moved backwards if there was expunged
	   space which we wanted to remove */
	i_assert(dest_offset >= str_len(mail_ctx->header));
	dest_offset -= str_len(mail_ctx->header);
	i_assert(dest_offset >= mails[idx].from_offset - expunged_space);
	if (pwrite_full(sync_ctx->write_fd, str_data(mail_ctx->header),
			str_len(mail_ctx->header), dest_offset) < 0) {
		mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()");
		return -1;
	}
	mbox_sync_file_updated(sync_ctx, TRUE);

	if (sync_ctx->dest_first_mail) {
		mbox_sync_first_mail_written(mail_ctx, dest_offset);
		sync_ctx->dest_first_mail = FALSE;
	}

	mails[idx].offset = dest_offset +
		(mail_ctx->mail.offset - mail_ctx->hdr_offset);
	mails[idx].space = mail_ctx->mail.space;
	return 0;
}