]> granicus.if.org Git - mutt/commitdiff
Add autocrypt header parsing to mutt_parse_rfc822_line().
authorKevin McCarthy <kevin@8t8.us>
Fri, 12 Jul 2019 01:06:56 +0000 (18:06 -0700)
committerKevin McCarthy <kevin@8t8.us>
Sat, 3 Aug 2019 21:08:09 +0000 (14:08 -0700)
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.

mutt.h
muttlib.c
parse.c
protos.h

diff --git a/mutt.h b/mutt.h
index 35a3ae604dbcc322373a8b91abc2dd8849e0f976..7e6cdc5a8ee2c03369fa3ef3ffc4cbdac4e04d57 100644 (file)
--- 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;
index 8aa0011bb3dc28868701e0cbf14b24162d405dce..c31817ccdd326e2496aac241f0f017b626c64409 100644 (file)
--- 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 6646fd03dbe7bcce315820319f976a7ee65dcc0d..50b23b37da91045ca07ae8c02348dda4b4361ced 100644 (file)
--- 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 (&params);
+  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':
index 453dde6dc29031266ff218fabcddb7dab122d9fb..8a0616ba52d7f422b3592450a0a5c970c482797c 100644 (file)
--- 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);