Files
send-mail/node_modules/nodemailer/dist/esm/smtp-connection/data-stream.js
T

103 lines
3.3 KiB
JavaScript
Raw Normal View History

2026-09-15 13:00:32 +02:00
import { Transform } from 'node:stream';
2026-10-03 10:23:35 +02:00
// bytes inserted into the output, shared as they are only ever copied by Buffer.concat
const INSERT_LF = Buffer.from('\n');
const INSERT_LF_DOT = Buffer.from('\n.');
const INSERT_CR = Buffer.from('\r');
const INSERT_DOT = Buffer.from('.');
2020-02-29 23:01:03 +01:00
/**
* Escapes dots in the beginning of lines. Ends the stream with <CR><LF>.<CR><LF>
2026-10-03 10:23:35 +02:00
* Also makes sure that only <CR><LF> sequences are used for linebreaks, bare CR and bare LF
* are both turned into <CR><LF>
2020-02-29 23:01:03 +01:00
*
2026-09-15 13:00:32 +02:00
* @param options Stream options
2020-02-29 23:01:03 +01:00
*/
2026-09-15 13:00:32 +02:00
export default class DataStream extends Transform {
2020-02-29 23:01:03 +01:00
constructor(options) {
super(options);
this.options = options || {};
this.inByteCount = 0;
this.outByteCount = 0;
this.lastByte = false;
}
/**
* Escapes dots
2026-09-15 13:00:32 +02:00
* @internal
2020-02-29 23:01:03 +01:00
*/
_transform(chunk, encoding, done) {
2026-04-28 12:50:45 +02:00
const chunks = [];
2020-02-29 23:01:03 +01:00
let chunklen = 0;
2026-09-15 13:00:32 +02:00
let i, len, lastPos = 0;
2020-02-29 23:01:03 +01:00
let buf;
if (!chunk || !chunk.length) {
return done();
}
if (typeof chunk === 'string') {
chunk = Buffer.from(chunk);
}
this.inByteCount += chunk.length;
for (i = 0, len = chunk.length; i < len; i++) {
2026-10-03 10:23:35 +02:00
const byte = chunk[i];
const prev = i ? chunk[i - 1] : this.lastByte;
let insert = false;
if (prev === 0x0d && byte !== 0x0a) {
// a bare CR becomes CRLF. A receiver that treats a lone CR as a line end would
// otherwise see "\r.\r" as the end of the data (SMTP smuggling), so a dot
// following it is stuffed like at the start of any other line
insert = byte === 0x2e ? INSERT_LF_DOT : INSERT_LF;
2026-09-15 13:00:32 +02:00
}
2026-10-03 10:23:35 +02:00
else if (byte === 0x0a && prev !== 0x0d) {
// a bare LF becomes CRLF
insert = INSERT_CR;
}
else if (byte === 0x2e && (prev === 0x0a || prev === false)) {
// a dot at the start of a line
insert = INSERT_DOT;
}
if (insert) {
if (i > lastPos) {
buf = chunk.slice(lastPos, i);
chunks.push(buf);
chunklen += buf.length;
2020-02-29 23:01:03 +01:00
}
2026-10-03 10:23:35 +02:00
chunks.push(insert);
chunklen += insert.length;
lastPos = i;
2020-02-29 23:01:03 +01:00
}
}
2026-10-03 10:23:35 +02:00
if (chunks.length) {
2020-02-29 23:01:03 +01:00
// add last piece
if (lastPos < chunk.length) {
buf = chunk.slice(lastPos);
chunks.push(buf);
chunklen += buf.length;
}
this.outByteCount += chunklen;
this.push(Buffer.concat(chunks, chunklen));
2026-09-15 13:00:32 +02:00
}
else {
2020-02-29 23:01:03 +01:00
this.outByteCount += chunk.length;
this.push(chunk);
}
this.lastByte = chunk[chunk.length - 1];
done();
}
/**
* Finalizes the stream with a dot on a single line
2026-09-15 13:00:32 +02:00
* @internal
2020-02-29 23:01:03 +01:00
*/
_flush(done) {
let buf;
if (this.lastByte === 0x0a) {
buf = Buffer.from('.\r\n');
2026-09-15 13:00:32 +02:00
}
else if (this.lastByte === 0x0d) {
2020-02-29 23:01:03 +01:00
buf = Buffer.from('\n.\r\n');
2026-09-15 13:00:32 +02:00
}
else {
2020-02-29 23:01:03 +01:00
buf = Buffer.from('\r\n.\r\n');
}
this.outByteCount += buf.length;
this.push(buf);
done();
}
}