mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-29 12:09:20 +03:00
Also fix MIME header parsing in JavaScript, read issue #1403
This commit is contained in:
parent
0914ede3a3
commit
ec9197cb85
8 changed files with 314 additions and 223 deletions
|
|
@ -1,17 +1,5 @@
|
|||
//import { b64Encode } from 'Common/Utils';
|
||||
|
||||
const
|
||||
// RFC2045
|
||||
QPDecodeParams = [/=([0-9A-F]{2})/g, (...args) => String.fromCharCode(parseInt(args[1], 16))],
|
||||
QPDecode = data => data.replace(/=\r?\n/g, '').replace(...QPDecodeParams),
|
||||
decodeText = (charset, data) => {
|
||||
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});
|
||||
}
|
||||
};
|
||||
import { decodeEncodedWords, BDecode, BEncode, QPDecode, decodeText } from 'Mime/Encoding';
|
||||
import { addressparser } from 'Mime/Address';
|
||||
|
||||
export function ParseMime(text)
|
||||
{
|
||||
|
|
@ -27,7 +15,49 @@ export function ParseMime(text)
|
|||
this.bodyEnd = 0;
|
||||
this.boundary = '';
|
||||
this.bodyText = '';
|
||||
this.headers = {};
|
||||
// https://datatracker.ietf.org/doc/html/rfc2822#section-3.6
|
||||
this.headers = {
|
||||
// Required
|
||||
date = null,
|
||||
from = [], // mailbox-list
|
||||
// Optional
|
||||
sender = [], // MUST occur with multi-address
|
||||
'reply-to' = [], // address-list
|
||||
to = [], // address-list
|
||||
cc = [], // address-list
|
||||
bcc = [], // address-list
|
||||
'message-id' = '', // msg-id SHOULD be present
|
||||
'in-reply-to' = '', // 1*msg-id SHOULD occur in some replies
|
||||
references = '', // 1*msg-id SHOULD occur in some replies
|
||||
subject = '', // unstructured
|
||||
// Optional unlimited
|
||||
comments = [], // unstructured
|
||||
keywords = [], // phrase *("," phrase)
|
||||
// https://datatracker.ietf.org/doc/html/rfc2822#section-3.6.6
|
||||
trace = [],
|
||||
'resent-date' = [],
|
||||
'resent-from' = [],
|
||||
'resent-sender' = [],
|
||||
'resent-to' = [],
|
||||
'resent-cc' = [],
|
||||
'resent-bcc' = [],
|
||||
'resent-msg-id' = [],
|
||||
// optional others outside RFC2822
|
||||
'mime-version' = '',
|
||||
'content-transfer-encoding' = '',
|
||||
'content-type' = '',
|
||||
'delivered-to' = '', // angle-addr
|
||||
'return-path' = '', // angle-addr
|
||||
'received' = [],
|
||||
'authentication-results' = '', // dkim, spf, dmarc
|
||||
'dkim-signature' = '',
|
||||
'x-rspamd-queue-id' = '',
|
||||
'x-rspamd-action' = '',
|
||||
'x-spamd-bar' = '',
|
||||
'x-rspamd-server' = '',
|
||||
'x-spamd-result' = '',
|
||||
'x-remote-address' = '',
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
|
|
@ -54,7 +84,7 @@ export function ParseMime(text)
|
|||
if ('quoted-printable' == encoding) {
|
||||
body = QPDecode(body);
|
||||
} else if ('base64' == encoding) {
|
||||
body = atob(body.replace(/\r?\n/g, ''));
|
||||
body = BDecode(body.replace(/\r?\n/g, ''));
|
||||
}
|
||||
return decodeText(charset, body);
|
||||
}
|
||||
|
|
@ -68,8 +98,7 @@ export function ParseMime(text)
|
|||
if ('quoted-printable' == encoding) {
|
||||
body = QPDecode(body);
|
||||
}
|
||||
body = btoa(body);
|
||||
// body = b64Encode(body);
|
||||
body = BEncode(body);
|
||||
}
|
||||
return 'data:' + this.headerValue('content-type') + ';base64,' + body;
|
||||
}
|
||||
|
|
@ -92,6 +121,9 @@ export function ParseMime(text)
|
|||
}
|
||||
}
|
||||
|
||||
// mailbox-list or address-list
|
||||
const lists = ['from','reply-to','to','cc','bcc'];
|
||||
|
||||
const ParsePart = (mimePart, start_pos = 0, id = '') =>
|
||||
{
|
||||
let part = new MimePart,
|
||||
|
|
@ -113,11 +145,19 @@ export function ParseMime(text)
|
|||
[...header.matchAll(/;\s*([^;=]+)=\s*"?([^;"]+)"?/g)].forEach(param =>
|
||||
params[param[1].trim().toLowerCase()] = param[2].trim()
|
||||
);
|
||||
// encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
|
||||
match[2] = match[2].trim().replace(/=\?([^?]+)\?(B|Q)\?(.+?)\?=/g, (m, charset, encoding, text) =>
|
||||
decodeText(charset, 'B' == encoding ? atob(text) : QPDecode(text))
|
||||
);
|
||||
headers[match[1].trim().toLowerCase()] = {
|
||||
let field = match[1].trim().toLowerCase();
|
||||
if (lists.includes(field)) {
|
||||
match[2] = addressparser(match[2]);
|
||||
} else if ('keywords' === field) {
|
||||
match[2] = match[2].split(',').forEach(entry => decodeEncodedWords(entry.trim()));
|
||||
match[2] = (headers[field]?.value || []).concat(match[2]);
|
||||
} else {
|
||||
match[2] = decodeEncodedWords(match[2].trim());
|
||||
if ('comments' === field) {
|
||||
match[2] = (headers[field]?.value || []).push(match[2]);
|
||||
}
|
||||
}
|
||||
headers[field] = {
|
||||
value: match[2],
|
||||
params: params
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue