UNPKG

nodemailer

Version:

Easy as cake e-mail sending from your Node.js applications

140 lines (114 loc) 3.67 kB
'use strict'; const Transform = require('stream').Transform; /** * Encodes a Buffer into a base64 encoded string * * @param {Buffer} buffer Buffer to convert * @returns {String} base64 encoded string */ function encode(buffer) { if (typeof buffer === 'string') { buffer = Buffer.from(buffer, 'utf-8'); } return buffer.toString('base64'); } /** * Adds soft line breaks to a base64 string * * @param {String} str base64 encoded string that might need line wrapping * @param {Number} [lineLength=76] Maximum allowed length for a line * @returns {String} Soft-wrapped base64 encoded string */ function wrap(str, lineLength) { str = (str || '').toString(); lineLength = lineLength || 76; if (str.length <= lineLength) { return str; } let result = []; let pos = 0; let chunkLength = lineLength * 1024; while (pos < str.length) { let wrappedLines = str.substr(pos, chunkLength).replace(new RegExp('.{' + lineLength + '}', 'g'), '$&\r\n'); result.push(wrappedLines); pos += chunkLength; } return result.join(''); } /** * Creates a transform stream for encoding data to base64 encoding * * @constructor * @param {Object} options Stream options * @param {Number} [options.lineLength=76] Maximum length for lines, set to false to disable wrapping */ class Encoder extends Transform { constructor(options) { super(); this.options = options || {}; if (this.options.lineLength !== false) { this.options.lineLength = this.options.lineLength || 76; } this._curLine = ''; this._remainingBytes = false; this.inputBytes = 0; this.outputBytes = 0; } _transform(chunk, encoding, done) { if (encoding !== 'buffer') { chunk = Buffer.from(chunk, encoding); } if (!chunk || !chunk.length) { return setImmediate(done); } this.inputBytes += chunk.length; if (this._remainingBytes && this._remainingBytes.length) { chunk = Buffer.concat([this._remainingBytes, chunk], this._remainingBytes.length + chunk.length); this._remainingBytes = false; } if (chunk.length % 3) { this._remainingBytes = chunk.slice(chunk.length - (chunk.length % 3)); chunk = chunk.slice(0, chunk.length - (chunk.length % 3)); } else { this._remainingBytes = false; } let b64 = this._curLine + encode(chunk); if (this.options.lineLength) { b64 = wrap(b64, this.options.lineLength); let lastLF = b64.lastIndexOf('\n'); if (lastLF < 0) { this._curLine = b64; b64 = ''; } else { this._curLine = b64.substring(lastLF + 1); b64 = b64.substring(0, lastLF + 1); if (b64 && !b64.endsWith('\r\n')) { b64 += '\r\n'; } } } else { this._curLine = ''; } if (b64) { this.outputBytes += b64.length; this.push(Buffer.from(b64, 'ascii')); } setImmediate(done); } _flush(done) { if (this._remainingBytes && this._remainingBytes.length) { this._curLine += encode(this._remainingBytes); } if (this._curLine) { this.outputBytes += this._curLine.length; this.push(Buffer.from(this._curLine, 'ascii')); this._curLine = ''; } done(); } } module.exports = { encode, wrap, Encoder };