[exim-cvs] MIME: support RFC 2331 for name=. Bug 3099

Αρχική Σελίδα
Delete this message
Reply to this message
Συντάκτης: Exim Git Commits Mailing List
Ημερομηνία:  
Προς: exim-cvs
Αντικείμενο: [exim-cvs] MIME: support RFC 2331 for name=. Bug 3099
Gitweb: https://git.exim.org/exim.git/commitdiff/1b3209b0577a9327ebb076f3b32b8a159c253f7b
Commit:     1b3209b0577a9327ebb076f3b32b8a159c253f7b
Parent:     6ce5c70cff8989418e05d01fd2a57703007a6357
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Tue Jul 2 14:41:19 2024 +0100
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Tue Jul 2 14:41:19 2024 +0100


    MIME: support RFC 2331 for name=.  Bug 3099
---
 doc/doc-txt/ChangeLog           |   4 +-
 src/src/mime.c                  | 181 +++++++++++++++++++++-------------------
 test/mail/4000.userx            |  22 +++++
 test/scripts/4000-scanning/4000 |  10 +++
 4 files changed, 129 insertions(+), 88 deletions(-)


diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index acb779605..fad93254e 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -164,8 +164,10 @@ JH/33 Bug 2994: A subdir dsearch lookup should permit a directory name that star
 JH/34 Fix delivery ordering for 2-phase queue run combined with
       queue_run_in_order.


-JH/35 Bug 3099: fix parsing of MIME filenames split over multiple paramemters.
+JH/35 Bug 3099: fix parsing of MIME filename= split over multiple paramemters.
       Previously the $mime_filename variable would have an incorrect value.
+      While in the code, extend coverage to name= which previously was only
+      supported for single parameters, despite also filling in $mime_filename.



Exim version 4.97
diff --git a/src/src/mime.c b/src/src/mime.c
index 5f9e1ade7..8044bb3fd 100644
--- a/src/src/mime.c
+++ b/src/src/mime.c
@@ -30,10 +30,10 @@ static int mime_header_list_size = nelem(mime_header_list);

 static mime_parameter mime_parameter_list[] = {
   /*    name    namelen     value */
-  { US"name=",     5, &mime_filename },
-  { US"filename=", 9, &mime_filename },
-  { US"charset=",  8, &mime_charset  },
-  { US"boundary=", 9, &mime_boundary }
+  { US"name",     4, &mime_filename },
+  { US"filename", 8, &mime_filename },
+  { US"charset",  7, &mime_charset  },
+  { US"boundary", 8, &mime_boundary }
 };



