The character '/' from base64 alphabet breaks web redirectors if the
message-id from an email is used as part of the URL for redirectors and/
or automatic pointers to an email.

Use the URL safe alphabet from RFC4648 section 5 for message id
generation.

Signed-off-by: Sebastian Andrzej Siewior <sebast...@breakpoint.cc>
---
 base64.c    | 33 +++++++++++++++++++++++----------
 messageid.c |  4 ++--
 mime.h      |  1 +
 protos.h    |  1 +
 sendlib.c   |  9 +++++++++
 5 files changed, 36 insertions(+), 12 deletions(-)

diff --git a/base64.c b/base64.c
index 290f384e16957..cc7106f4358b2 100644
--- a/base64.c
+++ b/base64.c
@@ -56,16 +56,15 @@ void mutt_buffer_to_base64 (BUFFER *out, const unsigned 
char *in, size_t len)
   mutt_buffer_fix_dptr (out);
 }
 
-/* raw bytes to null-terminated base 64 string */
-void mutt_to_base64 (unsigned char *out, const unsigned char *in, size_t len,
-                    size_t olen)
+static void to_base64 (unsigned char *out, const unsigned char *in, size_t len,
+                    size_t olen, const char *dict)
 {
   while (len >= 3 && olen > 4)
   {
-    *out++ = B64Chars[in[0] >> 2];
-    *out++ = B64Chars[((in[0] << 4) & 0x30) | (in[1] >> 4)];
-    *out++ = B64Chars[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
-    *out++ = B64Chars[in[2] & 0x3f];
+    *out++ = dict[in[0] >> 2];
+    *out++ = dict[((in[0] << 4) & 0x30) | (in[1] >> 4)];
+    *out++ = dict[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
+    *out++ = dict[in[2] & 0x3f];
     olen  -= 4;
     len   -= 3;
     in    += 3;
@@ -76,15 +75,29 @@ void mutt_to_base64 (unsigned char *out, const unsigned 
char *in, size_t len,
   {
     unsigned char fragment;
 
-    *out++ = B64Chars[in[0] >> 2];
+    *out++ = dict[in[0] >> 2];
     fragment = (in[0] << 4) & 0x30;
     if (len > 1)
       fragment |= in[1] >> 4;
-    *out++ = B64Chars[fragment];
-    *out++ = (len < 2) ? '=' : B64Chars[(in[1] << 2) & 0x3c];
+    *out++ = dict[fragment];
+    *out++ = (len < 2) ? '=' : dict[(in[1] << 2) & 0x3c];
     *out++ = '=';
   }
   *out = '\0';
+
+}
+
+/* raw bytes to null-terminated base 64 string */
+void mutt_to_base64 (unsigned char *out, const unsigned char *in, size_t len,
+                    size_t olen)
+{
+       to_base64(out, in, len, olen, B64Chars);
+}
+
+void mutt_to_base64_safeurl (unsigned char *out, const unsigned char *in,
+                            size_t len, size_t olen)
+{
+       to_base64(out, in, len, olen, B64Chars_urlsafe);
 }
 
 int mutt_buffer_from_base64 (BUFFER *out, const char *in)
diff --git a/messageid.c b/messageid.c
index e4ac441002626..d96a5d4467102 100644
--- a/messageid.c
+++ b/messageid.c
@@ -49,7 +49,7 @@ static const char *id_format_str (char *dest, size_t destlen, 
size_t col,
   {
     case 'r':
       mutt_random_bytes ((char *)r_raw, sizeof(r_raw));
-      mutt_to_base64 (r_out, r_raw, sizeof(r_raw), sizeof(r_out));
+      mutt_to_base64_safeurl (r_out, r_raw, sizeof(r_raw), sizeof(r_out));
       mutt_format_s (dest, destlen, fmt, (const char *)r_out);
       break;
 
@@ -65,7 +65,7 @@ static const char *id_format_str (char *dest, size_t destlen, 
size_t col,
       for (int i = 0; i < 4; i++)
         z_raw[i] = (uint8_t) (id_data->now >> (3-i)*8u);
       mutt_random_bytes ((char *)z_raw + 4, sizeof(z_raw) - 4);
-      mutt_to_base64 (z_out, z_raw, sizeof(z_raw), sizeof(z_out));
+      mutt_to_base64_safeurl (z_out, z_raw, sizeof(z_raw), sizeof(z_out));
       mutt_format_s (dest, destlen, fmt, (const char *)z_out);
       break;
 
diff --git a/mime.h b/mime.h
index c162ee2b5a2b6..1a876ae27749c 100644
--- a/mime.h
+++ b/mime.h
@@ -63,6 +63,7 @@ enum
 extern const int Index_hex[];
 extern const int Index_64[];
 extern const char B64Chars[];
+extern const char B64Chars_urlsafe[];
 #endif
 
 #define hexval(c) Index_hex[(unsigned int)(c)]
diff --git a/protos.h b/protos.h
index a055d1a2ca266..d760c7d137ed5 100644
--- a/protos.h
+++ b/protos.h
@@ -432,6 +432,7 @@ int safe_symlink (const char *, const char *);
 
 /* base64.c */
 void mutt_to_base64 (unsigned char*, const unsigned char*, size_t, size_t);
+void mutt_to_base64_safeurl (unsigned char*, const unsigned char*, size_t, 
size_t);
 int mutt_from_base64 (char*, const char*, size_t);
 void mutt_buffer_to_base64 (BUFFER *, const unsigned char *, size_t);
 int mutt_buffer_from_base64 (BUFFER *, const char *);
diff --git a/sendlib.c b/sendlib.c
index d9429c2e62629..c2283972f1d33 100644
--- a/sendlib.c
+++ b/sendlib.c
@@ -84,6 +84,15 @@ const char B64Chars[64] = {
   '8', '9', '+', '/'
 };
 
+/* RFC4648 section 5 Base 64 Encoding with URL and Filename Safe Alphabet */
+const char B64Chars_urlsafe[64] = {
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+  'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+  't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+  '8', '9', '-', '_'
+};
+
 static void transform_to_7bit (BODY *a, FILE *fpin);
 
 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
-- 
2.39.2

Reply via email to