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-Tag: 2019-10-25~97^2~46 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a8f888f91019d578fa9e1cd9bb7927554e7e348b;p=neomutt Add autocrypt header parsing to mutt_parse_rfc822_line() Convert parse_parameters() for autocrypt header usage: * change to use a struct 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. Co-authored-by: Richard Russon --- diff --git a/email/envelope.c b/email/envelope.c index a40cc8736..f81859cd2 100644 --- a/email/envelope.c +++ b/email/envelope.c @@ -56,6 +56,25 @@ struct Envelope *mutt_env_new(void) return e; } +#ifdef USE_AUTOCRYPT +void mutt_free_autocrypthdr(struct AutocryptHeader **p) +{ + struct AutocryptHeader *cur; + + if (!p) + return; + + while (*p) + { + cur = *p; + *p = (*p)->next; + FREE(&cur->addr); + FREE(&cur->keydata); + FREE(&cur); + } +} +#endif + /** * mutt_env_free - Free an Envelope * @param[out] p Envelope to free @@ -95,6 +114,11 @@ void mutt_env_free(struct Envelope **p) mutt_list_free(&(*p)->references); mutt_list_free(&(*p)->in_reply_to); mutt_list_free(&(*p)->userhdrs); + +#ifdef USE_AUTOCRYPT + mutt_free_autocrypthdr(&(*p)->autocrypt); +#endif + FREE(p); } diff --git a/email/envelope.h b/email/envelope.h index 60331ca0f..bb35e8c76 100644 --- a/email/envelope.h +++ b/email/envelope.h @@ -34,6 +34,17 @@ #define MUTT_ENV_CHANGED_XLABEL (1 << 2) ///< X-Label edited #define MUTT_ENV_CHANGED_SUBJECT (1 << 3) ///< Protected header update +#ifdef USE_AUTOCRYPT +struct AutocryptHeader +{ + char *addr; + char *keydata; + unsigned int prefer_encrypt : 1; + unsigned int invalid : 1; + struct AutocryptHeader *next; /* used by gossip headers */ +}; +#endif + /** * struct Envelope - The header of an Email */ @@ -67,7 +78,9 @@ struct Envelope struct ListHead references; ///< message references (in reverse order) struct ListHead in_reply_to; ///< in-reply-to header content struct ListHead userhdrs; ///< user defined headers - +#ifdef USE_AUTOCRYPT + struct AutocryptHeader *autocrypt; +#endif unsigned char changed; ///< Changed fields, e.g. #MUTT_ENV_CHANGED_SUBJECT }; @@ -78,4 +91,8 @@ struct Envelope *mutt_env_new (void); int mutt_env_to_intl (struct Envelope *env, const char **tag, char **err); void mutt_env_to_local (struct Envelope *e); +#ifdef USE_AUTOCRYPT +#define mutt_new_autocrypthdr() mutt_mem_calloc(1, sizeof(struct AutocryptHeader)) +#endif + #endif /* MUTT_EMAIL_ENVELOPE_H */ diff --git a/email/parse.c b/email/parse.c index 59af6ee15..0a682cb27 100644 --- a/email/parse.c +++ b/email/parse.c @@ -93,18 +93,32 @@ void mutt_auto_subscribe(const char *mailto) * parse_parameters - Parse a list of Parameters * @param param Parameter list for the results * @param s String to parse + * + * 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 void parse_parameters(struct ParameterList *param, const char *s) +static void parse_parameters(struct ParameterList *param, const char *s, int allow_value_spaces) { struct Parameter *pnew = NULL; - char buf[1024]; + struct Buffer *buf = NULL; const char *p = NULL; size_t i; + buf = 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(buf, mutt_str_strlen(s)); + mutt_debug(LL_DEBUG2, "'%s'\n", s); while (*s) { + mutt_buffer_reset(buf); + p = strpbrk(s, "=;"); if (!p) { @@ -133,53 +147,58 @@ static void parse_parameters(struct ParameterList *param, const char *s) pnew->attribute = mutt_str_substr_dup(s, s + i); } - s = mutt_str_skip_email_wsp(p + 1); /* skip over the = */ - - if (*s == '"') + do { - bool state_ascii = true; - s++; - for (i = 0; *s && (i < (sizeof(buf) - 1)); i++, s++) + s = mutt_str_skip_email_wsp(p + 1); /* skip over the =, or space if we loop */ + + if (*s == '"') { - if (C_AssumedCharset) + bool state_ascii = true; + s++; + for (; *s; s++) { - /* As iso-2022-* has a character of '"' with non-ascii state, - * ignore it. */ - if ((*s == 0x1b) && (i < (sizeof(buf) - 2))) + if (C_AssumedCharset) { - if ((s[1] == '(') && ((s[2] == 'B') || (s[2] == 'J'))) - state_ascii = true; - else - state_ascii = false; + /* 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 = true; + else + state_ascii = false; + } } - } - if (state_ascii && (*s == '"')) - break; - if (*s == '\\') - { - /* Quote the next character */ - buf[i] = s[1]; - if (!*++s) + if (state_ascii && (*s == '"')) break; + if (*s == '\\') + { + if (s[1]) + { + s++; + /* Quote the next character */ + mutt_buffer_addch(buf, *s); + } + } + else + mutt_buffer_addch(buf, *s); } - else - buf[i] = *s; + if (*s) + s++; /* skip over the " */ } - buf[i] = '\0'; - if (*s) - s++; /* skip over the " */ - } - else - { - for (i = 0; *s && (*s != ' ') && (*s != ';') && (i < (sizeof(buf) - 1)); i++, s++) - buf[i] = *s; - buf[i] = '\0'; - } + else + { + for (; *s && *s != ' ' && *s != ';'; s++) + mutt_buffer_addch(buf, *s); + } + + p = s; + } while (allow_value_spaces && (*s == ' ')); /* if the attribute token was missing, 'new' will be NULL */ if (pnew) { - pnew->value = mutt_str_strdup(buf); + pnew->value = mutt_str_strdup(mutt_b2s(buf)); mutt_debug(LL_DEBUG2, "parse_parameter: '%s' = '%s'\n", pnew->attribute ? pnew->attribute : "", pnew->value ? pnew->value : ""); @@ -208,6 +227,7 @@ static void parse_parameters(struct ParameterList *param, const char *s) bail: rfc2231_decode_parameters(param); + mutt_buffer_pool_release(&buf); } /** @@ -234,7 +254,7 @@ static void parse_content_disposition(const char *s, struct Body *ct) if (s) { s = mutt_str_skip_email_wsp(s + 1); - parse_parameters(&parms, s); + parse_parameters(&parms, s, 0); s = mutt_param_get(&parms, "filename"); if (s) mutt_str_replace(&ct->filename, s); @@ -446,7 +466,7 @@ void mutt_parse_content_type(const char *s, struct Body *ct) *pc++ = 0; while (*pc && IS_SPACE(*pc)) pc++; - parse_parameters(&ct->parameter, pc); + parse_parameters(&ct->parameter, pc, 0); /* Some pre-RFC1521 gateways still use the "name=filename" convention, * but if a filename has already been set in the content-disposition, @@ -522,6 +542,66 @@ void mutt_parse_content_type(const char *s, struct Body *ct) } } +#ifdef USE_AUTOCRYPT +static struct AutocryptHeader *parse_autocrypt(struct AutocryptHeader *head, const char *s) +{ + struct AutocryptHeader *autocrypt = mutt_new_autocrypthdr(); + autocrypt->next = head; + + struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl); + parse_parameters(&pl, s, 1); + if (TAILQ_EMPTY(&pl)) + { + autocrypt->invalid = 1; + goto cleanup; + } + + struct Parameter *p = NULL; + TAILQ_FOREACH(p, &pl, entries) + { + if (!mutt_str_strcasecmp(p->attribute, "addr")) + { + if (autocrypt->addr) + { + autocrypt->invalid = 1; + goto cleanup; + } + autocrypt->addr = p->value; + p->value = NULL; + } + else if (!mutt_str_strcasecmp(p->attribute, "prefer-encrypt")) + { + if (!mutt_str_strcasecmp(p->value, "mutual")) + autocrypt->prefer_encrypt = 1; + } + else if (!mutt_str_strcasecmp(p->attribute, "keydata")) + { + if (autocrypt->keydata) + { + autocrypt->invalid = 1; + goto cleanup; + } + autocrypt->keydata = p->value; + p->value = NULL; + } + else if (p->attribute && (p->attribute[0] != '_')) + { + autocrypt->invalid = 1; + goto cleanup; + } + } + + /* 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_param_free(&pl); + return autocrypt; +} +#endif + /** * mutt_rfc822_parse_line - Parse an email header * @param env Envelope of the email @@ -860,6 +940,13 @@ int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, char *line, mutt_addrlist_parse(&env->to, p); matched = true; } +#ifdef USE_AUTOCRYPT + else if (mutt_str_strcasecmp(line + 1, "utocrypt") == 0) + { + env->autocrypt = parse_autocrypt(env->autocrypt, p); + matched = 1; + } +#endif break; case 'x':