@@ -577,8 +577,8 @@ while(1)
       if (*(p = q)) p++;            /* jump past the ; */


     {
-    uschar * mime_fname = NULL;
-    uschar * mime_fname_rfc2231 = NULL;
+    gstring * mime_fname = NULL;
+    gstring * mime_fname_rfc2231 = NULL;
     uschar * mime_filename_charset = NULL;
     BOOL decoding_failed = FALSE;


@@ -590,90 +590,92 @@ while(1)
       DEBUG(D_acl)
         debug_printf_indent("MIME:   considering paramlist '%s'\n", p);


-      if (  strncmpic(CUS"content-disposition:", header, 20) == 0
-         && strncmpic(CUS"filename*", p, 9) == 0
-         )
-        {                    /* RFC 2231 filename */
-        uschar * q;
-
-        /* find value of the filename */
-        p += 9;
-        while(*p != '=' && *p) p++;
-        if (*p) p++;            /* p is filename or NUL */
-        q = mime_param_val(&p);        /* p now trailing ; or NUL */
-
-        if (q && *q)
+      /* look for interesting parameters */
+      for (mime_parameter * mp = mime_parameter_list;
+           mp < mime_parameter_list + nelem(mime_parameter_list);
+           mp++
+          ) if (strncmpic(mp->name, p, mp->namelen) == 0)
+        {
+        p += mp->namelen;
+        if (*p == '*')            /* RFC 2231 */
           {
-          uschar * temp_string, * err_msg, * fname = q;
-          int slen;
-
-          /* build up an un-decoded filename over successive
-          filename*= parameters (for use when 2047 decode fails) */
-/*XXX could grow a gstring here */
-
-          mime_fname_rfc2231 = string_sprintf("%#s%s",
-        mime_fname_rfc2231, q);
-
-          if (!decoding_failed)
+          while (isdigit(*++p)) ;        /* ignore cont-cnt values */
+          if (*p == '*') p++;        /* step over sep chset mark */
+          if (*p == '=')
         {
-        int size;
-        if (!mime_filename_charset)
+        uschar * q;
+        p++;                /* step over = */
+        q = mime_param_val(&p);        /* p now trailing ; or NUL */
+
+        if (q && *q)            /* q is the dequoted value */
           {
-          uschar * s = q;
+          uschar * err_msg, * fname = q;
+          int slen;
+
+          /* build up an un-decoded filename over successive
+          filename*= parameters (for use when 2047 decode fails) */


-          /* look for a ' in the "filename" */
-          while(*s != '\'' && *s) s++;    /* s is 1st ' or NUL */
+          mime_fname_rfc2231 = string_cat(mime_fname_rfc2231, q);


-          if (*s)            /* there was a ' */
+          if (!decoding_failed)
             {
-            if ((size = s-q) > 0)
-              mime_filename_charset = string_copyn(q, size);
-
-            if (*(fname = s)) fname++;
-            while(*fname == '\'') fname++;    /* fname is after 2nd ' */
-            }
-          }
-
-        DEBUG(D_acl)
-          debug_printf_indent("MIME:    charset %s fname '%s'\n",
-            mime_filename_charset ? mime_filename_charset : US"<NULL>",
-            fname);
-
-        temp_string = rfc2231_to_2047(fname, mime_filename_charset,
-                          &slen);
-        DEBUG(D_acl)
-          debug_printf_indent("MIME:    2047-name %s\n", temp_string);
-
-        temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ',
-                          NULL, &err_msg);
-        DEBUG(D_acl)
-          debug_printf_indent("MIME:    plain-name %s\n", temp_string);
-
-        if (!temp_string || (size = Ustrlen(temp_string)) == slen)
-          decoding_failed = TRUE;
-        else
-          /* build up a decoded filename over successive
-          filename*= parameters */
-
-          mime_filename = mime_fname = mime_fname
-            ? string_sprintf("%s%s", mime_fname, temp_string)
-            : temp_string;
-        }    /*!decoding_failed*/
-          }        /*q*/
-        }        /*2231 filename*/
-
-      else
-        /* look for interesting parameters */
-        for (mime_parameter * mp = mime_parameter_list;
-         mp < mime_parameter_list + nelem(mime_parameter_list);
-         mp++
-        ) if (strncmpic(mp->name, p, mp->namelen) == 0)
-          {
-          uschar * q;
-          uschar * dummy_errstr;
+            if (!mime_filename_charset)
+              {            /* try for RFC 2231 chset/lang */
+              uschar * s = q;
+
+              /* look for a ' in the raw paramval */
+              while(*s != '\'' && *s) s++;    /* s is 1st ' or NUL */
+
+              if (*s)                /* there was a ' */
+            {
+            int size;
+            if ((size = s-q) > 0)
+              mime_filename_charset = string_copyn(q, size);
+
+            if (*(fname = s)) fname++;
+            while(*fname == '\'') fname++;    /*fname is after 2nd '*/
+            }
+              }
+
+            DEBUG(D_acl)
+              debug_printf_indent("MIME:    charset %s fname '%s'\n",
+            mime_filename_charset ? mime_filename_charset : US"<NULL>",
+            fname);
+
+            fname = rfc2231_to_2047(fname, mime_filename_charset,
+                          &slen);
+            DEBUG(D_acl)
+              debug_printf_indent("MIME:    2047-name %s\n", fname);
+
+            fname = rfc2047_decode(fname, FALSE, NULL, ' ',
+                          NULL, &err_msg);
+            DEBUG(D_acl) debug_printf_indent(
+                    "MIME:    plain-name %s\n", fname);
+
+            if (!fname || Ustrlen(fname) == slen)
+              decoding_failed = TRUE;
+            else if (mp->value == &mime_filename)
+              {
+              /* build up a decoded filename over successive
+              filename*= parameters */
+
+              mime_fname = string_cat(mime_fname, fname);
+              mime_filename = string_from_gstring(mime_fname);
+              }
+            }    /*!decoding_failed*/
+          }    /*q*/
+
+        if (*p) p++;            /* p is past ; */
+        goto param_done;        /* done matching param names */
+        }        /*2231 param coding extension*/
+          }
+        else if (*p == '=')
+          {        /* non-2231 param */
+          uschar * q, * dummy_errstr;


           /* grab the value and copy to its expansion variable */
-          p += mp->namelen;
+
+          if (*p) p++;            /* step over = */
           q = mime_param_val(&p);        /* p now trailing ; or NUL */


           *mp->value = q && *q
@@ -684,26 +686,31 @@ while(1)
         "MIME:  found %s parameter in %s header, value '%s'\n",
         mp->name, mh->name, *mp->value);


