From: Kevin McCarthy Date: Fri, 12 Jul 2019 01:06:56 +0000 (-0700) Subject: Add autocrypt header parsing to mutt_parse_rfc822_line(). X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7fd50ddd8f4899232567573e26faf1863ae5b390;p=mutt Add autocrypt header parsing to mutt_parse_rfc822_line(). Convert parse_parameters() for autocrypt header usage: * change to use a BUFFER to accomodate large autocrypt keydata attribute values. * Autocrypt header parameters are not rfc2231 compliant. Rather than rolling another very similar function, just change the existing one to allow space separation. --- diff --git a/mutt.h b/mutt.h index 35a3ae60..7e6cdc5a 100644 --- a/mutt.h +++ b/mutt.h @@ -673,6 +673,17 @@ typedef struct alias #define MUTT_ENV_CHANGED_XLABEL (1<<2) /* X-Label edited */ #define MUTT_ENV_CHANGED_SUBJECT (1<<3) /* Protected header update */ +#ifdef USE_AUTOCRYPT +typedef struct autocrypt +{ + char *addr; + char *keydata; + unsigned int prefer_encrypt : 1; + unsigned int invalid : 1; + struct autocrypt *next; /* used by gossip headers */ +} AUTOCRYPTHDR; +#endif + typedef struct envelope { ADDRESS *return_path; @@ -695,7 +706,9 @@ typedef struct envelope LIST *references; /* message references (in reverse order) */ LIST *in_reply_to; /* in-reply-to header content */ LIST *userhdrs; /* user defined headers */ - +#ifdef USE_AUTOCRYPT + AUTOCRYPTHDR *autocrypt; +#endif unsigned char changed; /* The MUTT_ENV_CHANGED_* flags specify which * fields are modified */ } ENVELOPE; diff --git a/muttlib.c b/muttlib.c index 8aa0011b..c31817cc 100644 --- a/muttlib.c +++ b/muttlib.c @@ -779,6 +779,25 @@ int mutt_is_text_part (BODY *b) return 0; } +#ifdef USE_AUTOCRYPT +void mutt_free_autocrypthdr (AUTOCRYPTHDR **p) +{ + AUTOCRYPTHDR *cur; + + if (!p) + return; + + while (*p) + { + cur = *p; + *p = (*p)->next; + FREE (&cur->addr); + FREE (&cur->keydata); + FREE (&cur); + } +} +#endif + void mutt_free_envelope (ENVELOPE **p) { if (!*p) return; @@ -805,6 +824,11 @@ void mutt_free_envelope (ENVELOPE **p) mutt_free_list (&(*p)->references); mutt_free_list (&(*p)->in_reply_to); mutt_free_list (&(*p)->userhdrs); + +#ifdef USE_AUTOCRYPT + mutt_free_autocrypthdr (&(*p)->autocrypt); +#endif + FREE (p); /* __FREE_CHECKED__ */ } diff --git a/parse.c b/parse.c index 6646fd03..50b23b37 100644 --- a/parse.c +++ b/parse.c @@ -136,17 +136,33 @@ int mutt_check_encoding (const char *c) return (ENCOTHER); } -static PARAMETER *parse_parameters (const char *s) +/* Performs rfc2231 parameter parsing on s. + * + * Autocrypt defines an irregular parameter format that doesn't follow the + * rfc. It splits keydata across multiple lines without parameter continuations. + * The allow_value_spaces parameter allows parsing those values which + * are split by spaces when unfolded. + */ +static PARAMETER *parse_parameters (const char *s, int allow_value_spaces) { PARAMETER *head = 0, *cur = 0, *new; - char buffer[LONG_STRING]; + BUFFER *buffer = NULL; const char *p; size_t i; + buffer = mutt_buffer_pool_get (); + /* allow_value_spaces, especially with autocrypt keydata, can result + * in quite large parameter values. avoid frequent reallocs by + * pre-sizing */ + if (allow_value_spaces) + mutt_buffer_increase_size (buffer, mutt_strlen (s)); + dprint (2, (debugfile, "parse_parameters: `%s'\n", s)); while (*s) { + mutt_buffer_clear (buffer); + if ((p = strpbrk (s, "=;")) == NULL) { dprint(1, (debugfile, "parse_parameters: malformed parameter: %s\n", s)); @@ -175,53 +191,58 @@ static PARAMETER *parse_parameters (const char *s) new->attribute = mutt_substrdup(s, s + i); } - s = skip_email_wsp(p + 1); /* skip over the = */ - - if (*s == '"') + do { - int state_ascii = 1; - s++; - for (i=0; *s && i < sizeof (buffer) - 1; i++, s++) - { - if (AssumedCharset) + s = skip_email_wsp(p + 1); /* skip over the =, or space if we loop */ + + if (*s == '"') + { + int state_ascii = 1; + s++; + for (; *s; s++) { - /* As iso-2022-* has a character of '"' with non-ascii state, - * ignore it. */ - if (*s == 0x1b && i < sizeof (buffer) - 2) + if (AssumedCharset) { - if (s[1] == '(' && (s[2] == 'B' || s[2] == 'J')) - state_ascii = 1; - else - state_ascii = 0; + /* As iso-2022-* has a character of '"' with non-ascii state, + * ignore it. */ + if (*s == 0x1b) + { + if (s[1] == '(' && (s[2] == 'B' || s[2] == 'J')) + state_ascii = 1; + else + state_ascii = 0; + } } + if (state_ascii && *s == '"') + break; + if (*s == '\\') + { + if (s[1]) + { + s++; + /* Quote the next character */ + mutt_buffer_addch (buffer, *s); + } + } + else + mutt_buffer_addch (buffer, *s); } - if (state_ascii && *s == '"') - break; - if (*s == '\\') - { - /* Quote the next character */ - buffer[i] = s[1]; - if (!*++s) - break; - } - else - buffer[i] = *s; - } - buffer[i] = 0; - if (*s) - s++; /* skip over the " */ - } - else - { - for (i=0; *s && *s != ' ' && *s != ';' && i < sizeof (buffer) - 1; i++, s++) - buffer[i] = *s; - buffer[i] = 0; - } + if (*s) + s++; /* skip over the " */ + } + else + { + for (; *s && *s != ' ' && *s != ';'; s++) + mutt_buffer_addch (buffer, *s); + } + + p = s; + } while (allow_value_spaces && (*s == ' ')); /* if the attribute token was missing, 'new' will be NULL */ if (new) { - new->value = safe_strdup (buffer); + new->value = safe_strdup (mutt_b2s (buffer)); dprint (2, (debugfile, "parse_parameter: `%s' = `%s'\n", new->attribute ? new->attribute : "", @@ -258,6 +279,7 @@ static PARAMETER *parse_parameters (const char *s) bail: rfc2231_decode_parameters (&head); + mutt_buffer_pool_release (&buffer); return (head); } @@ -305,7 +327,7 @@ void mutt_parse_content_type (char *s, BODY *ct) *pc++ = 0; while (*pc && ISSPACE (*pc)) pc++; - ct->parameter = parse_parameters(pc); + ct->parameter = parse_parameters(pc, 0); /* Some pre-RFC1521 gateways still use the "name=filename" convention, * but if a filename has already been set in the content-disposition, @@ -393,7 +415,7 @@ static void parse_content_disposition (const char *s, BODY *ct) if ((s = strchr (s, ';')) != NULL) { s = skip_email_wsp(s + 1); - if ((s = mutt_get_parameter ("filename", (parms = parse_parameters (s))))) + if ((s = mutt_get_parameter ("filename", (parms = parse_parameters (s, 0))))) mutt_str_replace (&ct->filename, s); if ((s = mutt_get_parameter ("name", parms))) ct->form_name = safe_strdup (s); @@ -401,6 +423,69 @@ static void parse_content_disposition (const char *s, BODY *ct) } } +#ifdef USE_AUTOCRYPT +static AUTOCRYPTHDR *parse_autocrypt (AUTOCRYPTHDR *head, const char *s) +{ + AUTOCRYPTHDR *autocrypt; + PARAMETER *params = NULL, *param; + + autocrypt = mutt_new_autocrypthdr (); + autocrypt->next = head; + + param = params = parse_parameters (s, 1); + if (!params) + { + autocrypt->invalid = 1; + goto cleanup; + } + + while (param) + { + if (!ascii_strcasecmp (param->attribute, "addr")) + { + if (autocrypt->addr) + { + autocrypt->invalid = 1; + goto cleanup; + } + autocrypt->addr = param->value; + param->value = NULL; + } + else if (!ascii_strcasecmp (param->attribute, "prefer-encrypt")) + { + if (!ascii_strcasecmp (param->value, "mutual")) + autocrypt->prefer_encrypt = 1; + } + else if (!ascii_strcasecmp (param->attribute, "keydata")) + { + if (autocrypt->keydata) + { + autocrypt->invalid = 1; + goto cleanup; + } + autocrypt->keydata = param->value; + param->value = NULL; + } + else if (param->attribute && (param->attribute[0] != '_')) + { + autocrypt->invalid = 1; + goto cleanup; + } + + param = param->next; + } + + /* Checking the addr against From, and for multiple valid headers + * occurs later, after all the headers are parsed. */ + if (!autocrypt->addr || !autocrypt->keydata) + autocrypt->invalid = 1; + +cleanup: + mutt_free_parameter (¶ms); + return autocrypt; +} +#endif + /* args: * fp stream to read from * @@ -1043,6 +1128,13 @@ int mutt_parse_rfc822_line (ENVELOPE *e, HEADER *hdr, char *line, char *p, short e->from = rfc822_parse_adrlist (e->from, p); matched = 1; } +#ifdef USE_AUTOCRYPT + else if (ascii_strcasecmp (line+1, "utocrypt") == 0) + { + e->autocrypt = parse_autocrypt (e->autocrypt, p); + matched = 1; + } +#endif break; case 'b': diff --git a/protos.h b/protos.h index 453dde6d..8a0616ba 100644 --- a/protos.h +++ b/protos.h @@ -61,6 +61,9 @@ int _mutt_traverse_thread (CONTEXT *ctx, HEADER *hdr, int flag); #define mutt_new_parameter() safe_calloc (1, sizeof (PARAMETER)) #define mutt_new_header() safe_calloc (1, sizeof (HEADER)) #define mutt_new_envelope() safe_calloc (1, sizeof (ENVELOPE)) +#ifdef USE_AUTOCRYPT +#define mutt_new_autocrypthdr() safe_calloc (1, sizeof (AUTOCRYPTHDR)) +#endif #define mutt_new_enter_state() safe_calloc (1, sizeof (ENTER_STATE)) typedef const char * format_t (char *, size_t, size_t, int, char, const char *, const char *, const char *, const char *, unsigned long, format_flag);