Improved MIME charset decoding #662

This commit is contained in:
the-djmaze 2022-11-15 10:38:43 +01:00
parent 456ade5845
commit f23e0d7a03

View file

@ -1,10 +1,16 @@
const const
// RFC2045 // RFC2045
QPDecodeIn = /=([0-9A-F]{2})/g, QPDecodeParams = [/=([0-9A-F]{2})/g, (...args) => String.fromCharCode(parseInt(args[1], 16))],
QPDecodeOut = (...args) => String.fromCharCode(parseInt(args[1], 16)), QPDecode = data => data.replace(/=\r?\n/g, '').replace(...QPDecodeParams),
decodeText = (charset, data) => {
utf8To16 = data => new TextDecoder().decode(Uint8Array.from(data, c => c.charCodeAt(0))); try {
// https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API/Encodings
return new TextDecoder(charset).decode(Uint8Array.from(data, c => c.charCodeAt(0)));
} catch (e) {
console.error({charset:charset,error:e});
}
};
export function ParseMime(text) export function ParseMime(text)
{ {
@ -42,23 +48,14 @@ export function ParseMime(text)
get body() { get body() {
let body = this.bodyRaw, let body = this.bodyRaw,
// charset = this.header('content-type')?.params.charset, charset = this.header('content-type')?.params.charset,
encoding = this.headerValue('content-transfer-encoding'); encoding = this.headerValue('content-transfer-encoding');
if ('quoted-printable' == encoding) { if ('quoted-printable' == encoding) {
body = utf8To16(body.replace(/=\r?\n/g, '').replace(QPDecodeIn, QPDecodeOut)); body = QPDecode(body);
} else if ('base64' == encoding) { } else if ('base64' == encoding) {
body = utf8To16(atob(body.replace(/\r?\n/g, ''))); body = atob(body.replace(/\r?\n/g, ''));
} }
/* return decodeText(charset, body);
// TODO: convert charsets other then utf8
try {
// https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API/Encodings
return new TextDecoder(charset).decode(Uint8Array.from(body, c => c.charCodeAt(0)));
// return new TextDecoder(charset).decode(new TextEncoder().encode(body));
} catch (e) {
}
*/
return body;
} }
get dataUrl() { get dataUrl() {
@ -68,7 +65,7 @@ export function ParseMime(text)
body = body.replace(/\r?\n/g, ''); body = body.replace(/\r?\n/g, '');
} else { } else {
if ('quoted-printable' == encoding) { if ('quoted-printable' == encoding) {
body = body.replace(/=\r?\n/g, '').replace(QPDecodeIn, QPDecodeOut); body = QPDecode(body);
} }
body = btoa(body); body = btoa(body);
} }
@ -115,15 +112,9 @@ export function ParseMime(text)
params[param[1].trim().toLowerCase()] = param[2].trim() params[param[1].trim().toLowerCase()] = param[2].trim()
); );
// encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" // encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
match[2] = match[2].trim().replace(/=\?([^?]+)\?(B|Q)\?(.+?)\?=/g, (m, charset, encoding, text) => { match[2] = match[2].trim().replace(/=\?([^?]+)\?(B|Q)\?(.+?)\?=/g, (m, charset, encoding, text) =>
m = decodeURIComponent( decodeText(charset, 'B' == encoding ? atob(text) : QPDecode(text))
'B' == encoding ? escape(atob(text))
// else 'Q'
: text.replace(/=([A-Z0-9]{2})/, '%$1')
); );
// TODO: convert when charset != utf-8
return m;
});
headers[match[1].trim().toLowerCase()] = { headers[match[1].trim().toLowerCase()] = {
value: match[2], value: match[2],
params: params params: params