Mercurial > dovecot > core-2.2
view src/lib-imap/imap-bodystructure.c @ 1327:36ba64c5dbb2 HEAD
crashfix
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 01 Apr 2003 17:55:48 +0300 |
parents | 97f8c00b8d4c |
children | d4563a5ba30b |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" #include "buffer.h" #include "istream.h" #include "str.h" #include "message-parser.h" #include "message-content-parser.h" #include "message-tokenize.h" #include "imap-parser.h" #include "imap-quote.h" #include "imap-envelope.h" #include "imap-bodystructure.h" #define EMPTY_BODYSTRUCTURE \ "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 0 0)" struct message_part_body_data { pool_t pool; string_t *str; char *content_type, *content_subtype; char *content_type_params; char *content_transfer_encoding; char *content_id; char *content_description; char *content_disposition; char *content_disposition_params; char *content_md5; char *content_language; struct message_part_envelope_data *envelope; }; static void part_write_bodystructure(struct message_part *part, string_t *str, int extended); static void parse_content_type(const unsigned char *value, size_t value_len, void *context) { struct message_part_body_data *data = context; size_t i; for (i = 0; i < value_len; i++) { if (value[i] == '/') break; } if (i == value_len) data->content_type = imap_quote(data->pool, value, value_len); else { data->content_type = imap_quote(data->pool, value, i); i++; data->content_subtype = imap_quote(data->pool, value+i, value_len-i); } } static void parse_save_params_list(const unsigned char *name, size_t name_len, const unsigned char *value, size_t value_len, int value_quoted __attr_unused__, void *context) { struct message_part_body_data *data = context; if (str_len(data->str) != 0) str_append_c(data->str, ' '); imap_quote_append(data->str, name, name_len); str_append_c(data->str, ' '); imap_quote_append(data->str, value, value_len); } static void parse_content_transfer_encoding(const unsigned char *value, size_t value_len, void *context) { struct message_part_body_data *data = context; data->content_transfer_encoding = imap_quote(data->pool, value, value_len); } static void parse_content_disposition(const unsigned char *value, size_t value_len, void *context) { struct message_part_body_data *data = context; data->content_disposition = imap_quote(data->pool, value, value_len); } static void parse_content_language(const unsigned char *value, size_t value_len, struct message_part_body_data *data) { struct message_tokenizer *tok; enum message_token token; string_t *str; int quoted; /* Content-Language: en-US, az-arabic (comments allowed) */ tok = message_tokenize_init(value, value_len, NULL, NULL); t_push(); str = t_str_new(256); quoted = FALSE; while ((token = message_tokenize_next(tok)) != TOKEN_LAST) { if (token == ',') { /* list separator */ if (quoted) { str_append_c(str, '"'); quoted = FALSE; } } else { /* anything else goes as-is. only alphabetic characters and '-' is allowed, so anything else is error which we can deal with however we want. */ if (!quoted) { if (str_len(str) > 0) str_append_c(str, ' '); str_append_c(str, '"'); quoted = TRUE; } if (!IS_TOKEN_STRING(token)) str_append_c(str, token); else { value = message_tokenize_get_value(tok, &value_len); str_append_n(str, value, value_len); } } } if (quoted) str_append_c(str, '"'); data->content_language = p_strdup(data->pool, str_c(str)); t_pop(); message_tokenize_deinit(tok); } static void parse_content_header(struct message_part_body_data *d, struct message_header_line *hdr, pool_t pool) { const char *name = hdr->name; const unsigned char *value; size_t value_len; if (strncasecmp(name, "Content-", 8) != 0) return; name += 8; if (hdr->continues) { hdr->use_full_value = TRUE; return; } value = hdr->full_value; value_len = hdr->full_value_len; switch (*name) { case 'i': case 'I': if (strcasecmp(name, "ID") == 0 && d->content_id == NULL) d->content_id = imap_quote(pool, value, value_len); break; case 'm': case 'M': if (strcasecmp(name, "MD5") == 0 && d->content_md5 == NULL) d->content_md5 = imap_quote(pool, value, value_len); break; case 't': case 'T': if (strcasecmp(name, "Type") == 0 && d->content_type == NULL) { d->str = t_str_new(256); message_content_parse_header(value, value_len, parse_content_type, parse_save_params_list, d); d->content_type_params = p_strdup_empty(pool, str_c(d->str)); } if (strcasecmp(name, "Transfer-Encoding") == 0 && d->content_transfer_encoding == NULL) { message_content_parse_header(value, value_len, parse_content_transfer_encoding, NULL, d); } break; case 'l': case 'L': if (strcasecmp(name, "Language") == 0 && d->content_language == NULL) parse_content_language(value, value_len, d); break; case 'd': case 'D': if (strcasecmp(name, "Description") == 0 && d->content_description == NULL) { d->content_description = imap_quote(pool, value, value_len); } if (strcasecmp(name, "Disposition") == 0 && d->content_disposition_params == NULL) { d->str = t_str_new(256); message_content_parse_header(value, value_len, parse_content_disposition, parse_save_params_list, d); d->content_disposition_params = p_strdup_empty(pool, str_c(d->str)); } break; } } static void parse_header(struct message_part *part, struct message_header_line *hdr, void *context) { pool_t pool = context; struct message_part_body_data *part_data; int parent_rfc822; if (hdr == NULL || hdr->eoh) return; parent_rfc822 = part->parent != NULL && (part->parent->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822); 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; t_push(); parse_content_header(part_data, hdr, pool); if (parent_rfc822) { /* message/rfc822, we need the envelope */ imap_envelope_parse_header(pool, &part_data->envelope, hdr); } t_pop(); } static void part_parse_headers(struct message_part *part, struct istream *input, uoff_t start_offset, pool_t pool) { while (part != NULL) { /* note that we want to parse the header of all the message parts, multiparts too. */ i_assert(part->physical_pos >= input->v_offset - start_offset); i_stream_skip(input, part->physical_pos - (input->v_offset - start_offset)); message_parse_header(part, input, NULL, parse_header, pool); if (part->children != NULL) { part_parse_headers(part->children, input, start_offset, pool); } part = part->next; } } static void part_write_body_multipart(struct message_part *part, string_t *str, int extended) { struct message_part_body_data *data = part->context; if (data == NULL) { /* there was no content headers, use an empty structure */ data = t_new(struct message_part_body_data, 1); } if (part->children != NULL) part_write_bodystructure(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, ')'); } 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, ')'); } } static void part_write_body(struct message_part *part, string_t *str, int extended) { struct message_part_body_data *data = part->context; if (data == NULL) { /* there was no content headers, use an empty structure */ data = t_new(struct message_part_body_data, 1); } /* "content type" "subtype" */ 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 (data->content_type == NULL || strcasecmp(data->content_type, "\"text\"") == 0) str_append(str, "\"plain\""); else str_append(str, "\"unknown\""); } /* ("content type param key" "value" ...) */ 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, ')'); } 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 (part->flags & MESSAGE_PART_FLAG_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_c(str, ' '); if (child_data != NULL && child_data->envelope != NULL) { str_append_c(str, '('); imap_envelope_write_part_data(child_data->envelope, str); str_append_c(str, ')'); } else { /* buggy message */ str_append(str, "NIL"); } str_append_c(str, ' '); part_write_bodystructure(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") */ str_append_c(str, ' '); str_append(str, NVL(data->content_md5, "NIL")); 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, ')'); } } static void part_write_bodystructure(struct message_part *part, string_t *str, int extended) { i_assert(part->parent != NULL || part->next == NULL); while (part != NULL) { if (part->parent != NULL) str_append_c(str, '('); if (part->flags & MESSAGE_PART_FLAG_MULTIPART) part_write_body_multipart(part, str, extended); else part_write_body(part, str, extended); if (part->parent != NULL) str_append_c(str, ')'); part = part->next; } } const char *imap_part_get_bodystructure(pool_t pool, struct message_part **part, struct istream *input, int extended) { string_t *str; uoff_t start_offset; if (*part == NULL) *part = message_parse(pool, input, parse_header, pool); else { start_offset = input->v_offset; part_parse_headers(*part, input, start_offset, pool); } str = t_str_new(2048); part_write_bodystructure(*part, str, extended); return str_c(str); } static int str_append_imap_arg(string_t *str, const struct imap_arg *arg) { switch (arg->type) { case IMAP_ARG_NIL: str_append(str, "NIL"); break; case IMAP_ARG_ATOM: str_append(str, IMAP_ARG_STR(arg)); break; case IMAP_ARG_STRING: str_append_c(str, '"'); str_append(str, IMAP_ARG_STR(arg)); str_append_c(str, '"'); break; case IMAP_ARG_LITERAL: { const char *argstr = IMAP_ARG_STR(arg); str_printfa(str, "{%"PRIuSIZE_T"}", strlen(argstr)); str_append(str, argstr); break; } default: return FALSE; } return TRUE; } static int imap_write_list(const struct imap_arg *args, string_t *str) { /* don't do any typechecking, just write it out */ str_append_c(str, '('); while (args->type != IMAP_ARG_EOL) { if (!str_append_imap_arg(str, args)) { if (args->type != IMAP_ARG_LIST) return FALSE; if (!imap_write_list(IMAP_ARG_LIST(args)->args, str)) return FALSE; } args++; if (args->type != IMAP_ARG_EOL) str_append_c(str, ' '); } str_append_c(str, ')'); return TRUE; } static int imap_parse_bodystructure_args(const struct imap_arg *args, string_t *str) { struct imap_arg *subargs; struct imap_arg_list *list; int i, multipart, text, message_rfc822; multipart = FALSE; while (args->type == IMAP_ARG_LIST) { str_append_c(str, '('); list = IMAP_ARG_LIST(args); if (!imap_parse_bodystructure_args(list->args, str)) return FALSE; str_append_c(str, ')'); multipart = TRUE; args++; } if (multipart) { /* next is subtype of Content-Type. rest is skipped. */ str_append_c(str, ' '); return str_append_imap_arg(str, args); } /* "content type" "subtype" */ if (args[0].type == IMAP_ARG_NIL || args[1].type == IMAP_ARG_NIL) return FALSE; if (!str_append_imap_arg(str, &args[0])) return FALSE; str_append_c(str, ' '); if (!str_append_imap_arg(str, &args[1])) return FALSE; text = strcasecmp(IMAP_ARG_STR(&args[0]), "text") == 0; message_rfc822 = strcasecmp(IMAP_ARG_STR(&args[0]), "message") == 0 && strcasecmp(IMAP_ARG_STR(&args[1]), "rfc822") == 0; args += 2; /* ("content type param key" "value" ...) | NIL */ if (args->type == IMAP_ARG_LIST) { str_append(str, " ("); subargs = IMAP_ARG_LIST(args)->args; for (; subargs->type != IMAP_ARG_EOL; ) { if (subargs[0].type != IMAP_ARG_STRING || subargs[1].type != IMAP_ARG_STRING) return FALSE; if (!str_append_imap_arg(str, &subargs[0])) return FALSE; str_append_c(str, ' '); if (!str_append_imap_arg(str, &subargs[1])) return FALSE; subargs += 2; if (subargs->type == IMAP_ARG_EOL) break; str_append_c(str, ' '); } str_append(str, ")"); } else if (args->type == IMAP_ARG_NIL) { str_append(str, " NIL"); } else { return FALSE; } args++; /* "content id" "content description" "transfer encoding" size */ for (i = 0; i < 4; i++, args++) { str_append_c(str, ' '); if (!str_append_imap_arg(str, args)) return FALSE; } if (text) { /* text/xxx - text lines */ if (args->type != IMAP_ARG_ATOM) return FALSE; str_append_c(str, ' '); str_append(str, IMAP_ARG_STR(args)); } else if (message_rfc822) { /* message/rfc822 - envelope + bodystructure + text lines */ if (args[0].type != IMAP_ARG_LIST || args[1].type != IMAP_ARG_LIST || args[2].type != IMAP_ARG_ATOM) return FALSE; str_append_c(str, ' '); list = IMAP_ARG_LIST(&args[0]); if (!imap_write_list(list->args, str)) return FALSE; str_append_c(str, ' '); list = IMAP_ARG_LIST(&args[1]); if (!imap_parse_bodystructure_args(list->args, str)) return FALSE; str_append_c(str, ' '); str_append(str, IMAP_ARG_STR(&args[2])); } return TRUE; } const char *imap_body_parse_from_bodystructure(const char *bodystructure) { struct istream *input; struct imap_parser *parser; struct imap_arg *args; string_t *str; const char *value; size_t len; int ret; len = strlen(bodystructure); str = t_str_new(len); input = i_stream_create_from_data(data_stack_pool, bodystructure, len); (void)i_stream_read(input); parser = imap_parser_create(input, NULL, 0, (size_t)-1); ret = imap_parser_read_args(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE | IMAP_PARSE_FLAG_LITERAL_TYPE, &args); if (ret <= 0 || !imap_parse_bodystructure_args(args, str)) value = NULL; else value = str_c(str); if (value == NULL) i_error("Error parsing IMAP bodystructure: %s", bodystructure); imap_parser_destroy(parser); i_stream_unref(input); return value; }