]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-imap: imap-bodystructure: Added support for making imap_bodystructure_parse(...
authorStephan Bosch <stephan.bosch@dovecot.fi>
Sun, 23 Oct 2016 23:45:18 +0000 (01:45 +0200)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 20 Feb 2017 09:28:08 +0000 (11:28 +0200)
It can now create the tree from the BODYSTRUCTURE string itself.

src/lib-imap/imap-bodystructure.c
src/lib-imap/imap-bodystructure.h
src/lib-imap/test-imap-bodystructure.c

index 54e81c75e7e3b7c21447a5d9c362e00a4afc381e..a01b4ab4faf7eba5991bf8efe0969d7f0af7eafe 100644 (file)
@@ -339,44 +339,79 @@ imap_bodystructure_parse_args_common(struct message_part *part,
 
 static int
 imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
-                             struct message_part *part,
+                             struct message_part **_part,
                              const char **error_r)
 {
+       struct message_part *part = *_part, *child_part;;
+       struct message_part **child_part_p;
        struct message_part_data *data;
-       struct message_part *child_part;
        const struct imap_arg *list_args;
        const char *value, *content_type, *subtype, *error;
-       bool multipart, text, message_rfc822, has_lines;
+       bool multipart, text, message_rfc822, parsing_tree, has_lines;
        unsigned int lines;
        uoff_t vsize;
 
-       i_assert(part->data == NULL);
+       if (part != NULL) {
+               /* parsing with pre-existing message_part tree */
+               parsing_tree = FALSE;
+       } else {
+               /* parsing message_part tree from BODYSTRUCTURE as well */
+               part = *_part = p_new(pool, struct message_part, 1);
+               parsing_tree = TRUE;
+       }
        part->data = data = p_new(pool, struct message_part_data, 1);
 
        multipart = FALSE;
-       child_part = part->children;
-       while (args->type == IMAP_ARG_LIST) {
-               if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
-                   child_part == NULL) {
-                       *error_r = "message_part hierarchy doesn't match BODYSTRUCTURE";
-                       return -1;
+       if (!parsing_tree) {
+               child_part = part->children;
+               while (args->type == IMAP_ARG_LIST) {
+                       if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
+                                 child_part == NULL) {
+                               *error_r = "message_part hierarchy "
+                                       "doesn't match BODYSTRUCTURE";
+                               return -1;
+                       }
+
+                       list_args = imap_arg_as_list(args);
+                       if (imap_bodystructure_parse_args(list_args, pool,
+                                                               &child_part, error_r) < 0)
+                               return -1;
+                       child_part = child_part->next;
+
+                       multipart = TRUE;
+                       args++;
                }
 
-               list_args = imap_arg_as_list(args);
-               if (imap_bodystructure_parse_args(list_args, pool,
-                                                 child_part, error_r) < 0)
+               if (multipart) {
+                       if (child_part != NULL) {
+                               *error_r = "message_part hierarchy "
+                                       "doesn't match BODYSTRUCTURE";
+                               return -1;
+                       }
+               } else  if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
+                       *error_r = "message_part multipart flag "
+                               "doesn't match BODYSTRUCTURE";
                        return -1;
-               child_part = child_part->next;
+               }
+       } else {
+               child_part_p = &part->children;
+               while (args->type == IMAP_ARG_LIST) {
+                       list_args = imap_arg_as_list(args);
+                       if (imap_bodystructure_parse_args(list_args, pool,
+                                                               child_part_p, error_r) < 0)
+                               return -1;
+                       (*child_part_p)->parent = part;
+                       child_part_p = &(*child_part_p)->next;
 
-               multipart = TRUE;
-               args++;
+                       multipart = TRUE;
+                       args++;
+               }
+               if (multipart) {
+                       part->flags |= MESSAGE_PART_FLAG_MULTIPART;
+               }
        }
 
        if (multipart) {
-               if (child_part != NULL) {
-                       *error_r = "message_part hierarchy doesn't match BODYSTRUCTURE";
-                       return -1;
-               }
                data->content_type = "multipart";
                if (!imap_arg_get_nstring(args++, &data->content_subtype)) {
                        *error_r = "Invalid multipart content-type";
@@ -394,10 +429,6 @@ imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
                return imap_bodystructure_parse_args_common
                        (part, pool, args, error_r);
        }
-       if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
-               *error_r = "message_part multipart flag doesn't match BODYSTRUCTURE";
-               return -1;
-       }
 
        /* "content type" "subtype" */
        if (!imap_arg_get_astring(&args[0], &content_type) ||
@@ -413,19 +444,29 @@ imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
        message_rfc822 = strcasecmp(content_type, "message") == 0 &&
                strcasecmp(subtype, "rfc822") == 0;
 
+       if (!parsing_tree) {
 #if 0
-       /* Disabled for now. Earlier Dovecot versions handled broken
-          Content-Type headers by writing them as "text" "plain" to
-          BODYSTRUCTURE reply, but the message_part didn't have
-          MESSAGE_PART_FLAG_TEXT. */
-       if (text != ((part->flags & MESSAGE_PART_FLAG_TEXT) != 0)) {
-               *error_r = "message_part text flag doesn't match BODYSTRUCTURE";
-               return -1;
-       }
+               /* Disabled for now. Earlier Dovecot versions handled broken
+                  Content-Type headers by writing them as "text" "plain" to
+                  BODYSTRUCTURE reply, but the message_part didn't have
+                  MESSAGE_PART_FLAG_TEXT. */
+               if (text != ((part->flags & MESSAGE_PART_FLAG_TEXT) != 0)) {
+                       *error_r = "message_part text flag "
+                               "doesn't match BODYSTRUCTURE";
+                       return -1;
+               }
 #endif
-       if (message_rfc822 != ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)) {
-               *error_r = "message_part message/rfc822 flag doesn't match BODYSTRUCTURE";
-               return -1;
+               if (message_rfc822 !=
+                       ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)) {
+                       *error_r = "message_part message/rfc822 flag "
+                               "doesn't match BODYSTRUCTURE";
+                       return -1;
+               }
+       } else {
+               if (text)
+                       part->flags |= MESSAGE_PART_FLAG_TEXT;
+               if (message_rfc822)
+                       part->flags |= MESSAGE_PART_FLAG_MESSAGE_RFC822;
        }
 
        /* ("content type param key" "value" ...) | NIL */
@@ -458,10 +499,14 @@ imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
                *error_r = "Invalid size field";
                return -1;
        }
