Files
send-mail/node_modules/nodemailer/dist/cjs/base64/index.js
T

158 lines
5.4 KiB
JavaScript
Raw Normal View History

2026-09-15 13:00:32 +02:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Encoder = void 0;
exports.encode = encode;
exports.wrap = wrap;
const node_stream_1 = require("node:stream");
2020-02-29 23:01:03 +01:00
/**
* Encodes a Buffer into a base64 encoded string
*
2026-09-15 13:00:32 +02:00
* @param buffer Buffer to convert
* @returns base64 encoded string
2020-02-29 23:01:03 +01:00
*/
function encode(buffer) {
if (typeof buffer === 'string') {
buffer = Buffer.from(buffer, 'utf-8');
}
return buffer.toString('base64');
}
2026-10-10 17:27:57 +02:00
/**
* Turns a line length option into a whole number of characters, the default for anything unusable
*/
function normalizeLineLength(lineLength) {
const length = Math.floor(Number(lineLength));
return Number.isFinite(length) && length >= 1 ? length : 76;
}
/**
* Splits the bytes of `src` into lines of `lineLength` bytes, each followed by a line break. With
* `final` set the last line, which may be shorter, gets no line break; otherwise only complete
* lines are taken and the rest is left for the caller
*
* @param src Bytes to wrap
* @param lineLength Line length
* @param final Whether `src` ends the output
* @returns The wrapped bytes and the number of trailing bytes not taken
*/
function wrapBuffer(src, lineLength, final) {
const lines = Math.ceil(src.length / lineLength);
// the last line waits for more data unless this is the end: whether it gets a line break
// depends on whether anything follows it
const complete = Math.max(lines - 1, 0);
let rest = src.length - complete * lineLength;
const output = Buffer.allocUnsafe(complete * (lineLength + 2) + (final ? rest : 0));
let to = 0;
for (let from = 0; from < complete * lineLength; from += lineLength) {
src.copy(output, to, from, from + lineLength);
to += lineLength;
output[to++] = 0x0d;
output[to++] = 0x0a;
}
if (final) {
to += src.copy(output, to, complete * lineLength);
rest = 0;
}
if (to !== output.length) {
// never hand out bytes of the unfilled allocation
throw new Error('Unexpected wrapped length');
}
return { output, rest };
}
2020-02-29 23:01:03 +01:00
/**
* Adds soft line breaks to a base64 string
*
2026-09-15 13:00:32 +02:00
* @param str base64 encoded string that might need line wrapping
* @param [lineLength=76] Maximum allowed length for a line
* @returns Soft-wrapped base64 encoded string
2020-02-29 23:01:03 +01:00
*/
function wrap(str, lineLength) {
str = (str || '').toString();
2026-10-01 09:48:38 +02:00
// a negative length would step backwards through the input and never finish
lineLength = Math.max(Number(lineLength) || 76, 1);
2020-02-29 23:01:03 +01:00
if (str.length <= lineLength) {
return str;
}
2026-04-28 12:50:45 +02:00
const result = [];
2020-02-29 23:01:03 +01:00
let pos = 0;
2026-04-28 12:50:45 +02:00
const chunkLength = lineLength * 1024;
const wrapRegex = new RegExp('.{' + lineLength + '}', 'g');
2020-02-29 23:01:03 +01:00
while (pos < str.length) {
2026-04-28 12:50:45 +02:00
const wrappedLines = str.substr(pos, chunkLength).replace(wrapRegex, '$&\r\n').trim();
2020-02-29 23:01:03 +01:00
result.push(wrappedLines);
pos += chunkLength;
}
2026-04-28 12:50:45 +02:00
return result.join('\r\n').trim();
2020-02-29 23:01:03 +01:00
}
/**
* Creates a transform stream for encoding data to base64 encoding
*
2026-10-10 17:27:57 +02:00
* The output is the same as `wrap(encode(input), lineLength)` no matter how the input is split
* into chunks: every line but the last one ends with a line break, the last one does not
*
2020-02-29 23:01:03 +01:00
* @constructor
2026-09-15 13:00:32 +02:00
* @param options Stream options
* @param [options.lineLength=76] Maximum length for lines, set to false to disable wrapping
2020-02-29 23:01:03 +01:00
*/
2026-09-15 13:00:32 +02:00
class Encoder extends node_stream_1.Transform {
2020-02-29 23:01:03 +01:00
constructor(options) {
super();
this.options = options || {};
if (this.options.lineLength !== false) {
2026-10-10 17:27:57 +02:00
this.options.lineLength = normalizeLineLength(this.options.lineLength);
2020-02-29 23:01:03 +01:00
}
this._curLine = '';
this._remainingBytes = false;
this.inputBytes = 0;
this.outputBytes = 0;
}
2026-10-10 17:27:57 +02:00
/**
* Emits the encoded characters `b64` that follow the current line, keeping what can not be
* emitted yet as the new current line
*
* @internal
*/
_emit(b64, final) {
const src = Buffer.from(this._curLine + b64, 'latin1');
if (!src.length) {
return;
}
let output = src;
if (this.options.lineLength) {
const wrapped = wrapBuffer(src, this.options.lineLength, final);
output = wrapped.output;
this._curLine = wrapped.rest ? src.toString('latin1', src.length - wrapped.rest) : '';
}
else {
this._curLine = '';
}
if (output.length) {
this.outputBytes += output.length;
this.push(output);
}
}
2026-09-15 13:00:32 +02:00
/** @internal */
2020-02-29 23:01:03 +01:00
_transform(chunk, encoding, done) {
2026-09-15 13:00:32 +02:00
let buf = encoding !== 'buffer' ? Buffer.from(chunk, encoding) : chunk;
if (!buf || !buf.length) {
2026-10-10 17:27:57 +02:00
return done();
2020-02-29 23:01:03 +01:00
}
2026-09-15 13:00:32 +02:00
this.inputBytes += buf.length;
2026-10-10 17:27:57 +02:00
if (this._remainingBytes) {
2026-09-15 13:00:32 +02:00
buf = Buffer.concat([this._remainingBytes, buf], this._remainingBytes.length + buf.length);
2020-02-29 23:01:03 +01:00
this._remainingBytes = false;
}
2026-10-10 17:27:57 +02:00
const extra = buf.length % 3;
if (extra) {
this._remainingBytes = buf.subarray(buf.length - extra);
buf = buf.subarray(0, buf.length - extra);
2026-09-15 13:00:32 +02:00
}
2026-10-10 17:27:57 +02:00
this._emit(encode(buf), false);
done();
2020-02-29 23:01:03 +01:00
}
2026-09-15 13:00:32 +02:00
/** @internal */
2020-02-29 23:01:03 +01:00
_flush(done) {
2026-10-10 17:27:57 +02:00
this._emit(this._remainingBytes ? encode(this._remainingBytes) : '', true);
this._remainingBytes = false;
2020-02-29 23:01:03 +01:00
done();
}
}
2026-09-15 13:00:32 +02:00
exports.Encoder = Encoder;