# HG changeset patch # User Stephan Bosch # Date 1477266318 -7200 # Node ID 140000f0ba69e3810e578c62674cf23fb50f270e # Parent c7b8d82390d47cd493c2271e1c5aef6898f2f275 lib-imap: imap-bodystructure: Added support for making imap_bodystructure_parse() work without a pre-existing message_part tree. It can now create the tree from the BODYSTRUCTURE string itself. diff -r c7b8d82390d4 -r 140000f0ba69 src/lib-imap/imap-bodystructure.c --- a/src/lib-imap/imap-bodystructure.c Mon Oct 24 01:01:19 2016 +0200 +++ b/src/lib-imap/imap-bodystructure.c Mon Oct 24 01:45:18 2016 +0200 @@ -339,44 +339,79 @@ 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 @@ 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 @@ 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 @@ *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 @@ } 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 @@ 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 @@ (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 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 @@ 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; diff -r c7b8d82390d4 -r 140000f0ba69 src/lib-imap/imap-bodystructure.h --- a/src/lib-imap/imap-bodystructure.h Mon Oct 24 01:01:19 2016 +0200 +++ b/src/lib-imap/imap-bodystructure.h Mon Oct 24 01:45:18 2016 +0200 @@ -11,7 +11,15 @@ 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); diff -r c7b8d82390d4 -r 140000f0ba69 src/lib-imap/test-imap-bodystructure.c --- a/src/lib-imap/test-imap-bodystructure.c Mon Oct 24 01:01:19 2016 +0200 +++ b/src/lib-imap/test-imap-bodystructure.c Mon Oct 24 01:45:18 2016 +0200 @@ -457,6 +457,37 @@ } 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 @@ test_imap_bodystructure_write, test_imap_bodystructure_parse, test_imap_bodystructure_normalize, + test_imap_bodystructure_parse_full, NULL }; return test_run(test_functions);