-       if (vsize != part->body_size.virtual_size) {
-               *error_r = "message_part virtual_size doesn't match "
-                       "size in BODYSTRUCTURE";
-               return -1;
+       if (!parsing_tree) {
+               if (vsize != part->body_size.virtual_size) {
+                       *error_r = "message_part virtual_size doesn't match "
+                               "size in BODYSTRUCTURE";
+                       return -1;
+               }
+       } else {
+               part->body_size.virtual_size = vsize;
        }
 
        if (text) {
@@ -476,16 +521,23 @@ imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
        } else if (message_rfc822) {
                /* message/rfc822 - envelope + bodystructure + text lines */
 
-               i_assert(part->children != NULL &&
-                        part->children->next == NULL);
+               if (!parsing_tree) {
+                       i_assert(part->children != NULL &&
+                                part->children->next == NULL);
+               }
 
                if (!imap_arg_get_list(&args[1], &list_args)) {
                        *error_r = "Child bodystructure list expected";
                        return -1;
                }
                if (imap_bodystructure_parse_args
-                       (list_args, pool, part->children, error_r) < 0)
+                       (list_args, pool, &part->children, error_r) < 0)
                        return -1;
+               if (parsing_tree) {
+                       i_assert(part->children != NULL &&
+                                part->children->next == NULL);
+                       part->children->parent = part;
+               }
 
                if (!imap_arg_get_list(&args[0], &list_args)) {
                        *error_r = "Envelope list expected";
@@ -506,12 +558,17 @@ imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
                has_lines = TRUE;
        } else {
                i_assert(part->children == NULL);
+               lines = 0;
                has_lines = FALSE;
        }
-       if (has_lines && lines != part->body_size.lines) {
-               *error_r = "message_part lines "
-                       "doesn't match lines in BODYSTRUCTURE";
-               return -1;
+       if (!parsing_tree) {
+               if (has_lines && lines != part->body_size.lines) {
+                       *error_r = "message_part lines "
+                               "doesn't match lines in BODYSTRUCTURE";
+                       return -1;
+               }
+       } else {
+               part->body_size.lines = lines;
        }
        if (args->type == IMAP_ARG_EOL)
                return 0;
@@ -524,8 +581,8 @@ imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
                (part, pool, args, error_r);
 }
 
-int imap_bodystructure_parse(const char *bodystructure,
-       pool_t pool, struct message_part *parts,
+int imap_bodystructure_parse_full(const char *bodystructure,
+       pool_t pool, struct message_part **parts,
        const char **error_r)
 {
        struct istream *input;
@@ -535,8 +592,7 @@ int imap_bodystructure_parse(const char *bodystructure,
        int ret;
        bool fatal;
 
-       i_assert(parts != NULL);
-       i_assert(parts->next == NULL);
+       i_assert(*parts == NULL || (*parts)->next == NULL);
 
        input = i_stream_create_from_data(bodystructure, strlen(bodystructure));
        (void)i_stream_read(input);
@@ -569,6 +625,16 @@ int imap_bodystructure_parse(const char *bodystructure,
        return ret;
 }
 
+int imap_bodystructure_parse(const char *bodystructure,
+       pool_t pool, struct message_part *parts,
+       const char **error_r)
+{
+       i_assert(parts != NULL);
+
+       return imap_bodystructure_parse_full(bodystructure,
+               pool, &parts, error_r);
+}
+
 static bool str_append_nstring(string_t *str, const struct imap_arg *arg)
 {
        const char *cstr;
index aee4ea6fdddfd2ae3915a7abecbaef4f7f6d07ed..f622561f7b0bfc6ab24e62e1bf7f7a4f7123f5f8 100644 (file)
@@ -11,7 +11,15 @@ void imap_bodystructure_write(const struct message_part *part,
                              string_t *dest, bool extended);
 
 /* Parse BODYSTRUCTURE and save the contents to message_part->data for each
-   message tree node. Returns 0 if ok, -1 if bodystructure wasn't valid. */
+   message tree node. If the parts argument points to NULL, the message_part
+   tree is created from the BODYSTRUCTURE. Otherwise, existing tree is used.
+   Returns 0 if ok, -1 if bodystructure wasn't valid. */
+int imap_bodystructure_parse_full(const char *bodystructure, pool_t pool,
+                            struct message_part **parts, const char **error_r);
+
+/* Parse BODYSTRUCTURE and save the contents to message_part->data for each
+   message tree node. The parts argument must point to an existing message_part
+   tree. Returns 0 if ok, -1 if bodystructure wasn't valid. */
 int imap_bodystructure_parse(const char *bodystructure, pool_t pool,
                             struct message_part *parts, const char **error_r);
 
index a6d15d8c19bb604257da3783af3750d95f231234..77dbd609c7b7aa2d2cc613d0b6e1b27350a55a17 100644 (file)
@@ -457,6 +457,37 @@ static void test_imap_bodystructure_parse(void)
        } T_END;
 }
 
+static void test_imap_bodystructure_parse_full(void)
+{
+       const char *error;
+       unsigned int i;
+       int ret;
+
+       for (i = 0; i < parse_tests_count; i++) T_BEGIN {
+               struct parse_test *test = &parse_tests[i];
+               struct message_part *parts = NULL;
+               string_t *str = t_str_new(128);
+               pool_t pool = pool_alloconly_create("imap bodystructure parse full", 1024);
+
+               test_begin(t_strdup_printf("imap bodystructure parser full [%u]", i));
+
+               ret = imap_bodystructure_parse_full(test->bodystructure,
+                                                          pool, &parts, &error);
+               test_assert(ret == 0);
+
+               if (ret == 0) {
+                       str_truncate(str, 0);
+                       imap_bodystructure_write(parts, str, TRUE);
+                       test_assert(strcmp(str_c(str), test->bodystructure) == 0);
+               } else {
+                       i_error("Invalid BODYSTRUCTURE: %s", error);
+               }
+
+               pool_unref(&pool);
+               test_end();
+       } T_END;
+}
+
 static void test_imap_bodystructure_normalize(void)
 {
        struct message_part *parts;
@@ -495,6 +526,7 @@ int main(void)
                test_imap_bodystructure_write,
                test_imap_bodystructure_parse,
                test_imap_bodystructure_normalize,
+               test_imap_bodystructure_parse_full,
                NULL
        };
        return test_run(test_functions);