/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "buffer.h"
#include "message-parser.h"
#include "message-part-serialize.h"
/*
root part
root's first children
children's first children
...
root's next children
...
part
unsigned int flags
(not root 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
(flags & (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_MESSAGE_RFC822))
unsigned int body_lines
(flags & (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_MESSAGE_RFC822))
unsigned int children_count
*/
#define MINIMUM_SERIALIZED_SIZE \
(sizeof(unsigned int) + sizeof(uoff_t) * 4)
struct deserialize_context {
pool_t pool;
const unsigned char *data, *end;
uoff_t pos;
const char *error;
};
static void part_serialize(struct message_part *part, buffer_t *dest,
unsigned int *children_count_r)
{
unsigned int count, children_count;
size_t children_offset;
bool root = part->parent == NULL;
count = 0;
while (part != NULL) {
/* create serialized part */
buffer_append(dest, &part->flags, sizeof(part->flags));
if (root)
root = FALSE;
else {
buffer_append(dest, &part->physical_pos,
sizeof(part->physical_pos));
}
buffer_append(dest, &part->header_size.physical_size,
sizeof(part->header_size.physical_size));
buffer_append(dest, &part->header_size.virtual_size,
sizeof(part->header_size.virtual_size));
buffer_append(dest, &part->body_size.physical_size,
sizeof(part->body_size.physical_size));
buffer_append(dest, &part->body_size.virtual_size,
sizeof(part->body_size.virtual_size));
if ((part->flags & (MESSAGE_PART_FLAG_TEXT |
MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0) {
buffer_append(dest, &part->body_size.lines,
sizeof(part->body_size.lines));
}
if ((part->flags & (MESSAGE_PART_FLAG_MULTIPART |
MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0) {
children_offset = dest->used;
children_count = 0;
buffer_append(dest, &children_count,
sizeof(children_count));
if (part->children != NULL) {
part_serialize(part->children, dest,
&children_count);
buffer_write(dest, children_offset,
&children_count,
sizeof(children_count));
}
} else {
i_assert(part->children == NULL);
}
count++;
part = part->next;
}
*children_count_r = count;
}
void message_part_serialize(struct message_part *part, buffer_t *dest)
{
unsigned int children_count;
part_serialize(part, dest, &children_count);
}
static bool read_next(struct deserialize_context *ctx,
void *buffer, size_t buffer_size)
{
if (ctx->data + buffer_size > ctx->end) {
ctx->error = "Not enough data";
return FALSE;
}
memcpy(buffer, ctx->data, buffer_size);
ctx->data += buffer_size;
return TRUE;
}
static bool ATTR_NULL(2)
message_part_deserialize_part(struct deserialize_context *ctx,
struct message_part *parent,
unsigned int siblings,
struct message_part **part_r)
{
struct message_part *p, *part, *first_part, **next_part;
unsigned int children_count;
uoff_t pos;
bool root = parent == NULL;
first_part = NULL;
next_part = NULL;
while (siblings > 0) {
siblings--;
part = p_new(ctx->pool, struct message_part, 1);
part->parent = parent;
for (p = parent; p != NULL; p = p->parent)
p->children_count++;
if (!read_next(ctx, &part->flags, sizeof(part->flags)))
return FALSE;
if (root)
root = FALSE;
else {
if (!read_next(ctx, &part->physical_pos,
sizeof(part->physical_pos)))
return FALSE;
}
if (part->physical_pos < ctx->pos) {
ctx->error = "physical_pos less than expected";
return FALSE;
}
if (!read_next(ctx, &part->header_size.physical_size,
sizeof(part->header_size.physical_size)))
return FALSE;
if (!read_next(ctx, &part->header_size.virtual_size,
sizeof(part->header_size.virtual_size)))
return FALSE;
if (part->header_size.virtual_size <
part->header_size.physical_size) {
ctx->error = "header_size.virtual_size too small";
return FALSE;
}
if (!read_next(ctx, &part->body_size.physical_size,
sizeof(part->body_size.physical_size)))
return FALSE;
if (!read_next(ctx, &part->body_size.virtual_size,
sizeof(part->body_size.virtual_size)))
return FALSE;
if ((part->flags & (MESSAGE_PART_FLAG_TEXT |
MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0) {
if (!read_next(ctx, &part->body_size.lines,
sizeof(part->body_size.lines)))
return FALSE;
}
if (part->body_size.virtual_size <
part->body_size.physical_size) {
ctx->error = "body_size.virtual_size too small";
return FALSE;
}
if ((part->flags & (MESSAGE_PART_FLAG_MULTIPART |
MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0) {
if (!read_next(ctx, &children_count,
sizeof(children_count)))
return FALSE;
} else {
children_count = 0;
}
if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0) {
/* Only one child is possible */
if (children_count == 0) {
ctx->error =
"message/rfc822 part has no children";
return FALSE;
}
if (children_count != 1) {
ctx->error = "message/rfc822 part "
"has multiple children";
return FALSE;
}
}
if (children_count > 0) {
/* our children must be after our physical_pos+header
and the last child must be within our size. */
ctx->pos = part->physical_pos +
part->header_size.physical_size;
pos = ctx->pos + part->body_size.physical_size;
if (!message_part_deserialize_part(ctx, part,
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 (first_part == NULL)
first_part = part;
if (next_part != NULL)
*next_part = part;
next_part = &part->next;
}
*part_r = first_part;
return TRUE;
}
struct message_part *
message_part_deserialize(pool_t pool, const void *data, size_t size,
const char **error_r)
{
struct deserialize_context ctx;
struct message_part *part;
i_zero(&ctx);
ctx.pool = pool;
ctx.data = data;
ctx.end = ctx.data + size;
if (!message_part_deserialize_part(&ctx, NULL, 1, &part)) {
*error_r = ctx.error;
return NULL;
}
if (ctx.data != ctx.end) {
*error_r = "Too much data";
return NULL;
}
return part;
}