]> granicus.if.org Git - mutt/commitdiff
Implement RFC 2231.
authorThomas Roessler <roessler@does-not-exist.org>
Mon, 26 Jul 1999 12:30:54 +0000 (12:30 +0000)
committerThomas Roessler <roessler@does-not-exist.org>
Mon, 26 Jul 1999 12:30:54 +0000 (12:30 +0000)
Makefile.am
TODO
copy.c
init.h
mutt.h
parse.c
rfc2047.c
rfc2231.c [new file with mode: 0644]
rfc2231.h [new file with mode: 0644]
sendlib.c

index dd58ac9b97ce3c3f6dc0910b6621647aad2f5839..4884535c8d448ab1e738dfd0577d9e2b5e0cd04f 100644 (file)
@@ -18,8 +18,9 @@ mutt_SOURCES =        addrbook.c alias.c attach.c browser.c buffy.c color.c \
        edit.c enter.c flags.c init.c filter.c from.c getdomain.c \
        handler.c hash.c hdrline.c headers.c help.c hook.c keymap.c \
        main.c mbox.c menu.c mh.c mx.c pager.c parse.c pattern.c \
-       postpone.c query.c recvattach.c rfc822.c \
-       rfc1524.c rfc2047.c score.c send.c sendlib.c signal.c sort.c \
+       postpone.c query.c recvattach.c \
+       rfc822.c rfc1524.c rfc2047.c rfc2231.c \
+       score.c send.c sendlib.c signal.c sort.c \
        status.c system.c thread.c charset.c history.c lib.c muttlib.c
 
 mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(INTLLIBS) 
@@ -44,7 +45,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP TODO configure acconfig.h attach.h \
        globals.h hash.h history.h imap.h init.h keymap.h \
        mailbox.h mapping.h mime.h mutt.h mutt_curses.h mutt_menu.h  \
        mutt_regex.h mutt_socket.h mx.h pager.h parse.h pgp.h protos.h  \
-       reldate.h rfc1524.h rfc2047.h rfc822.h sha.h sha_locl.h \
+       reldate.h rfc1524.h rfc2047.h rfc2231.h rfc822.h sha.h sha_locl.h \
        sort.h mime.types VERSION prepare _regex.h OPS.MIX      \
        README.SECURITY remailer.c remailer.h browser.h Muttrc.in \
        lib.h extlib.c pgpewrap pgplib.h
diff --git a/TODO b/TODO
index 72f428fcfc8ca146b4476d3b01fbaad76331b912..741d6fb123237bab8ab527da78e7a64af0292e6c 100644 (file)
--- a/TODO
+++ b/TODO
@@ -4,6 +4,10 @@ Problems are listed in approximate order of priority.
 - character set support: We should have a global cache of
   character to file name mappings.
 
+- When displaying MIME headers, rfc 2047 decoding is applied (which
+  should not happen), and rfc 2231 decoding is not applied (which
+  should happen).
+
 - Help formatting could be revamped a bit.
 
 - re-add support for .mh_sequences files
@@ -52,4 +56,5 @@ Problems are listed in approximate order of priority.
 - handle message/external-body in some fashion
 
 - handle message/partial reconstruction
+
 $Id$
