class MailParser extends Transform { constructor(config) { let options = { readableObjectMode: true, writableObjectMode: false }; super(options); this.options = config || {}; this.splitter = new Splitter(); this.finished = false; this.waitingEnd = false; this.headers = false; this.endReceived = false; this.reading = false; this.errored = false; this.tree = false; this.curnode = false; this.waitUntilAttachmentEnd = false; this.attachmentCallback = false; this.hasHtml = false; this.hasText = false; this.text = false; this.html = false; this.textAsHtml = false; this.attachmentList = []; this.splitter.on('readable', () => { if (this.reading) { return false; } this.readData(); }); this.splitter.on('end', () => { this.endReceived = true; if (!this.reading) { this.endStream(); } }); this.splitter.on('error', err => { this.errored = true; if (typeof this.waitingEnd === 'function') { return this.waitingEnd(err); } this.emit('error', err); }); } readData() { if (this.errored) { return false; } this.reading = true; let data = this.splitter.read(); if (data === null) { this.reading = false; if (this.endReceived) { this.endStream(); } return; } this.processChunk(data, err => { if (err) { if (typeof this.waitingEnd === 'function') { return this.waitingEnd(err); } return this.emit('error', err); } setImmediate(() => this.readData()); }); } endStream() { this.finished = true; if (typeof this.waitingEnd === 'function') { this.waitingEnd(); } } _transform(chunk, encoding, done) { if (!chunk || !chunk.length) { return done(); } if (this.splitter.write(chunk) === false) { return this.splitter.once('drain', () => { done(); }); } else { return done(); } } _flush(done) { setImmediate(() => this.splitter.end()); if (this.finished) { return this.cleanup(done); } this.waitingEnd = () => this.cleanup(done); } cleanup(done) { if (this.curnode && this.curnode.decoder) { this.curnode.decoder.end(); } setImmediate(() => { this.push(this.getTextContent()); done(); }); } processHeaders(lines) { let headers = new Map(); (lines || []).forEach(line => { let key = line.key; let value = ((libmime.decodeHeader(line.line) || {}).value || '').toString().trim(); value = Buffer.from(value, 'binary').toString(); switch (key) { case 'content-type': case 'content-disposition': case 'dkim-signature': value = libmime.parseHeaderValue(value); Object.keys(value && value.params || {}).forEach(key => { try { value.params[key] = libmime.decodeWords(value.params[key]); } catch (E) { // ignore, keep as is } }); break; case 'date': value = new Date(value); if (!value || value.toString() === 'Invalid Date' || !value.getTime()) { // date parsing failed :S value = new Date(); } ...
n/a
(input, callback) => { let promise; if (!callback) { promise = new Promise((resolve, reject) => { callback = callbackPromise(resolve, reject); }); } let mail = { attachments: [] }; let parser = new MailParser(); parser.on('headers', headers => { mail.headers = headers; }); parser.on('data', data => { if (data.type === 'text') { Object.keys(data).forEach(key => { if (['text', 'html', 'textAsHtml'].includes(key)) { mail[key] = data[key]; } }); } if (data.type === 'attachment') { mail.attachments.push(data); let chunks = []; let chunklen = 0; data.content.on('readable', () => { let chunk; while ((chunk = data.content.read()) !== null) { chunks.push(chunk); chunklen += chunk.length; } }); data.content.on('end', () => { data.content = Buffer.concat(chunks, chunklen); data.release(); }); } }); parser.on('end', () => { ['subject', 'references', 'date', 'to', 'from', 'to', 'cc', 'bcc', 'message-id', 'in-reply-to', 'reply-to'].forEach(key => { if (mail.headers.has(key)) { mail[key.replace(/\-([a-z])/g, (m, c) => c.toUpperCase())] = mail.headers.get(key); } }); parser.updateImageLinks((attachment, done) => done(false, 'data:' + attachment.contentType + ';base64,' + attachment.content .toString('base64')), (err, html) => { if (err) { return callback(err); } mail.html = html; callback(null, mail); }); }); if (typeof input === 'string') { parser.end(Buffer.from(input)); } else if (Buffer.isBuffer(input)) { parser.end(input); } else { input.pipe(parser); } return promise; }
n/a