diff options
Diffstat (limited to 'src/lib-mail/message-part-serialize.c')
-rw-r--r-- | src/lib-mail/message-part-serialize.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/lib-mail/message-part-serialize.c b/src/lib-mail/message-part-serialize.c new file mode 100644 index 0000000..23f3a01 --- /dev/null +++ b/src/lib-mail/message-part-serialize.c @@ -0,0 +1,269 @@ +/* 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; +} |