parse-multipart-mixed-mail-pmb
Version:
Split a 'Content-Type: multipart/mixed' email into parsed headers and body parts.
65 lines (53 loc) • 1.92 kB
JavaScript
// -*- coding: utf-8, tab-width: 2 -*-
import mAtt from 'parse-mail-attachment-pmb';
import mustBe from 'typechecks-pmb/must-be';
import splitOnce from 'split-string-or-buffer-once-pmb';
function rebufferize(x) {
return Buffer.from(x, 'latin1');
// … to remind users that we do not know the charset.
}
const EX = function parseMail(raw, opt) {
if (!opt) { return parseMail(raw, true); }
const mail = mAtt.splitParseHeaders(raw);
if (opt.acceptJustText && (mail.cType || '').startsWith('text/')) {
mail.body = [rebufferize(raw)];
return mail;
}
mustBe([['oneOf', EX.supportedMimeTypes]], 'MIME type')(mail.cType);
const boundary = mustBe.nest('MIME boundary in mail headers',
mail.ctDetails.boundary);
let { body } = mail;
body = body.replace(/^This is a multi-part message in MIME format\.\s*/i, '');
if (!body.startsWith('--' + boundary + '\n')) {
throw new Error('The body must start with the MIME boundary.');
}
mail.body = EX.splitBody(body);
return mail;
};
Object.assign(EX, {
supportedMimeTypes: [
'multipart/alternative',
'multipart/mixed',
'multipart/related',
],
splitBody(raw) {
let body = mAtt.normText(raw);
let boundary = mustBe.nest('MIME boundary in first line of body',
(/^\-{2}(\S{1,160})\n/.exec(body) || false)[1]);
const preBound = '\n--';
let part = body.slice(-32 - preBound.length - boundary.length);
part = splitOnce({ sep: preBound, last: true }, part)[1];
if ((part || '').trimRight() !== (boundary + '--')) {
throw new Error('Missing end mark. Incomplete download?');
}
body = body.slice(boundary.length + 3, -preBound.length - part.length);
boundary = preBound + boundary + '\n';
const parts = [];
while (body) {
[part, body] = (splitOnce(boundary, body) || [body]);
parts.push(rebufferize(part));
}
return parts;
},
});
export default EX;