Mercurial > dovecot > core-2.2
view src/lib-imap/imap-bodystructure.c @ 18137:3009a1a6f6d5
global: freshen copyright
Robomatically:
git ls-files | xargs perl -p -i -e 's/(\d+)-201[0-4]/$1-2015/g;s/ (201[0-4]) Dovecot/ $1-2015 Dovecot/'
Happy 2015 everyone!
Signed-off-by: Phil Carmody <phil@dovecot.fi>
author | Phil Carmody <phil@dovecot.fi> |
---|---|
date | Mon, 05 Jan 2015 22:20:10 +0200 |
parents | add8c00fb3cc |
children | 0f22db71df7a |
line wrap: on
line source
/* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "buffer.h" #include "istream.h" #include "str.h" #include "message-parser.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" #include "imap-parser.h" #include "imap-quote.h" #include "imap-envelope.h" #include "imap-bodystructure.h" #define DEFAULT_CHARSET \ "\"charset\" \"us-ascii\"" #define EMPTY_BODYSTRUCTURE \ "(\"text\" \"plain\" ("DEFAULT_CHARSET") NIL NIL \"7bit\" 0 0)" #define NVL(str, nullstr) ((str) != NULL ? (str) : (nullstr)) static char *imap_get_string(pool_t pool, const char *value) { string_t *str = t_str_new(64); imap_append_string(str, value); return p_strdup(pool, str_c(str)); } static void parse_content_type(struct message_part_body_data *data, struct message_header_line *hdr) { struct rfc822_parser_context parser; const char *value, *const *results; string_t *str; unsigned int i; bool charset_found = FALSE; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); str = t_str_new(256); if (rfc822_parse_content_type(&parser, str) < 0) return; /* Save content type and subtype */ value = str_c(str); for (i = 0; value[i] != '\0'; i++) { if (value[i] == '/') { data->content_subtype = imap_get_string(data->pool, value + i+1); break; } } str_truncate(str, i); data->content_type = imap_get_string(data->pool, str_c(str)); /* parse parameters and save them */ str_truncate(str, 0); rfc2231_parse(&parser, &results); for (; *results != NULL; results += 2) { if (strcasecmp(results[0], "charset") == 0) charset_found = TRUE; str_append_c(str, ' '); imap_append_string(str, results[0]); str_append_c(str, ' '); imap_append_string(str, results[1]); } if (!charset_found && strcasecmp(data->content_type, "\"text\"") == 0) { /* set a default charset */ str_append_c(str, ' '); str_append(str, DEFAULT_CHARSET); } if (str_len(str) > 0) { data->content_type_params = p_strdup(data->pool, str_c(str) + 1); } } static void parse_content_transfer_encoding(struct message_part_body_data *data, struct message_header_line *hdr) { struct rfc822_parser_context parser; string_t *str; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); str = t_str_new(256); if (rfc822_parse_mime_token(&parser, str) >= 0) { data->content_transfer_encoding = imap_get_string(data->pool, str_c(str)); } } static void parse_content_disposition(struct message_part_body_data *data, struct message_header_line *hdr) { struct rfc822_parser_context parser; const char *const *results; string_t *str; rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL); rfc822_skip_lwsp(&parser); str = t_str_new(256); if (rfc822_parse_mime_token(&parser, str) < 0) return; data->content_disposition = imap_get_string(data->pool, str_c(str)); /* parse parameters and save them */ str_truncate(str, 0); rfc2231_parse(&parser, &results); for (; *results != NULL; results += 2) { str_append_c(str, ' '); imap_append_string(str, results[0]); str_append_c(str, ' '); imap_append_string(str, results[1]); } if (str_len(str) > 0) { data->content_disposition_params = p_strdup(data->pool, str_c(str) + 1); } } static void parse_content_language(const unsigned char *value, size_t value_len, struct message_part_body_data *data) { struct rfc822_parser_context parser; string_t *str; /* Language-Header = "Content-Language" ":" 1#Language-tag Language-Tag = Primary-tag *( "-" Subtag ) Primary-tag = 1*8ALPHA Subtag = 1*8ALPHA */ rfc822_parser_init(&parser, value, value_len, NULL); str = t_str_new(128); str_append_c(str, '"'); rfc822_skip_lwsp(&parser); while (rfc822_parse_atom(&parser, str) >= 0) { str_append(str, "\" \""); if (parser.data == parser.end || *parser.data != ',') break; parser.data++; rfc822_skip_lwsp(&parser); } if (str_len(str) > 1) { str_truncate(str, str_len(str) - 2); data->content_language = p_strdup(data->pool, str_c(str)); } } static void parse_content_header(struct message_part_body_data *d, struct message_header_line *hdr, pool_t pool) { const char *name = hdr->name + strlen("Content-"); const char *value; if (hdr->continues) { hdr->use_full_value = TRUE; return; } value = t_strndup(hdr->full_value, hdr->full_value_len); switch (*name) { case 'i': case 'I': if (strcasecmp(name, "ID") == 0 && d->content_id == NULL) d->content_id = imap_get_string(pool, value); break; case 'm': case 'M': if (strcasecmp(name, "MD5") == 0 && d->content_md5 == NULL) d->content_md5 = imap_get_string(pool, value); break; case 't': case 'T': if (strcasecmp(name, "Type") == 0 && d->content_type == NULL) parse_content_type(d, hdr); else if (strcasecmp(name, "Transfer-Encoding") == 0 && d->content_transfer_encoding == NULL) parse_content_transfer_encoding(d, hdr); break; case 'l': case 'L': if (strcasecmp(name, "Language") == 0 && d->content_language == NULL) { parse_content_language(hdr->full_value, hdr->full_value_len, d); } else if (strcasecmp(name, "Location") == 0 && d->content_location == NULL) { d->content_location = imap_get_string(pool, value); } break; case 'd': case 'D': if (strcasecmp(name, "Description") == 0 && d->content_description == NULL) d->content_description = imap_get_string(pool, value); else if (strcasecmp(name, "Disposition") == 0 && d->content_disposition_params == NULL) parse_content_disposition(d, hdr); break; } } void imap_bodystructure_parse_header(pool_t pool, struct message_part *part, struct message_header_line *hdr) { struct message_part_body_data *part_data; struct message_part_envelope_data *envelope; bool parent_rfc822; if (hdr == NULL) { if (part->context == NULL) { /* no Content-* headers. add an empty context structure anyway. */ part->context = part_data = p_new(pool, struct message_part_body_data, 1); part_data->pool = pool; } else if ((part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) { /* If there was no Mime-Version, forget all the Content-stuff */ part_data = part->context; envelope = part_data->envelope; memset(part_data, 0, sizeof(*part_data)); part_data->pool = pool; part_data->envelope = envelope; } return; } if (hdr->eoh) return; parent_rfc822 = part->parent != NULL && (part->parent->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0; if (!parent_rfc822 && strncasecmp(hdr->name, "Content-", 8) != 0) return; if (part->context == NULL) { /* initialize message part data */ part->context = part_data = p_new(pool, struct message_part_body_data, 1); part_data->pool = pool; } part_data = part->context; if (strncasecmp(hdr->name, "Content-", 8) == 0) { T_BEGIN { parse_content_header(part_data, hdr, pool); } T_END; } if (parent_rfc822) { /* message/rfc822, we need the envelope */ imap_envelope_parse_header(pool, &part_data->envelope, hdr); } } static void imap_bodystructure_write_siblings(const struct message_part *part, string_t *dest, bool extended) { for (; part != NULL; part = part->next) { str_append_c(dest, '('); imap_bodystructure_write(part, dest, extended); str_append_c(dest, ')'); } } static void part_write_bodystructure_data_common(struct message_part_body_data *data, string_t *str) { str_append_c(str, ' '); if (data->content_disposition == NULL) str_append(str, "NIL"); else { str_append_c(str, '('); str_append(str, data->content_disposition); str_append_c(str, ' '); if (data->content_disposition_params == NULL) str_append(str, "NIL"); else { str_append_c(str, '('); str_append(str, data->content_disposition_params); str_append_c(str, ')'); } str_append_c(str, ')'); } str_append_c(str, ' '); if (data->content_language == NULL) str_append(str, "NIL"); else { str_append_c(str, '('); str_append(str, data->content_language); str_append_c(str, ')'); } str_append_c(str, ' '); str_append(str, NVL(data->content_location, "NIL")); } static void part_write_body_multipart(const struct message_part *part, string_t *str, bool extended) { struct message_part_body_data *data = part->context; if (part->children != NULL) imap_bodystructure_write_siblings(part->children, str, extended); else { /* no parts in multipart message, that's not allowed. write a single 0-length text/plain structure */ str_append(str, EMPTY_BODYSTRUCTURE); } str_append_c(str, ' '); if (data->content_subtype != NULL) str_append(str, data->content_subtype); else str_append(str, "\"x-unknown\""); if (!extended) return; /* BODYSTRUCTURE data */ str_append_c(str, ' '); if (data->content_type_params == NULL) str_append(str, "NIL"); else { str_append_c(str, '('); str_append(str, data->content_type_params); str_append_c(str, ')'); } part_write_bodystructure_data_common(data, str); } static void part_write_body(const struct message_part *part, string_t *str, bool extended) { struct message_part_body_data *data = part->context; bool text; if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) { str_append(str, "\"message\" \"rfc822\""); text = FALSE; } else { /* "content type" "subtype" */ text = data->content_type == NULL || strcasecmp(data->content_type, "\"text\"") == 0; str_append(str, NVL(data->content_type, "\"text\"")); str_append_c(str, ' '); if (data->content_subtype != NULL) str_append(str, data->content_subtype); else { if (text) str_append(str, "\"plain\""); else str_append(str, "\"unknown\""); } } /* ("content type param key" "value" ...) */ str_append_c(str, ' '); if (data->content_type_params == NULL) { if (!text) str_append(str, "NIL"); else str_append(str, "("DEFAULT_CHARSET")"); } else { str_append_c(str, '('); str_append(str, data->content_type_params); str_append_c(str, ')'); } str_printfa(str, " %s %s %s %"PRIuUOFF_T, NVL(data->content_id, "NIL"), NVL(data->content_description, "NIL"), NVL(data->content_transfer_encoding, "\"7bit\""), part->body_size.virtual_size); if (text) { /* text/.. contains line count */ str_printfa(str, " %u", part->body_size.lines); } else if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) { /* message/rfc822 contains envelope + body + line count */ struct message_part_body_data *child_data; i_assert(part->children != NULL); i_assert(part->children->next == NULL); child_data = part->children->context; str_append(str, " ("); if (child_data->envelope_str != NULL) str_append(str, child_data->envelope_str); else imap_envelope_write_part_data(child_data->envelope, str); str_append(str, ") "); imap_bodystructure_write_siblings(part->children, str, extended); str_printfa(str, " %u", part->body_size.lines); } if (!extended) return; /* BODYSTRUCTURE data */ /* "md5" ("content disposition" ("disposition" "params")) ("body" "language" "params") "location" */ str_append_c(str, ' '); str_append(str, NVL(data->content_md5, "NIL")); part_write_bodystructure_data_common(data, str); } bool imap_bodystructure_is_plain_7bit(const struct message_part *part) { const struct message_part_body_data *data = part->context; i_assert(part->parent == NULL); /* if content-type is text/xxx we don't have to check any multipart stuff */ if ((part->flags & MESSAGE_PART_FLAG_TEXT) == 0) return FALSE; if (part->next != NULL || part->children != NULL) return FALSE; /* shouldn't happen normally.. */ /* must be text/plain */ if (data->content_subtype != NULL && strcasecmp(data->content_subtype, "\"plain\"") != 0) return FALSE; /* only allowed parameter is charset=us-ascii, which is also default */ if (data->content_type_params != NULL && strcasecmp(data->content_type_params, DEFAULT_CHARSET) != 0) return FALSE; if (data->content_id != NULL || data->content_description != NULL) return FALSE; if (data->content_transfer_encoding != NULL && strcasecmp(data->content_transfer_encoding, "\"7bit\"") != 0) return FALSE; /* BODYSTRUCTURE checks: */ if (data->content_md5 != NULL || data->content_disposition != NULL || data->content_language != NULL || data->content_location != NULL) return FALSE; return TRUE; } void imap_bodystructure_write(const struct message_part *part, string_t *dest, bool extended) { if (part->flags & MESSAGE_PART_FLAG_MULTIPART) part_write_body_multipart(part, dest, extended); else part_write_body(part, dest, extended); } static bool str_append_nstring(string_t *str, const struct imap_arg *arg) { const char *cstr; if (!imap_arg_get_nstring(arg, &cstr)) return FALSE; switch (arg->type) { case IMAP_ARG_NIL: str_append(str, "NIL"); break; case IMAP_ARG_ATOM: str_append(str, cstr); break; case IMAP_ARG_STRING: str_append_c(str, '"'); /* NOTE: we're parsing with no-unescape flag, so don't double-escape it here */ str_append(str, cstr); str_append_c(str, '"'); break; case IMAP_ARG_LITERAL: { str_printfa(str, "{%"PRIuSIZE_T"}\r\n", strlen(cstr)); str_append(str, cstr); break; } default: i_unreached(); return FALSE; } return TRUE; } static bool get_nstring(const struct imap_arg *arg, pool_t pool, string_t *tmpstr, const char **value_r) { if (arg->type == IMAP_ARG_NIL) { *value_r = NULL; return TRUE; } str_truncate(tmpstr, 0); if (!str_append_nstring(tmpstr, arg)) return FALSE; *value_r = p_strdup(pool, str_c(tmpstr)); return TRUE; } static void imap_write_list(const struct imap_arg *args, string_t *str) { const struct imap_arg *children; /* don't do any typechecking, just write it out */ while (!IMAP_ARG_IS_EOL(args)) { if (!str_append_nstring(str, args)) { if (!imap_arg_get_list(args, &children)) { /* everything is either nstring or list */ i_unreached(); } str_append_c(str, '('); imap_write_list(children, str); str_append_c(str, ')'); } args++; if (!IMAP_ARG_IS_EOL(args)) str_append_c(str, ' '); } } static int imap_write_nstring_list(const struct imap_arg *args, string_t *str) { str_truncate(str, 0); while (!IMAP_ARG_IS_EOL(args)) { if (!str_append_nstring(str, &args[0])) return -1; args++; if (IMAP_ARG_IS_EOL(args)) break; str_append_c(str, ' '); } return 0; } static int imap_write_params(const struct imap_arg *arg, pool_t pool, string_t *tmpstr, unsigned int divisible, const char **value_r) { const struct imap_arg *list_args; unsigned int list_count; if (arg->type == IMAP_ARG_NIL) { *value_r = NULL; return 0; } if (!imap_arg_get_list_full(arg, &list_args, &list_count)) return -1; if ((list_count % divisible) != 0) return -1; if (imap_write_nstring_list(list_args, tmpstr) < 0) return -1; *value_r = p_strdup(pool, str_c(tmpstr)); return 0; } static int imap_bodystructure_parse_lines(const struct imap_arg *arg, const struct message_part *part, const char **error_r) { const char *value; unsigned int lines; if (!imap_arg_get_atom(arg, &value) || str_to_uint(value, &lines) < 0) { *error_r = "Invalid lines field"; return -1; } if (lines != part->body_size.lines) { *error_r = "message_part lines doesn't match lines in BODYSTRUCTURE"; return -1; } return 0; } static int imap_bodystructure_parse_args_common(struct message_part_body_data *data, pool_t pool, string_t *tmpstr, const struct imap_arg *args, const char **error_r) { const struct imap_arg *list_args; if (args->type == IMAP_ARG_NIL) args++; else if (!imap_arg_get_list(args, &list_args)) { *error_r = "Invalid content-disposition list"; return -1; } else { if (!get_nstring(list_args++, pool, tmpstr, &data->content_disposition)) { *error_r = "Invalid content-disposition"; return -1; } if (imap_write_params(list_args, pool, tmpstr, 2, &data->content_disposition_params) < 0) { *error_r = "Invalid content-disposition params"; return -1; } args++; } if (imap_write_params(args++, pool, tmpstr, 1, &data->content_language) < 0) { *error_r = "Invalid content-language"; return -1; } if (!get_nstring(args++, pool, tmpstr, &data->content_location)) { *error_r = "Invalid content-location"; return -1; } return 0; } static int imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool, struct message_part *part, string_t *tmpstr, const char **error_r) { struct message_part_body_data *data; struct message_part *child_part; const struct imap_arg *list_args; const char *value, *content_type, *subtype; uoff_t vsize; bool multipart, text, message_rfc822; i_assert(part->context == NULL); part->context = data = p_new(pool, struct message_part_body_data, 1); data->pool = pool; 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; } list_args = imap_arg_as_list(args); if (imap_bodystructure_parse_args(list_args, pool, child_part, tmpstr, error_r) < 0) return -1; child_part = child_part->next; multipart = TRUE; args++; } if (multipart) { if (child_part != NULL) { *error_r = "message_part hierarchy doesn't match BODYSTRUCTURE"; return -1; } data->content_type = "\"multipart\""; if (!get_nstring(args++, pool, tmpstr, &data->content_subtype)) { *error_r = "Invalid multipart content-type"; return -1; } if (imap_write_params(args++, pool, tmpstr, 2, &data->content_type_params) < 0) { *error_r = "Invalid content params"; return -1; } return imap_bodystructure_parse_args_common(data, pool, tmpstr, 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) || !imap_arg_get_astring(&args[1], &subtype)) { *error_r = "Invalid content-type"; return -1; } if (!get_nstring(&args[0], pool, tmpstr, &data->content_type) || !get_nstring(&args[1], pool, tmpstr, &data->content_subtype)) i_unreached(); args += 2; text = strcasecmp(content_type, "text") == 0; message_rfc822 = strcasecmp(content_type, "message") == 0 && strcasecmp(subtype, "rfc822") == 0; if (text != ((part->flags & MESSAGE_PART_FLAG_TEXT) != 0)) { *error_r = "message_part text 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; } /* ("content type param key" "value" ...) | NIL */ if (imap_write_params(args++, pool, tmpstr, 2, &data->content_type_params) < 0) { *error_r = "Invalid content params"; return -1; } /* "content id" "content description" "transfer encoding" size */ if (!get_nstring(args++, pool, tmpstr, &data->content_id)) { *error_r = "Invalid content-id"; return -1; } if (!get_nstring(args++, pool, tmpstr, &data->content_description)) { *error_r = "Invalid content-description"; return -1; } if (!get_nstring(args++, pool, tmpstr, &data->content_transfer_encoding)) { *error_r = "Invalid content-transfer-encoding"; return -1; } if (!imap_arg_get_atom(args++, &value) || str_to_uoff(value, &vsize) < 0) { *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 (text) { /* text/xxx - text lines */ if (imap_bodystructure_parse_lines(args, part, error_r) < 0) return -1; args++; i_assert(part->children == NULL); } else if (message_rfc822) { /* message/rfc822 - envelope + bodystructure + text lines */ struct message_part_body_data *child_data; 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, tmpstr, error_r) < 0) return -1; /* save envelope to the child's context data */ if (!imap_arg_get_list(&args[0], &list_args)) { *error_r = "Envelope list expected"; return -1; } str_truncate(tmpstr, 0); imap_write_list(list_args, tmpstr); child_data = part->children->context; child_data->envelope_str = p_strdup(pool, str_c(tmpstr)); args += 2; if (imap_bodystructure_parse_lines(args, part, error_r) < 0) return -1; args++; } else { i_assert(part->children == NULL); } if (!get_nstring(args++, pool, tmpstr, &data->content_md5)) { *error_r = "Invalid content-description"; return -1; } return imap_bodystructure_parse_args_common(data, pool, tmpstr, args, error_r); } int imap_bodystructure_parse(const char *bodystructure, pool_t pool, struct message_part *parts, const char **error_r) { struct istream *input; struct imap_parser *parser; const struct imap_arg *args; int ret; bool fatal; i_assert(parts != NULL); i_assert(parts->next == NULL); input = i_stream_create_from_data(bodystructure, strlen(bodystructure)); (void)i_stream_read(input); parser = imap_parser_create(input, NULL, (size_t)-1); ret = imap_parser_finish_line(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE | IMAP_PARSE_FLAG_LITERAL_TYPE, &args); if (ret < 0) { *error_r = t_strdup_printf("IMAP parser failed: %s", imap_parser_get_error(parser, &fatal)); } else if (ret == 0) { *error_r = "Empty bodystructure"; ret = -1; } else T_BEGIN { string_t *tmpstr = t_str_new(256); ret = imap_bodystructure_parse_args(args, pool, parts, tmpstr, error_r); } T_END; imap_parser_unref(&parser); i_stream_destroy(&input); return ret; } static int imap_parse_bodystructure_args(const struct imap_arg *args, string_t *str, const char **error_r) { const struct imap_arg *subargs; const struct imap_arg *list_args; const char *value, *content_type, *subtype; bool multipart, text, message_rfc822; int i; multipart = FALSE; while (args->type == IMAP_ARG_LIST) { str_append_c(str, '('); list_args = imap_arg_as_list(args); if (imap_parse_bodystructure_args(list_args, str, error_r) < 0) return -1; str_append_c(str, ')'); multipart = TRUE; args++; } if (multipart) { /* next is subtype of Content-Type. rest is skipped. */ str_append_c(str, ' '); if (!str_append_nstring(str, args)) { *error_r = "Invalid multipart content-type"; return -1; } return 0; } /* "content type" "subtype" */ if (!imap_arg_get_astring(&args[0], &content_type) || !imap_arg_get_astring(&args[1], &subtype)) { *error_r = "Invalid content-type"; return -1; } if (!str_append_nstring(str, &args[0])) i_unreached(); str_append_c(str, ' '); if (!str_append_nstring(str, &args[1])) i_unreached(); text = strcasecmp(content_type, "text") == 0; message_rfc822 = strcasecmp(content_type, "message") == 0 && strcasecmp(subtype, "rfc822") == 0; args += 2; /* ("content type param key" "value" ...) | NIL */ if (imap_arg_get_list(args, &subargs)) { str_append(str, " ("); while (!IMAP_ARG_IS_EOL(subargs)) { if (!str_append_nstring(str, &subargs[0])) { *error_r = "Invalid content param key"; return -1; } str_append_c(str, ' '); if (!str_append_nstring(str, &subargs[1])) { *error_r = "Invalid content param value"; return -1; } subargs += 2; if (IMAP_ARG_IS_EOL(subargs)) break; str_append_c(str, ' '); } str_append(str, ")"); } else if (args->type == IMAP_ARG_NIL) { str_append(str, " NIL"); } else { *error_r = "list/NIL expected"; return -1; } args++; /* "content id" "content description" "transfer encoding" size */ for (i = 0; i < 4; i++, args++) { str_append_c(str, ' '); if (!str_append_nstring(str, args)) { *error_r = "nstring expected"; return -1; } } if (text) { /* text/xxx - text lines */ if (!imap_arg_get_atom(args, &value)) { *error_r = "Text lines expected"; return -1; } str_append_c(str, ' '); str_append(str, value); } else if (message_rfc822) { /* message/rfc822 - envelope + bodystructure + text lines */ str_append_c(str, ' '); if (!imap_arg_get_list(&args[0], &list_args)) { *error_r = "Envelope list expected"; return -1; } str_append_c(str, '('); imap_write_list(list_args, str); str_append(str, ") ("); if (!imap_arg_get_list(&args[1], &list_args)) { *error_r = "Child bodystructure list expected"; return -1; } if (imap_parse_bodystructure_args(list_args, str, error_r) < 0) return -1; str_append(str, ") "); if (!imap_arg_get_atom(&args[2], &value)) { *error_r = "Text lines expected"; return -1; } str_append(str, value); } return 0; } int imap_body_parse_from_bodystructure(const char *bodystructure, string_t *dest, const char **error_r) { struct istream *input; struct imap_parser *parser; const struct imap_arg *args; bool fatal; int ret; input = i_stream_create_from_data(bodystructure, strlen(bodystructure)); (void)i_stream_read(input); parser = imap_parser_create(input, NULL, (size_t)-1); ret = imap_parser_finish_line(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE | IMAP_PARSE_FLAG_LITERAL_TYPE, &args); if (ret < 0) { *error_r = t_strdup_printf("IMAP parser failed: %s", imap_parser_get_error(parser, &fatal)); } else if (ret == 0) { *error_r = "Empty bodystructure"; ret = -1; } else { ret = imap_parse_bodystructure_args(args, dest, error_r); } imap_parser_unref(&parser); i_stream_destroy(&input); return ret; }