From 97adf68f6e1de2941a6563bd0c3447c45fdbe17a Mon Sep 17 00:00:00 2001
From: Karl Gustav Sterneberg <kg@roxen.com>
Date: Thu, 3 May 2018 11:36:17 +0200
Subject: [PATCH] MIME: Setter for message boundary prefix.

---
 lib/modules/MIME.pmod/module.pmod | 53 ++++++++++++++++++++++++++++++-
 src/modules/MIME/testsuite.in     | 12 +++++++
 2 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/lib/modules/MIME.pmod/module.pmod b/lib/modules/MIME.pmod/module.pmod
index c1efe16705..f3ee29c940 100644
--- a/lib/modules/MIME.pmod/module.pmod
+++ b/lib/modules/MIME.pmod/module.pmod
@@ -176,15 +176,66 @@ protected int(0..1) has_prefix(string|object s, string prefix)
 
 #endif
 
+private string boundary_prefix;
+
+//! Set a message boundary prefix. The @[MIME.generate_boundary()] will use this
+//! prefix when creating a boundary string.
+//!
+//! @throws
+//!  An error is thrown if @[boundary_prefix] doesn't adhere to @rfc{1521@}.
+//!
+//! @seealso
+//!  @[MIME.generate_boundary()], @[MIME.get_boundary_prefix()]
+//!
+//! @param boundary_prefix
+//!  This must adhere to @rfc{1521@} and can not be longer than 56 characters.
+void set_boundary_prefix(string(8bit) boundary_prefix)
+{
+  // 5 upto 14 chars is randomly added to the boundary so the prefix must not
+  // risking overflowing the max-length of 70 chars
+  if (boundary_prefix && (sizeof(boundary_prefix) + 14) > 70) {
+    error("Too long boundary prefix. The boundary prefix can not be longer "
+          "than 56 characters.\n");
+  }
+
+  sscanf(boundary_prefix, "%*s%[^0-9a-zA-Z'()+_,./:=?-]", string illegal);
+
+  if (illegal && sizeof(illegal)) {
+    error("An illegal character (%q) was found in the boundary prefix.\n",
+          illegal);
+  }
+
+  this::boundary_prefix = boundary_prefix;
+}
+
+//! Returns the @tt{boundary_prefix@} set via @[set_boundary_prefix()].
+//!
+//! @seealso
+//!  @[MIME.set_boundary_prefix()], @[MIME.Message.setboundary()]
+string(8bit) get_boundary_prefix()
+{
+  return boundary_prefix;
+}
+
 //! This function will create a string that can be used as a separator string
-//! for multipart messages.  The generated string is guaranteed not to appear
+//! for multipart messages. If a boundary prefix has been set
+//! using @[MIME.set_boundary_prefix()], the generated string will be prefixed
+//! with the boundary prefix.
+//!
+//! The generated string is guaranteed not to appear
 //! in @tt{base64@}, @tt{quoted-printable@}, or @tt{x-uue@} encoded data.
 //! It is also unlikely to appear in normal text.  This function is used by
 //! the cast method of the @tt{Message@} class if no boundary string is
 //! specified.
 //!
+//! @seealso
+//! @[MIME.set_boundary_prefix()]
+//!
 string generate_boundary( )
 {
+  if (boundary_prefix) {
+    return boundary_prefix + random( 1000000000 );
+  }
   return "'ThIs-RaNdOm-StRiNg-/=_."+random( 1000000000 )+":";
 }
 
diff --git a/src/modules/MIME/testsuite.in b/src/modules/MIME/testsuite.in
index f9b0173f43..204c087927 100644
--- a/src/modules/MIME/testsuite.in
+++ b/src/modules/MIME/testsuite.in
@@ -139,4 +139,16 @@ test_eq([[MIME.Message("MIME-Version: 1.0\r\n\r\nfoo\r\n")->getencoded()]], "foo
 test_equal([[MIME.Message((string)MIME.Message("foo\r\n",
 		(["mImE-veRsion":"1.0"])))->headers]],
 	(["mime-version":"1.0","content-length":"5"]))
+
+test_any([[
+  string pf_in = "My-Pr3f1x(.0/9)";
+  MIME.set_boundary_prefix(pf_in);
+  string pf_out = MIME.get_boundary_prefix();
+  string boundary = MIME.generate_boundary();
+  int nm = sscanf(boundary, pf_out + "=_.%d:", int n);
+  return pf_in == pf_out &&
+         boundary[0..sizeof(pf_out)-1] == pf_out &&
+         nm == 1 && n <= 1000000000;
+]], 1)
+
 END_MARKER
-- 
GitLab