view src/lib-mail/message-part-serialize.c @ 1102:fcc3a3b4aec9 HEAD

Variable type changes, fixes compiler warning.
author Timo Sirainen <tss@iki.fi>
date Sat, 08 Feb 2003 09:48:22 +0200
parents 26cafa3dc09c
children 8f56379c3917
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "buffer.h"
#include "message-parser.h"
#include "message-part-serialize.h"

/*
   Serialized a series of SerializedMessageParts:

   root part
     root's first children
       children's first children
       ...
     root's next children
     ...
*/

/* struct is 8 byte aligned */
struct serialized_message_part {
	uoff_t physical_pos;
  
	uoff_t header_physical_size;
	uoff_t header_virtual_size;
  
	uoff_t body_physical_size;
	uoff_t body_virtual_size;

	unsigned int header_lines;
	unsigned int body_lines;

	unsigned int children_count;
	unsigned int flags;
};

struct deserialize_context {
	pool_t pool;

	const struct serialized_message_part *spart;
	unsigned int sparts_left;

	uoff_t pos;
	const char *error;
};

static unsigned int
_message_part_serialize(struct message_part *part, buffer_t *dest)
{
	struct serialized_message_part *spart;
	unsigned int count = 0;

	while (part != NULL) {
		/* create serialized part */
		spart = buffer_append_space(dest, sizeof(*spart));
		memset(spart, 0, sizeof(*spart));

		spart->physical_pos = part->physical_pos;

		spart->header_physical_size = part->header_size.physical_size;
		spart->header_virtual_size = part->header_size.virtual_size;
		spart->header_lines = part->header_size.lines;

		spart->body_physical_size = part->body_size.physical_size;
		spart->body_virtual_size = part->body_size.virtual_size;
		spart->body_lines = part->body_size.lines;

		spart->flags = part->flags;

		if (part->children != NULL) {
			spart->children_count =
				_message_part_serialize(part->children, dest);
		}

		count++;
		part = part->next;
	}

	return count;
}

void message_part_serialize(struct message_part *part, buffer_t *dest)
{
	_message_part_serialize(part, dest);
}

static int message_part_deserialize_part(struct deserialize_context *ctx,
					 struct message_part *parent,
					 unsigned int child_count,
                                         struct message_part **part_r)
{
        const struct serialized_message_part *spart;
	struct message_part *part, *first_part, **next_part;
	uoff_t pos;

	first_part = NULL;
	next_part = NULL;
	while (child_count > 0) {
		child_count--;
		if (ctx->sparts_left == 0) {
			ctx->error = "Not enough data for all parts";
			return FALSE;
		}

		spart = ctx->spart;
		ctx->spart++;
		ctx->sparts_left--;

		part = p_new(ctx->pool, struct message_part, 1);
		part->physical_pos = spart->physical_pos;

		if (part->physical_pos < ctx->pos) {
			ctx->error = "physical_pos less than expected";
			return FALSE;
		}

		part->header_size.physical_size = spart->header_physical_size;
		part->header_size.virtual_size = spart->header_virtual_size;
		part->header_size.lines = spart->header_lines;

		if (spart->header_virtual_size < spart->header_physical_size) {
			ctx->error = "header_virtual_size too small";
			return FALSE;
		}

		part->body_size.physical_size = spart->body_physical_size;
		part->body_size.virtual_size = spart->body_virtual_size;
		part->body_size.lines = spart->body_lines;

		if (spart->body_virtual_size < spart->body_physical_size) {
			ctx->error = "body_virtual_size too small";
			return FALSE;
		}

		part->flags = spart->flags;
		part->parent = parent;

		/* our children must be after our physical_pos and the last
		   child must be within our size. */
		ctx->pos = part->physical_pos;
		pos = part->physical_pos + spart->header_physical_size +
			spart->body_physical_size;

		if (!message_part_deserialize_part(ctx, part,
						   spart->children_count,
						   &part->children))
			return FALSE;

		if (ctx->pos > pos) {
			ctx->error = "child part location exceeds our size";
			return FALSE;
		}
		ctx->pos = pos; /* save it for above check for parent */

		if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) {
			/* Only one child is possible */
			if (part->children == NULL) {
				ctx->error =
					"message/rfc822 part has no children";
				return FALSE;
			}
			if (part->children->next != NULL) {
				ctx->error = "message/rfc822 part "
					"has multiple children";
				return FALSE;
			}
		}