diff --git a/copy.c b/copy.c
index ce3e5ba2b5e186cddba7717d09357873ea633165..4400378f241ac86ddb1cf0cd540a10025dbfe5b0 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -203,7 +203,7 @@ mutt_copy_hdr (FILE *in, FILE *out, long off_start, long off_end, int flags,
     if (headers[x])
     {
       if (flags & CH_DECODE)
-       rfc2047_decode (headers[x], headers[x], mutt_strlen (headers[x]));
+       rfc2047_decode (headers[x], headers[x], mutt_strlen (headers[x]) + 1);
 
       /* We couldn't do the prefixing when reading because RFC 2047
        * decoding may have concatenated lines.
diff --git a/init.h b/init.h
index e968edcd99cbe0a3572ddcdb35bb0c439ff30243..dfbdfd38250bf46c5a799ce616cec75cb40b94e2 100644 (file)
--- a/init.h
+++ b/init.h
@@ -256,6 +256,7 @@ struct option_t MuttVars[] = {
   { "resolve",         DT_BOOL, R_NONE, OPTRESOLVE, 1 },
   { "reverse_alias",   DT_BOOL, R_BOTH, OPTREVALIAS, 0 },
   { "reverse_name",    DT_BOOL, R_BOTH, OPTREVNAME, 0 },
+  { "rfc2047_parameters", DT_BOOL, R_NONE, OPTRFC2047PARAMS, 0 },
   { "save_address",    DT_BOOL, R_NONE, OPTSAVEADDRESS, 0 },
   { "save_empty",      DT_BOOL, R_NONE, OPTSAVEEMPTY, 1 },
   { "save_name",       DT_BOOL, R_NONE, OPTSAVENAME, 0 },
diff --git a/mutt.h b/mutt.h
index d0c06232263523d872b5d6a6d4f16cbf70527d19..6d4ab16fdc13c4341a19a558ea9bbc0686cb6f4f 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -312,6 +312,7 @@ enum
   OPTRESOLVE,
   OPTREVALIAS,
   OPTREVNAME,
+  OPTRFC2047PARAMS,
   OPTSAVEADDRESS,
   OPTSAVEEMPTY,
   OPTSAVENAME,
diff --git a/parse.c b/parse.c
index 2bc22280099d53c1fc79cadda1bb7387ba4da5d1..5897f8e25c9d4ad0415941ecaa59fa194afd6a81 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -21,7 +21,7 @@
 #include "mailbox.h"
 #include "mime.h"
 #include "rfc2047.h"
-
+#include "rfc2231.h"
 
 
 #ifdef _PGPPATH
@@ -138,11 +138,14 @@ static PARAMETER *parse_parameters (const char *s)
   const char *p;
   size_t i;
 
+  dprint (2, (debugfile, "parse_parameters: `%s'\n", s));
+  
   while (*s)
   {
     if ((p = strpbrk (s, "=;")) == NULL)
     {
       dprint(1, (debugfile, "parse_parameters: malformed parameter: %s\n", s));
+      rfc2231_decode_parameters (&head);
       return (head); /* just bail out now */
     }
 
@@ -192,6 +195,8 @@ static PARAMETER *parse_parameters (const char *s)
 
       new->value = safe_strdup (buffer);
 
+      dprint (2, (debugfile, "parse_parameter: `%s' = `%s'\n", new->attribute, new->value));
+      
       /* Add this parameter to the list */
       if (head)
       {
@@ -221,6 +226,7 @@ static PARAMETER *parse_parameters (const char *s)
     while (*s == ';'); /* skip empty parameters */
   }    
 
+  rfc2231_decode_parameters (&head);
   return (head);
 }
 
index 872ebd5a1f0f1c8871ba02537ab9754fc59774e5..a6e521fd753a0ce119512a6a424f2268fc95b47c 100644 (file)
--- a/rfc2047.c
+++ b/rfc2047.c
@@ -281,15 +281,19 @@ static int rfc2047_decode_word (char *d, const char *s, size_t len)
   char *p = safe_strdup (s);
   char *pp = p;
   char *pd = d;
+  char *t;
   int enc = 0, filter = 0, count = 0, c1, c2, c3, c4;
   char *charset = NULL;