-          break;            /* done matching param names */
+          if (*p) p++;            /* p is past ; */
+          goto param_done;            /* done matching param names */
           }
-
+        }                    /* interesting parameters */


       /* There is something, but not one of our interesting parameters.
       Advance past the next semicolon */
+
       p = mime_next_semicolon(p);
       if (*p) p++;
-      }                /* param scan on line */
+  param_done:
+      }                    /* param scan on line */


     if (strncmpic(CUS"content-disposition:", header, 20) == 0)
       {
-      if (decoding_failed) mime_filename = mime_fname_rfc2231;
+      if (decoding_failed)
+        mime_filename = string_from_gstring(mime_fname_rfc2231);


       DEBUG(D_acl) debug_printf_indent(
         "MIME:  found %s parameter in %s header, value is '%s'\n",
         "filename", mh->name, mime_filename);
       }
     }
-      }
+      break;
+      }    /* interesting headers */


/* set additional flag variables (easier access) */
if ( mime_content_type
diff --git a/test/mail/4000.userx b/test/mail/4000.userx
index 242be6627..99f71010c 100644
--- a/test/mail/4000.userx
+++ b/test/mail/4000.userx
@@ -460,6 +460,19 @@ X-2-is-coverletter: 0
X-2-is-rfc822: 0
X-2-decode-filename: TESTSUITE/spool/scan/10HmbF-000000005vi-0000/10HmbF-000000005vi-0000-00002
X-2-content-size: 1
+X-3-content-type: application/octet-stream
+X-3-filename: example4.txt
+X-3-charset:
+X-3-boundary:
+X-3-content-disposition: attachment
+X-3-content-transfer-encoding: base64
+X-3-content-id:
+X-3-content-description:
+X-3-is-multipart: 0
+X-3-is-coverletter: 0
+X-3-is-rfc822: 0
+X-3-decode-filename: TESTSUITE/spool/scan/10HmbF-000000005vi-0000/10HmbF-000000005vi-0000-00003
+X-3-content-size: 1

------=_MIME_BOUNDARY_000_695039
Content-Type: text/plain
@@ -474,5 +487,14 @@ Content-Transfer-Encoding: BASE64

QmVpc3BpZWwK

+------=_MIME_BOUNDARY_000_695039
+Content-Type: application/octet-stream
+Content-Disposition: attachment;
+    name*0="example4";
+    name*1=".txt"
+Content-Transfer-Encoding: BASE64
+
+QmVpc3BpZWwK
+
 ------=_MIME_BOUNDARY_000_695039--


diff --git a/test/scripts/4000-scanning/4000 b/test/scripts/4000-scanning/4000
index bb2835ed3..b10f13d00 100644
--- a/test/scripts/4000-scanning/4000
+++ b/test/scripts/4000-scanning/4000
@@ -275,6 +275,7 @@ quit
#
#
# Filename using parameter value continuation (RFC 2231 sec. 3)
+#
exim -odi -bs
ehlo test.ex
mail from:<>
@@ -299,6 +300,15 @@ Content-Transfer-Encoding: BASE64

QmVpc3BpZWwK

+------=_MIME_BOUNDARY_000_695039
+Content-Type: application/octet-stream
+Content-Disposition: attachment;
+    name*0="example4";
+    name*1=".txt"
+Content-Transfer-Encoding: BASE64
+
+QmVpc3BpZWwK
+
 ------=_MIME_BOUNDARY_000_695039--
 .
 quit


--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-cvs.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-cvs-unsubscribe@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/