		if (first_part == NULL)
			first_part = part;
		if (next_part != NULL)
			*next_part = part;
		next_part = &part->next;
	}

	*part_r = first_part;
	return TRUE;
}

static int check_size(size_t size, const char **error)
{
	if (size < sizeof(struct serialized_message_part)) {
		*error = "Not enough data for root";
		return FALSE;
	}

	if ((size % sizeof(struct serialized_message_part)) != 0) {
		*error = "Incorrect data size";
		return FALSE;
	}

	if (size / sizeof(struct serialized_message_part) > UINT_MAX) {
		*error = "Insane amount of data";
		return FALSE;
	}

	return TRUE;
}

struct message_part *message_part_deserialize(pool_t pool, const void *data,
					      size_t size, const char **error)
{
	struct deserialize_context ctx;
        struct message_part *part;

	if (!check_size(size, error))
		return NULL;

	memset(&ctx, 0, sizeof(ctx));
	ctx.pool = pool;
	ctx.spart = data;
	ctx.sparts_left =
		(unsigned int) (size / sizeof(struct serialized_message_part));

	if (!message_part_deserialize_part(&ctx, NULL, 1, &part)) {
		*error = ctx.error;
		return NULL;
	}

	if (ctx.sparts_left > 0) {
		*error = "Too much data";
		return NULL;
	}

	return part;
}

int message_part_serialize_update_header(void *data, size_t size,
					 struct message_size *hdr_size,
					 const char **error)
{
	struct serialized_message_part *spart = data;
	uoff_t first_pos;
	off_t pos_diff;
	unsigned int children, i, count;

	if (!check_size(size, error))
		return FALSE;

	if (hdr_size->physical_size >= OFF_T_MAX ||
	    spart->physical_pos >= OFF_T_MAX ||
	    spart->header_physical_size >= OFF_T_MAX) {
		*error = "Invalid data";
		return FALSE;
	}

	first_pos = spart->physical_pos;
	pos_diff = (off_t)hdr_size->physical_size - spart->header_physical_size;

	spart->header_physical_size = hdr_size->physical_size;
	spart->header_virtual_size = hdr_size->virtual_size;
	spart->header_lines = hdr_size->lines;

	if (pos_diff != 0) {
		/* have to update all positions, but skip the first one */
		children = spart->children_count;
		count = (size / sizeof(struct serialized_message_part))-1;
		spart++;

		for (i = 0; i < count; i++, spart++) {
			if (spart->physical_pos < first_pos ||
			    spart->physical_pos >= OFF_T_MAX) {
				/* invalid offset, might cause overflow */
				*error = "Invalid offset";
				return FALSE;
			}

			children += spart->children_count;
			spart->physical_pos += pos_diff;
		}

		if (children != count) {
			*error = t_strdup_printf("Size mismatch %u vs %u",
						 children, count);
			return FALSE;
		}
	}

	return TRUE;
}

int message_part_deserialize_size(const void *data, size_t size,
				  struct message_size *hdr_size,
				  struct message_size *body_size)
{
        const struct serialized_message_part *spart = data;

	/* make sure it looks valid */
	if (size < sizeof(struct serialized_message_part))
		return FALSE;

	hdr_size->physical_size = spart->header_physical_size;
	hdr_size->virtual_size = spart->header_virtual_size;
	hdr_size->lines = spart->header_lines;

	body_size->physical_size = spart->body_physical_size;
	body_size->virtual_size = spart->body_virtual_size;
	body_size->lines = spart->body_lines;

	return TRUE;
}