node_modules: update (#290)

Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
Dawid Dziurla
2026-04-28 12:50:45 +02:00
committed by GitHub
parent 63e1562580
commit 42942bc2f8
1077 changed files with 12540 additions and 33773 deletions

View File

@@ -1,7 +1,6 @@
'use strict';
const stream = require('stream');
const Transform = stream.Transform;
const { Transform } = require('stream');
/**
* Escapes dots in the beginning of lines. Ends the stream with <CR><LF>.<CR><LF>
@@ -12,9 +11,7 @@ const Transform = stream.Transform;
class DataStream extends Transform {
constructor(options) {
super(options);
// init Transform
this.options = options || {};
this._curLine = '';
this.inByteCount = 0;
this.outByteCount = 0;
@@ -25,7 +22,7 @@ class DataStream extends Transform {
* Escapes dots
*/
_transform(chunk, encoding, done) {
let chunks = [];
const chunks = [];
let chunklen = 0;
let i,
len,
@@ -53,7 +50,7 @@ class DataStream extends Transform {
lastPos = i + 1;
}
} else if (chunk[i] === 0x0a) {
// .
// \n
if ((i && chunk[i - 1] !== 0x0d) || (!i && this.lastByte !== 0x0d)) {
if (i > lastPos) {
buf = chunk.slice(lastPos, i);

View File

@@ -22,18 +22,14 @@ const errors = require('../errors');
* @param {Function} callback Callback to run with the rocket object once connection is established
*/
function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
let proxy = urllib.parse(proxyUrl);
const proxy = urllib.parse(proxyUrl);
// create a socket connection to the proxy server
let options;
let connect;
let socket;
options = {
const options = {
host: proxy.hostname,
port: Number(proxy.port) ? Number(proxy.port) : proxy.protocol === 'https:' ? 443 : 80
};
let connect;
if (proxy.protocol === 'https:') {
// we can use untrusted proxies as long as we verify actual SMTP certificates
options.rejectUnauthorized = false;
@@ -42,10 +38,12 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
connect = net.connect.bind(net);
}
let socket;
// Error harness for initial connection. Once connection is established, the responsibility
// to handle errors is passed to whoever uses this socket
let finished = false;
let tempSocketErr = err => {
const tempSocketErr = err => {
if (finished) {
return;
}
@@ -58,8 +56,8 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
callback(err);
};
let timeoutErr = () => {
let err = new Error('Proxy socket timed out');
const timeoutErr = () => {
const err = new Error('Proxy socket timed out');
err.code = 'ETIMEDOUT';
tempSocketErr(err);
};
@@ -69,7 +67,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
return;
}
let reqHeaders = {
const reqHeaders = {
Host: destinationHost + ':' + destinationPort,
Connection: 'close'
};
@@ -93,7 +91,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
);
let headers = '';
let onSocketData = chunk => {
const onSocketData = chunk => {
let match;
let remainder;
@@ -122,7 +120,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
} catch (_E) {
// ignore
}
let err = new Error('Invalid response from proxy' + ((match && ': ' + match[1]) || ''));
const err = new Error('Invalid response from proxy' + ((match && ': ' + match[1]) || ''));
err.code = errors.EPROXY;
return callback(err);
}

View File

@@ -1,13 +1,13 @@
'use strict';
const packageInfo = require('../../package.json');
const EventEmitter = require('events').EventEmitter;
const { EventEmitter } = require('events');
const net = require('net');
const tls = require('tls');
const os = require('os');
const crypto = require('crypto');
const DataStream = require('./data-stream');
const PassThrough = require('stream').PassThrough;
const { PassThrough } = require('stream');
const shared = require('../shared');
// default timeout values in ms
@@ -17,6 +17,28 @@ const GREETING_TIMEOUT = 30 * 1000; // how much to wait after connection is esta
const DNS_TIMEOUT = 30 * 1000; // how much to wait for resolveHostname
const TEARDOWN_NOOP = () => {}; // reusable no-op handler for absorbing errors during socket teardown
/**
* Re-interpret a server response stored in fake 8-bit byte-container form
* (the result of chunk.toString('binary') in _onData) as UTF-8.
*
* Server reply text has no formally defined charset (RFC 5321 §4.2.1), but
* modern MTAs commonly use UTF-8. The byte-container plumbing in _onData is
* required to reassemble multi-byte sequences split across socket chunks;
* this helper performs the actual decode at the line boundary, falling back
* to the byte-container form when the bytes are not valid UTF-8 so that
* legacy 8-bit replies are still recoverable byte-for-byte.
*/
function decodeServerResponse(str) {
if (!str) {
return str;
}
const utf8 = Buffer.from(str, 'binary').toString('utf8');
// The input is a byte container (each char is in U+0000..U+00FF) so it can never
// already contain U+FFFD; any \uFFFD in the result was inserted by Node's UTF-8
// decoder for invalid bytes, which means we should return the original bytes intact.
return utf8.includes('\uFFFD') ? str : utf8;
}
/**
* Generates a SMTP connection object
*
@@ -68,7 +90,7 @@ class SMTPConnection extends EventEmitter {
this.secureConnection = true;
}
this.name = this.options.name || this._getHostname();
this.name = (this.options.name || this._getHostname()).toString().replace(/[\r\n]+/g, '');
this.logger = shared.getLogger(this.options, {
component: this.options.component || 'smtp-connection',
@@ -76,13 +98,12 @@ class SMTPConnection extends EventEmitter {
});
this.customAuth = new Map();
Object.keys(this.options.customAuth || {}).forEach(key => {
let mapKey = (key || '').toString().trim().toUpperCase();
if (!mapKey) {
return;
for (const key of Object.keys(this.options.customAuth || {})) {
const mapKey = (key || '').toString().trim().toUpperCase();
if (mapKey) {
this.customAuth.set(mapKey, this.options.customAuth[key]);
}
this.customAuth.set(mapKey, this.options.customAuth[key]);
});
}
/**
* Expose version nr, just for the reference
@@ -266,27 +287,7 @@ class SMTPConnection extends EventEmitter {
} else if (this.options.socket) {
// socket object is set up but not yet connected
this._socket = this.options.socket;
return shared.resolveHostname(opts, (err, resolved) => {
if (err) {
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
}
this.logger.debug(
{
tnx: 'dns',
source: opts.host,
resolved: resolved.host,
cached: !!resolved.cached
},
'Resolved %s as %s [cache %s]',
opts.host,
resolved.host,
resolved.cached ? 'hit' : 'miss'
);
Object.keys(resolved).forEach(key => {
if (key.charAt(0) !== '_' && resolved[key]) {
opts[key] = resolved[key];
}
});
return this._resolveAndConnect(opts, _resolved => {
try {
this._socket.connect(this.port, this.host, () => {
this._socket.setKeepAlive(true);
@@ -297,80 +298,59 @@ class SMTPConnection extends EventEmitter {
return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
}
});
} else if (this.secureConnection) {
// connect using tls
if (this.options.tls) {
Object.keys(this.options.tls).forEach(key => {
opts[key] = this.options.tls[key];
});
}
// ensure servername for SNI
if (this.servername && !opts.servername) {
opts.servername = this.servername;
}
return shared.resolveHostname(opts, (err, resolved) => {
if (err) {
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
}
this.logger.debug(
{
tnx: 'dns',
source: opts.host,
resolved: resolved.host,
cached: !!resolved.cached
},
'Resolved %s as %s [cache %s]',
opts.host,
resolved.host,
resolved.cached ? 'hit' : 'miss'
);
Object.keys(resolved).forEach(key => {
if (key.charAt(0) !== '_' && resolved[key]) {
opts[key] = resolved[key];
}
});
// Store fallback addresses for retry on connection failure
this._fallbackAddresses = (resolved._addresses || []).filter(addr => addr !== opts.host);
this._connectOpts = Object.assign({}, opts);
this._connectToHost(opts, true);
});
} else {
// connect using plaintext
return shared.resolveHostname(opts, (err, resolved) => {
if (err) {
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
}
this.logger.debug(
{
tnx: 'dns',
source: opts.host,
resolved: resolved.host,
cached: !!resolved.cached
},
'Resolved %s as %s [cache %s]',
opts.host,
resolved.host,
resolved.cached ? 'hit' : 'miss'
);
Object.keys(resolved).forEach(key => {
if (key.charAt(0) !== '_' && resolved[key]) {
opts[key] = resolved[key];
}
});
if (this.secureConnection) {
Object.assign(opts, this.options.tls || {});
// ensure servername for SNI
if (this.servername && !opts.servername) {
opts.servername = this.servername;
}
}
return this._resolveAndConnect(opts, resolved => {
// Store fallback addresses for retry on connection failure
this._fallbackAddresses = (resolved._addresses || []).filter(addr => addr !== opts.host);
this._connectOpts = Object.assign({}, opts);
this._connectToHost(opts, false);
this._connectToHost(opts, this.secureConnection);
});
}
}
/**
* Resolves the hostname and applies resolved values to opts,
* then calls the provided callback with the resolved data
*
* @param {Object} opts Connection options (modified in place)
* @param {Function} callback Called with resolved data on success
*/
_resolveAndConnect(opts, callback) {
return shared.resolveHostname(opts, (err, resolved) => {
if (err) {
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
}
this.logger.debug(
{
tnx: 'dns',
source: opts.host,
resolved: resolved.host,
cached: !!resolved.cached
},
'Resolved %s as %s [cache %s]',
opts.host,
resolved.host,
resolved.cached ? 'hit' : 'miss'
);
for (const key of Object.keys(resolved)) {
if (key.charAt(0) !== '_' && resolved[key]) {
opts[key] = resolved[key];
}
}
callback(resolved);
});
}
/**
* Attempts to connect to the specified host address
*
@@ -381,7 +361,7 @@ class SMTPConnection extends EventEmitter {
this._connectionAttemptId++;
const currentAttemptId = this._connectionAttemptId;
let connectFn = secure ? tls.connect : net.connect;
const connectFn = secure ? tls.connect : net.connect;
try {
this._socket = connectFn(opts, () => {
// Ignore callback if this is a stale connection attempt
@@ -418,7 +398,7 @@ class SMTPConnection extends EventEmitter {
clearTimeout(this._connectionTimeout);
// Check if we have fallback addresses to try
let canFallback = this._fallbackAddresses && this._fallbackAddresses.length && this.stage === 'init' && !this._destroyed;
const canFallback = this._fallbackAddresses && this._fallbackAddresses.length && this.stage === 'init' && !this._destroyed;
if (!canFallback) {
// No more fallback addresses, report the error
@@ -426,7 +406,7 @@ class SMTPConnection extends EventEmitter {
return;
}
let nextHost = this._fallbackAddresses.shift();
const nextHost = this._fallbackAddresses.shift();
this.logger.info(
{
@@ -478,12 +458,7 @@ class SMTPConnection extends EventEmitter {
}
this._closing = true;
let closeMethod = 'end';
if (this.stage === 'init') {
// Close the socket immediately when connection timed out
closeMethod = 'destroy';
}
const closeMethod = this.stage === 'init' ? 'destroy' : 'end';
this.logger.debug(
{
@@ -493,7 +468,7 @@ class SMTPConnection extends EventEmitter {
closeMethod
);
let socket = (this._socket && this._socket.socket) || this._socket;
const socket = (this._socket && this._socket.socket) || this._socket;
if (socket && !socket.destroyed) {
try {
@@ -551,11 +526,11 @@ class SMTPConnection extends EventEmitter {
}
if (this.customAuth.has(this._authMethod)) {
let handler = this.customAuth.get(this._authMethod);
const handler = this.customAuth.get(this._authMethod);
let lastResponse;
let returned = false;
let resolve = () => {
const resolve = () => {
if (returned) {
return;
}
@@ -574,7 +549,7 @@ class SMTPConnection extends EventEmitter {
callback(null, true);
};
let reject = err => {
const reject = err => {
if (returned) {
return;
}
@@ -582,7 +557,7 @@ class SMTPConnection extends EventEmitter {
callback(this._formatError(err, 'EAUTH', lastResponse, 'AUTH ' + this._authMethod));
};
let handlerResponse = handler({
const handlerResponse = handler({
auth: this._auth,
method: this._authMethod,
@@ -709,7 +684,7 @@ class SMTPConnection extends EventEmitter {
// ensure that callback is only called once
let returned = false;
let callback = function () {
const callback = function () {
if (returned) {
return;
}
@@ -722,11 +697,11 @@ class SMTPConnection extends EventEmitter {
message.on('error', err => callback(this._formatError(err, 'ESTREAM', false, 'API')));
}
let startTime = Date.now();
const startTime = Date.now();
this._setEnvelope(envelope, (err, info) => {
if (err) {
// create passthrough stream to consume to prevent OOM
let stream = new PassThrough();
const stream = new PassThrough();
if (typeof message.pipe === 'function') {
message.pipe(stream);
} else {
@@ -736,8 +711,8 @@ class SMTPConnection extends EventEmitter {
return callback(err);
}
let envelopeTime = Date.now();
let stream = this._createSendStream((err, str) => {
const envelopeTime = Date.now();
const stream = this._createSendStream((err, str) => {
if (err) {
return callback(err);
}
@@ -921,7 +896,7 @@ class SMTPConnection extends EventEmitter {
err.message += ': ' + response;
}
let responseCode = (typeof response === 'string' && Number((response.match(/^\d+/) || [])[0])) || false;
const responseCode = (typeof response === 'string' && Number((response.match(/^\d+/) || [])[0])) || false;
if (responseCode) {
err.responseCode = responseCode;
}
@@ -942,15 +917,15 @@ class SMTPConnection extends EventEmitter {
let serverResponse = false;
if (this._remainder && this._remainder.trim()) {
this.lastServerResponse = serverResponse = decodeServerResponse(this._remainder.trim());
if (this.options.debug || this.options.transactionLog) {
this.logger.debug(
{
tnx: 'server'
},
this._remainder.replace(/\r?\n$/, '')
serverResponse
);
}
this.lastServerResponse = serverResponse = this._remainder.trim();
}
this.logger.info(
@@ -1016,15 +991,14 @@ class SMTPConnection extends EventEmitter {
this._socket.removeListener('data', this._onSocketData); // incoming data is going to be gibberish from this point onwards
this._socket.removeListener('timeout', this._onSocketTimeout); // timeout will be re-set for the new socket object
let socketPlain = this._socket;
let opts = {
socket: this._socket,
host: this.host
};
Object.keys(this.options.tls || {}).forEach(key => {
opts[key] = this.options.tls[key];
});
const socketPlain = this._socket;
const opts = Object.assign(
{
socket: this._socket,
host: this.host
},
this.options.tls || {}
);
// ensure servername for SNI
if (this.servername && !opts.servername) {
@@ -1071,7 +1045,7 @@ class SMTPConnection extends EventEmitter {
return false;
}
let str = (this.lastServerResponse = (this._responseQueue.shift() || '').toString());
let str = (this.lastServerResponse = decodeServerResponse((this._responseQueue.shift() || '').toString()));
if (/^\d+-/.test(str.split('\n').pop())) {
// keep waiting for the final part of multiline response
@@ -1092,7 +1066,7 @@ class SMTPConnection extends EventEmitter {
setImmediate(() => this._processResponse());
}
let action = this._responseActions.shift();
const action = this._responseActions.shift();
if (typeof action === 'function') {
action.call(this, str);
@@ -1140,7 +1114,7 @@ class SMTPConnection extends EventEmitter {
* {from:{address:'...',name:'...'}, to:[address:'...',name:'...']}
*/
_setEnvelope(envelope, callback) {
let args = [];
const args = [];
let useSmtpUtf8 = false;
this._envelope = envelope || {};
@@ -1175,7 +1149,7 @@ class SMTPConnection extends EventEmitter {
}
// clone the recipients array for latter manipulation
this._envelope.rcptQueue = JSON.parse(JSON.stringify(this._envelope.to || []));
this._envelope.rcptQueue = [].concat(this._envelope.to || []);
this._envelope.rejected = [];
this._envelope.rejectedErrors = [];
this._envelope.accepted = [];
@@ -1207,7 +1181,10 @@ class SMTPConnection extends EventEmitter {
}
if (this._envelope.size && this._supportedExtensions.includes('SIZE')) {
args.push('SIZE=' + this._envelope.size);
const sizeValue = Number(this._envelope.size) || 0;
if (sizeValue > 0) {
args.push('SIZE=' + sizeValue);
}
}
// If the server supports DSN and the envelope includes an DSN prop
@@ -1260,7 +1237,7 @@ class SMTPConnection extends EventEmitter {
throw new Error('ret: ' + JSON.stringify(ret));
}
let envid = (params.envid || params.id || '').toString() || null;
const envid = (params.envid || params.id || '').toString() || null;
let notify = params.notify || null;
if (notify) {
@@ -1268,8 +1245,8 @@ class SMTPConnection extends EventEmitter {
notify = notify.split(',');
}
notify = notify.map(n => n.trim().toUpperCase());
let validNotify = ['NEVER', 'SUCCESS', 'FAILURE', 'DELAY'];
let invalidNotify = notify.filter(n => !validNotify.includes(n));
const validNotify = ['NEVER', 'SUCCESS', 'FAILURE', 'DELAY'];
const invalidNotify = notify.filter(n => !validNotify.includes(n));
if (invalidNotify.length || (notify.length > 1 && notify.includes('NEVER'))) {
throw new Error('notify: ' + JSON.stringify(notify.join(',')));
}
@@ -1290,7 +1267,7 @@ class SMTPConnection extends EventEmitter {
}
_getDsnRcptToArgs() {
let args = [];
const args = [];
// If the server supports DSN and the envelope includes an DSN prop
// then append DSN params to the RCPT TO command
if (this._envelope.dsn && this._supportedExtensions.includes('DSN')) {
@@ -1305,12 +1282,11 @@ class SMTPConnection extends EventEmitter {
}
_createSendStream(callback) {
let dataStream = new DataStream();
let logStream;
const dataStream = new DataStream();
if (this.options.lmtp) {
this._envelope.accepted.forEach((recipient, i) => {
let final = i === this._envelope.accepted.length - 1;
const final = i === this._envelope.accepted.length - 1;
this._responseActions.push(str => {
this._actionLMTPStream(recipient, final, str, callback);
});
@@ -1326,7 +1302,7 @@ class SMTPConnection extends EventEmitter {
});
if (this.options.debug) {
logStream = new PassThrough();
const logStream = new PassThrough();
logStream.on('readable', () => {
let chunk;
while ((chunk = logStream.read())) {
@@ -1604,24 +1580,21 @@ class SMTPConnection extends EventEmitter {
* @param {String} str Message from the server
*/
_actionAUTH_CRAM_MD5(str, callback) {
let challengeMatch = str.match(/^334\s+(.+)$/);
let challengeString = '';
const challengeMatch = str.match(/^334\s+(.+)$/);
if (!challengeMatch) {
return callback(
this._formatError('Invalid login sequence while waiting for server challenge string', 'EAUTH', str, 'AUTH CRAM-MD5')
);
} else {
challengeString = challengeMatch[1];
}
// Decode from base64
let base64decoded = Buffer.from(challengeString, 'base64').toString('ascii'),
hmacMD5 = crypto.createHmac('md5', this._auth.credentials.pass);
const base64decoded = Buffer.from(challengeMatch[1], 'base64').toString('ascii');
const hmacMD5 = crypto.createHmac('md5', this._auth.credentials.pass);
hmacMD5.update(base64decoded);
let prepended = this._auth.credentials.user + ' ' + hmacMD5.digest('hex');
const prepended = this._auth.credentials.user + ' ' + hmacMD5.digest('hex');
this._responseActions.push(str => {
this._actionAUTH_CRAM_MD5_PASS(str, callback);
@@ -1742,39 +1715,29 @@ class SMTPConnection extends EventEmitter {
* @param {String} str Message from the server
*/
_actionMAIL(str, callback) {
let message, curRecipient;
if (Number(str.charAt(0)) !== 2) {
if (this._usingSmtpUtf8 && /^550 /.test(str) && /[\x80-\uFFFF]/.test(this._envelope.from)) {
message = 'Internationalized mailbox name not allowed';
} else {
message = 'Mail command failed';
}
const message =
this._usingSmtpUtf8 && /^550 /.test(str) && /[\x80-\uFFFF]/.test(this._envelope.from)
? 'Internationalized mailbox name not allowed'
: 'Mail command failed';
return callback(this._formatError(message, 'EENVELOPE', str, 'MAIL FROM'));
}
if (!this._envelope.rcptQueue.length) {
return callback(this._formatError("Can't send mail - no recipients defined", 'EENVELOPE', false, 'API'));
} else {
this._recipientQueue = [];
if (this._supportedExtensions.includes('PIPELINING')) {
while (this._envelope.rcptQueue.length) {
curRecipient = this._envelope.rcptQueue.shift();
this._recipientQueue.push(curRecipient);
this._responseActions.push(str => {
this._actionRCPT(str, callback);
});
this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
}
} else {
curRecipient = this._envelope.rcptQueue.shift();
this._recipientQueue.push(curRecipient);
this._responseActions.push(str => {
this._actionRCPT(str, callback);
});
this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
}
}
this._recipientQueue = [];
const usePipelining = this._supportedExtensions.includes('PIPELINING');
do {
const curRecipient = this._envelope.rcptQueue.shift();
this._recipientQueue.push(curRecipient);
this._responseActions.push(str => {
this._actionRCPT(str, callback);
});
this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
} while (usePipelining && this._envelope.rcptQueue.length);
}
/**
@@ -1783,16 +1746,14 @@ class SMTPConnection extends EventEmitter {
* @param {String} str Message from the server
*/
_actionRCPT(str, callback) {
let message,
err,
curRecipient = this._recipientQueue.shift();
let err;
const curRecipient = this._recipientQueue.shift();
if (Number(str.charAt(0)) !== 2) {
// this is a soft error
if (this._usingSmtpUtf8 && /^553 /.test(str) && /[\x80-\uFFFF]/.test(curRecipient)) {
message = 'Internationalized mailbox name not allowed';
} else {
message = 'Recipient command failed';
}
const message =
this._usingSmtpUtf8 && /^553 /.test(str) && /[\x80-\uFFFF]/.test(curRecipient)
? 'Internationalized mailbox name not allowed'
: 'Recipient command failed';
this._envelope.rejected.push(curRecipient);
// store error for the failed recipient
err = this._formatError(message, 'EENVELOPE', str, 'RCPT TO');
@@ -1815,12 +1776,12 @@ class SMTPConnection extends EventEmitter {
return callback(err);
}
} else if (this._envelope.rcptQueue.length) {
curRecipient = this._envelope.rcptQueue.shift();
this._recipientQueue.push(curRecipient);
const nextRecipient = this._envelope.rcptQueue.shift();
this._recipientQueue.push(nextRecipient);
this._responseActions.push(str => {
this._actionRCPT(str, callback);
});
this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
this._sendCommand('RCPT TO:<' + nextRecipient + '>' + this._getDsnRcptToArgs());
}
}
@@ -1836,7 +1797,7 @@ class SMTPConnection extends EventEmitter {
return callback(this._formatError('Data command failed', 'EENVELOPE', str, 'DATA'));
}
let response = {
const response = {
accepted: this._envelope.accepted,
rejected: this._envelope.rejected
};
@@ -1860,12 +1821,9 @@ class SMTPConnection extends EventEmitter {
*/
_actionSMTPStream(str, callback) {
if (Number(str.charAt(0)) !== 2) {
// Message failed
return callback(this._formatError('Message failed', 'EMESSAGE', str, 'DATA'));
} else {
// Message sent succesfully
return callback(null, str);
}
return callback(null, str);
}
/**