summaryrefslogtreecommitdiffstats
path: root/src/lib-mail/message-part-serialize.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-mail/message-part-serialize.c')
-rw-r--r--src/lib-mail/message-part-serialize.c269
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;
+}