From: Art Cancro Date: Mon, 6 Nov 2023 22:51:13 +0000 (-0500) Subject: Quoted-printable encode HTML messages on the WebCit Server side. X-Git-Tag: v997~96 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=bf499ebf1dac652027fef8a5c78bee3822541bc7 Quoted-printable encode HTML messages on the WebCit Server side. This allows us to do multipart with attachments without nested encoding. --- diff --git a/webcit-ng/server/messages.c b/webcit-ng/server/messages.c index f176156a5..f3cba7c66 100644 --- a/webcit-ng/server/messages.c +++ b/webcit-ng/server/messages.c @@ -163,6 +163,7 @@ void dav_get_message(struct http_transaction *h, struct ctdlsession *c, long msg void dav_put_message(struct http_transaction *h, struct ctdlsession *c, char *euid, long old_msgnum) { char buf[1024]; char *content_type = NULL; + char *content_transfer_encoding = NULL; int n; long new_msgnum; char new_euid[1024]; @@ -213,9 +214,52 @@ void dav_put_message(struct http_transaction *h, struct ctdlsession *c, char *eu // This section content_type = header_val(h, "Content-type"); - ctdl_printf(c, "Content-type: %s\r", (content_type ? content_type : "application/octet-stream")); - ctdl_write(c, HKEY("\r\n")); - ctdl_write(c, h->request_body, h->request_body_length); + content_transfer_encoding = header_val(h, "Content-transfer-encoding"); + + // If the content is already encoded, pass it along as-is + if (!IsEmptyStr(content_transfer_encoding)) { + ctdl_printf(c, "Content-type: %s\r", (content_type ? content_type : "application/octet-stream")); + ctdl_write(c, HKEY("\r\n")); + ctdl_write(c, h->request_body, h->request_body_length); + } + + // But if it's raw, we ought to encode it so it's MIME-friendly. + else { + ctdl_printf(c, "Content-type: %s\r", (content_type ? content_type : "application/octet-stream")); + ctdl_printf(c, "Content-transfer-encoding: quoted-printable\r"); + ctdl_write(c, HKEY("\r\n")); + h->request_body[h->request_body_length] = 0; // make doubly sure it's null terminated. + + // Adapted from https://www.w3.org/Tools/Mail-Transcode/mail-transcode.c + char *s = h->request_body; + char strconv[8]; + int strconv_len; + int n; + for (n = 0; *s; s++) { + if (n >= 73 && *s != 10 && *s != 13) { + ctdl_write(c, HKEY("=\r\n")); + n = 0; + } + if (*s == 10 || *s == 13) { + ctdl_write(c, s, 1); + n = 0; + } + else if (*s<32 || *s==61 || *s>126) { + strconv_len = sprintf(strconv, "=%02X", (unsigned char)*s); + ctdl_write(c, strconv, strconv_len); + n += strconv_len; + } + else if (*s != 32 || (*(s+1) != 10 && *(s+1) != 13)) { + ctdl_write(c, s, 1); + n++; + } + else { + ctdl_write(c, "=20", 3); + n += 3; + } + } + } + if (h->request_body[h->request_body_length] != '\n') { ctdl_write(c, HKEY("\r\n")); } diff --git a/webcit-ng/static/js/util.js b/webcit-ng/static/js/util.js index 725fc4944..1f3ae65f8 100644 --- a/webcit-ng/static/js/util.js +++ b/webcit-ng/static/js/util.js @@ -4,36 +4,37 @@ // disclosure are subject to the GNU General Public License v3. -// Function to encode data in quoted-printable format -// Written by Theriault and Brett Zamir [https://locutus.io/php/quoted_printable_encode/] -function quoted_printable_encode(str) { - const hexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'] - const RFC2045Encode1IN = / \r\n|\r\n|[^!-<>-~ ]/gm - const RFC2045Encode1OUT = function (sMatch) { - // Encode space before CRLF sequence to prevent spaces from being stripped - // Keep hard line breaks intact; CRLF sequences - if (sMatch.length > 1) { - return sMatch.replace(' ', '=20'); - } - // Encode matching character - const chr = sMatch.charCodeAt(0); - return '=' + hexChars[((chr >>> 4) & 15)] + hexChars[(chr & 15)]; - } - // Split lines to 75 characters; the reason it's 75 and not 76 is because softline breaks are - // preceeded by an equal sign; which would be the 76th character. However, if the last line/string - // was exactly 76 characters, then a softline would not be needed. PHP currently softbreaks - // anyway; so this function replicates PHP. - const RFC2045Encode2IN = /.{1,72}(?!\r\n)[^=]{0,3}/g - const RFC2045Encode2OUT = function (sMatch) { - if (sMatch.substr(sMatch.length - 2) === '\r\n') { - return sMatch; - } - return sMatch + '=\r\n'; - } - str = str.replace(RFC2045Encode1IN, RFC2045Encode1OUT).replace(RFC2045Encode2IN, RFC2045Encode2OUT); - // Strip last softline break - return str.substr(0, str.length - 3) -} +// // (We don't need this anymore because we encode on the server side) +// // Function to encode data in quoted-printable format +// // Written by Theriault and Brett Zamir [https://locutus.io/php/quoted_printable_encode/] +// function quoted_printable_encode(str) { +// const hexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'] +// const RFC2045Encode1IN = / \r\n|\r\n|[^!-<>-~ ]/gm +// const RFC2045Encode1OUT = function (sMatch) { +// // Encode space before CRLF sequence to prevent spaces from being stripped +// // Keep hard line breaks intact; CRLF sequences +// if (sMatch.length > 1) { +// return sMatch.replace(' ', '=20'); +// } +// // Encode matching character +// const chr = sMatch.charCodeAt(0); +// return '=' + hexChars[((chr >>> 4) & 15)] + hexChars[(chr & 15)]; +// } +// // Split lines to 75 characters; the reason it's 75 and not 76 is because softline breaks are +// // preceeded by an equal sign; which would be the 76th character. However, if the last line/string +// // was exactly 76 characters, then a softline would not be needed. PHP currently softbreaks +// // anyway; so this function replicates PHP. +// const RFC2045Encode2IN = /.{1,72}(?!\r\n)[^=]{0,3}/g +// const RFC2045Encode2OUT = function (sMatch) { +// if (sMatch.substr(sMatch.length - 2) === '\r\n') { +// return sMatch; +// } +// return sMatch + '=\r\n'; +// } +// str = str.replace(RFC2045Encode1IN, RFC2045Encode1OUT).replace(RFC2045Encode2IN, RFC2045Encode2OUT); +// // Strip last softline break +// return str.substr(0, str.length - 3) +// } // generate a random string -- mainly used for generating one-time-use div names diff --git a/webcit-ng/static/js/view_forum.js b/webcit-ng/static/js/view_forum.js index 86f697cf3..9eebfb79f 100644 --- a/webcit-ng/static/js/view_forum.js +++ b/webcit-ng/static/js/view_forum.js @@ -415,21 +415,11 @@ function forum_save_message(editor_div_name) { + "/dummy_name_for_new_message" + "?wefw=" + wefw + "&subj=" + subj - boundary = randomString(); - body_text = - "--" + boundary + "\r\n" - + "Content-type: text/html\r\n" - + "Content-transfer-encoding: quoted-printable\r\n" - + "\r\n" - + quoted_printable_encode( - "" + document.getElementById("ctdl-editor-body").innerHTML + "" - ) + "\r\n" - + "--" + boundary + "--\r\n" - ; + body_text = "" + document.getElementById("ctdl-editor-body").innerHTML + "\r\n"; var request = new XMLHttpRequest(); request.open("PUT", url, true); - request.setRequestHeader("Content-type", "multipart/mixed; boundary=\"" + boundary + "\""); + request.setRequestHeader("Content-type", "text/html"); request.onreadystatechange = function() { if (request.readyState == 4) { document.body.style.cursor = "default"; diff --git a/webcit-ng/static/js/view_mail.js b/webcit-ng/static/js/view_mail.js index e015466f2..0efdf37f6 100644 --- a/webcit-ng/static/js/view_mail.js +++ b/webcit-ng/static/js/view_mail.js @@ -571,21 +571,11 @@ function mail_send_message() { } } } - boundary = randomString(); - body_text = - "--" + boundary + "\r\n" - + "Content-type: text/html\r\n" - + "Content-transfer-encoding: quoted-printable\r\n" - + "\r\n" - + quoted_printable_encode( - "" + document.getElementById("ctdl-editor-body").innerHTML + "" - ) + "\r\n" - + "--" + boundary + "--\r\n" - ; + body_text = "" + document.getElementById("ctdl-editor-body").innerHTML + "\r\n"; var request = new XMLHttpRequest(); request.open("PUT", url, true); - request.setRequestHeader("Content-type", "multipart/mixed; boundary=\"" + boundary + "\""); + request.setRequestHeader("Content-type", "text/html"); request.onreadystatechange = function() { if (request.readyState == 4) { document.body.style.cursor = "default";