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/