'use strict'; const stream = require('stream'); const Transform = stream.Transform; /** * Escapes dots in the beginning of lines. Ends the stream with . * Also makes sure that only sequences are used for linebreaks * * @param {Object} options Stream options */ class DataStream extends Transform { constructor(options) { super(options); // init Transform this.options = options || {}; this._curLine = ''; this.inByteCount = 0; this.outByteCount = 0; this.lastByte = false; } /** * Escapes dots */ _transform(chunk, encoding, done) { let chunks = []; let chunklen = 0; let i, len, lastPos = 0; 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++) { if (chunk[i] === 0x2e) { // . if ((i && chunk[i - 1] === 0x0a) || (!i && (!this.lastByte || this.lastByte === 0x0a))) { buf = chunk.slice(lastPos, i + 1); chunks.push(buf); chunks.push(Buffer.from('.')); chunklen += buf.length + 1; lastPos = i + 1; } } else if (chunk[i] === 0x0a) { // . if ((i && chunk[i - 1] !== 0x0d) || (!i && this.lastByte !== 0x0d)) { if (i > lastPos) { buf = chunk.slice(lastPos, i); chunks.push(buf); chunklen += buf.length + 2; } else { chunklen += 2; } chunks.push(Buffer.from('\r\n')); lastPos = i + 1; } } } if (chunklen) { // 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)); } else { this.outByteCount += chunk.length; this.push(chunk); } this.lastByte = chunk[chunk.length - 1]; done(); } /** * Finalizes the stream with a dot on a single line */ _flush(done) { let buf; if (this.lastByte === 0x0a) { buf = Buffer.from('.\r\n'); } else if (this.lastByte === 0x0d) { buf = Buffer.from('\n.\r\n'); } else { buf = Buffer.from('\r\n.\r\n'); } this.outByteCount += buf.length; this.push(buf); done(); } } module.exports = DataStream;