-  
+
   while ((pp = strtok (pp, "?")) != NULL)
   {
     count++;
     switch (count)
     {
       case 2:
+       /* ignore language specification a la RFC 2231 */        
+        if ((t = strchr (pp, '*')))
+         *t = '\0';
        if (mutt_strcasecmp (pp, Charset) != 0)
         {
          filter = 1;
@@ -316,6 +320,8 @@ static int rfc2047_decode_word (char *d, const char *s, size_t len)
            }
            else if (*pp == '=')
            {
+             if (pp[1] == 0 || pp[2] == 0)
+               break;  /* something wrong */
              *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]);
              len--;
              pp += 2;
@@ -333,19 +339,21 @@ static int rfc2047_decode_word (char *d, const char *s, size_t len)
        {
          while (*pp && len > 0)
          {
+           if (pp[0] == '=' || pp[1] == 0 || pp[1] == '=')
+             break;  /* something wrong */
            c1 = base64val(pp[0]);
            c2 = base64val(pp[1]);
            *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3);
            if (--len == 0) break;
            
-           if (pp[2] == '=') break;
+           if (pp[2] == 0 || pp[2] == '=') break;
 
            c3 = base64val(pp[2]);
            *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf);
            if (--len == 0)
              break;
 
-           if (pp[3] == '=')
+           if (pp[3] == 0 || pp[3] == '=')
              break;
 
            c4 = base64val(pp[3]);
diff --git a/rfc2231.c b/rfc2231.c
new file mode 100644 (file)
index 0000000..2ef80b1
--- /dev/null
+++ b/rfc2231.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 1999 Thomas Roessler <roessler@guug.de>
+ *
+ *     This program is free software; you can redistribute it
+ *     and/or modify it under the terms of the GNU General Public
+ *     License as published by the Free Software Foundation; either
+ *     version 2 of the License, or (at your option) any later
+ *     version.
+ * 
+ *     This program is distributed in the hope that it will be
+ *     useful, but WITHOUT ANY WARRANTY; without even the implied
+ *     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ *     PURPOSE.  See the GNU General Public License for more
+ *     details.
+ * 
+ *     You should have received a copy of the GNU General Public
+ *     License along with this program; if not, write to the Free
+ *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+ *     02139, USA.
+ */
+
+/*
+ * Yet another MIME encoding for header data.  This time, it's
+ * parameters, specified in RFC 2231, and modeled after the
+ * encoding used in URLs.
+ * 
+ * Additionally, continuations and encoding are mixed in an, errrm,
+ * interesting manner.
+ *
+ */
+
+#include "mutt.h"
+#include "mime.h"
+#include "charset.h"
+#include "rfc2047.h"
+#include "rfc2231.h"
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+struct rfc2231_parameter
+{
+  char *attribute;
+  char *value;
+  int  index;
+  int  encoded;
+  struct rfc2231_parameter 
+       *next;
+};
+
+static char *rfc2231_get_charset (char *, char *, size_t);
+static struct rfc2231_parameter *rfc2231_new_parameter (void);
+static void rfc2231_decode_one (char *, char *, char *);
+static void rfc2231_free_parameter (struct rfc2231_parameter **);
+static void rfc2231_join_continuations (PARAMETER **, struct rfc2231_parameter *);
+static void rfc2231_list_insert (struct rfc2231_parameter **, struct rfc2231_parameter *);
+
+void rfc2231_decode_parameters (PARAMETER **headp)
+{
+  PARAMETER *head = NULL;
+  PARAMETER **last;
+  PARAMETER *p, *q;
+
+  struct rfc2231_parameter *conthead = NULL;
+  struct rfc2231_parameter *conttmp;
+
+  char *s, *t;
+  char charset[STRING];
+
+  int encoded;
+  int index;
+  
+  if (!headp) return;
+  
+  for (last = &head, p = *headp; p; p = q)
+  {
+    q = p->next;
+
+    if (!(s = strchr (p->attribute, '*')))
+    {
+
+      /* 
+       * Using RFC 2047 encoding in MIME parameters is explicitly
+       * forbidden by that document.  Nevertheless, it's being
+       * generated by some software, including certain Lotus Notes to 
+       * Internet Gateways.  So we actually decode it.
+       */
+
+      if (option (OPTRFC2047PARAMS) && strstr (p->value, "=?"))
+       rfc2047_decode (p->value, p->value, strlen (p->value) + 1);
+
+      *last = p;
+      last = &p->next;
+      p->next = NULL;
+    }
+    else if (*(s + 1) == '\0')
+    {
+      *s = '\0';
+      
+      s = rfc2231_get_charset (p->value, charset, sizeof (charset));
+      rfc2231_decode_one (p->value, s, charset);
+
+      *last = p;
+      last = &p->next;
+      p->next = NULL;
+    }
+    else
+    {
+      *s = '\0'; s++; /* let s point to the first character of index. */
+      for (t = s; *t && isdigit (*t); t++)
+       ;
+      encoded = (*t == '*');
+      *t = '\0';
+
+      index = atoi (s);
+
+      conttmp = rfc2231_new_parameter ();
+      conttmp->attribute = p->attribute;
+      conttmp->value = p->value;
+      conttmp->encoded = encoded;
+      conttmp->index = index;
+      
+      p->attribute = NULL;
+      p->value = NULL;
+      safe_free ((void **) &p);
+
+      rfc2231_list_insert (&conthead, conttmp);
+
+    }
+  }
+
+  if (conthead)
+    rfc2231_join_continuations (last, conthead);
+  
+  *headp = head;
+}
+  
+static struct rfc2231_parameter *rfc2231_new_parameter (void)
+{
+  return safe_calloc (sizeof (struct rfc2231_parameter), 1);
+}
+
+static void rfc2231_free_parameter (struct rfc2231_parameter **p)
+{
+  if (*p)
+  {
+    safe_free ((void **) &(*p)->attribute);
+    safe_free ((void **) &(*p)->value);
+    safe_free ((void **) p);
+  }
+}
+
+static char *rfc2231_get_charset (char *value, char *charset, size_t chslen)
+{
+  char *t, *u;
+  
+  if (!(t = strchr (value, '\'')))
+  {
+    charset[0] = '\0';
+    return value;
+  }
+  
+  *t = '\0';
+  strfcpy (charset, value, chslen);
+  
+  if ((u = strchr (t + 1, '\'')))
+    return u + 1;
+  else
+    return t + 1;
+}
+
+static void rfc2231_decode_one (char *dest, char *src, char *chs)
+{
+  char *d;
+
+  for (d = dest; *src; src++)
+  {
+    if (*src == '%' && isxdigit (*(src + 1)) && isxdigit (*(src + 2)))
+    {
+      *d++ = (hexval (*(src + 1)) << 4) | (hexval (*(src + 2)));
+      src += 2;
+    }
+    else
+      *d++ = *src;
+  }
+  
+  *d = '\0';
+  
+  if (chs && strcmp (chs, "us-ascii") && strcmp (chs, Charset))
+    mutt_display_string (dest, mutt_get_translation (chs, Charset));
+}
+
+/* insert parameter into an ordered list.
+ * 
+ * Primary sorting key: attribute
+ * Secondary sorting key: index
+ */
+
+static void rfc2231_list_insert (struct rfc2231_parameter **list,
+                                struct rfc2231_parameter *par)
+{
+  struct rfc2231_parameter **last = list;
+  struct rfc2231_parameter *p = *list, *q;
+  int c;
+  
+  while (p)
+  {
+    last = &p->next;
+    q = p; p = p->next;
+
+    c = strcmp (par->value, q->value);
+    if ((c > 0) || (c == 0 && par->index >= q->index))
+      break;
+  }
+  
+  par->next = p;
+  *last = par;
+}
+
+/* process continuation parameters */
+
+static void rfc2231_join_continuations (PARAMETER **head,
+                                       struct rfc2231_parameter *par)
+{
+  struct rfc2231_parameter *q;
+
+  char attribute[STRING];
+  char charset[STRING];
+  char *value = NULL;
+  char *valp;
+  int encoded;
+
+  size_t l, vl;
+  
+  while (par)
+  {
+    value = NULL; l = 0;
+    
+    strfcpy (attribute, par->attribute, sizeof (attribute));
+
+    if ((encoded = par->encoded))
+      valp = rfc2231_get_charset (par->value, charset, sizeof (charset));
+    else
+      valp = par->value;
+
+    do 
+    {
+      if (encoded && par->encoded)
+       rfc2231_decode_one (par->value, valp, charset);
+      
+      vl = strlen (par->value);
+      
+      safe_realloc ((void **) &value, l + vl + 1);
+      strcpy (value + l, par->value);
+      l += vl;
+
+      q = par->next;
+      rfc2231_free_parameter (&par);
+      if ((par = q))
+       valp = par->value;
+    } while (par && !strcmp (par->attribute, attribute));
+    
+    if (value)
+    {
+      *head = mutt_new_parameter ();
+      (*head)->attribute = safe_strdup (attribute);
+      (*head)->value = value;
+      head = &(*head)->next;
+    }
+  }
+}
+
+int rfc2231_encode (char *dest, size_t l, unsigned char *src)
+{
+  char buff[LONG_STRING];
+  unsigned char *s;
+  char *t;
+  int encode = 0;
+
+  for (s = src; *s && !encode; s++)
+  {
+    if (*s & 0x80)
+      encode = 1;
+  }
+
+  if (!encode)
+    strfcpy (dest, (char *) src, l);
+  else
+  {
+    for (s = src, t = buff; *s && (t - buff) < sizeof (buff) - 4; s++)
+    {
+      if ((*s & 0x80) || *s == '\'')
+      {
+       sprintf ((char *) t, "%%%02x", (unsigned int) *s);
+       t += 3;
+      }
+      else
+       *t++ = *s;
+    }
+    *t = '\0';
+    
+    if (Charset && SendCharset && mutt_strcasecmp (Charset, SendCharset))
+      mutt_display_string (buff, mutt_get_translation (Charset, SendCharset));
+
+    snprintf (dest, l, "%s''%s", SendCharset ? SendCharset :
+             (Charset ? Charset : "unknown-8bit"), buff);
+  }
+
+  return encode;
+}
+
diff --git a/rfc2231.h b/rfc2231.h
new file mode 100644 (file)
index 0000000..ecd26fa
--- /dev/null
+++ b/rfc2231.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 1999 Thomas Roessler <roessler@guug.de>
+ *
+ *     This program is free software; you can redistribute it
+ *     and/or modify it under the terms of the GNU General Public
+ *     License as published by the Free Software Foundation; either
+ *     version 2 of the License, or (at your option) any later
+ *     version.
+ *
+ *     This program is distributed in the hope that it will be
+ *     useful, but WITHOUT ANY WARRANTY; without even the implied
+ *     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ *     PURPOSE.  See the GNU General Public License for more
+ *     details.
+ *
+ *     You should have received a copy of the GNU General Public
+ *     License along with this program; if not, write to the Free
+ *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+ *     02139, USA.
+ */
+
+void rfc2231_decode_parameters (PARAMETER **);
+int rfc2231_encode (char *, size_t, unsigned char *);
+
index 5abddaad4852fc5f5a69a1e151cf0812d30ae6b7..7205f0edc924e10b06c13a4f47f29c5efb02e648 100644 (file)
--- a/sendlib.c
+++ b/sendlib.c
@@ -19,6 +19,7 @@
 #include "mutt.h"
 #include "mutt_curses.h"
 #include "rfc2047.h"
+#include "rfc2231.h"
 #include "mx.h"
 #include "mime.h"
 #include "mailbox.h"
@@ -348,10 +349,12 @@ int mutt_write_mime_header (BODY *a, FILE *f)
 {
   PARAMETER *p;
   char buffer[STRING];
+  char tmp[STRING];
   char *t;
   char *fn;
   int len;
   int tmplen;
+  int encode;
   
   fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
 
@@ -368,7 +371,8 @@ int mutt_write_mime_header (BODY *a, FILE *f)
       fputc (';', f);
 
       buffer[0] = 0;
-      rfc822_cat (buffer, sizeof (buffer), p->value, MimeSpecials);
+      encode = rfc2231_encode (tmp, sizeof (tmp), (unsigned char *) p->value);
+      rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
 
       tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
 
@@ -383,7 +387,7 @@ int mutt_write_mime_header (BODY *a, FILE *f)
        len += tmplen + 1;
       }
 
-      fprintf (f, "%s=%s", p->attribute, buffer);
+      fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
 
     }
   }
@@ -409,8 +413,9 @@ int mutt_write_mime_header (BODY *a, FILE *f)
        t = fn;
       
       buffer[0] = 0;
-      rfc822_cat (buffer, sizeof (buffer), t, MimeSpecials);
-      fprintf (f, "; filename=%s", buffer);
+      encode = rfc2231_encode (tmp, sizeof (tmp), (unsigned char *) t);
+      rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
+      fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
     }
 
     fputc ('\n', f);