mirror of
https://github.com/dawidd6/action-send-mail.git
synced 2026-09-09 05:56:37 +07:00
node_modules: update (#314)
Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
+80
-19
@@ -5,19 +5,39 @@
|
||||
const base64 = require('../base64');
|
||||
const qp = require('../qp');
|
||||
const mimeTypes = require('./mime-types');
|
||||
const { isProtoKey } = require('../shared/objects');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Checks if a value is plaintext string (uses only printable 7bit chars)
|
||||
*
|
||||
* When isParam is set the value is destined for a header parameter, so HT, CR and LF
|
||||
* are not plaintext either: a header parameter has no way to carry them. HT is a valid
|
||||
* fold point, so folding and unfolding a header would rewrite it as a space, and CR/LF
|
||||
* cannot appear in a header value at all. DEL is neither a token character nor qtext,
|
||||
* so it can not be carried bare or quoted. Such values have to go through the rfc2231
|
||||
* parameter continuation encoding instead, the same way a quote already does.
|
||||
*
|
||||
* @param {String} value String to be tested
|
||||
* @param {Boolean} [isParam] Set to true if the value is a header parameter value
|
||||
* @returns {Boolean} true if it is a plaintext string
|
||||
*/
|
||||
isPlainText(value, isParam) {
|
||||
const re = isParam ? /[\x00-\x08\x0b\x0c\x0e-\x1f"\u0080-\uFFFF]/ : /[\x00-\x08\x0b\x0c\x0e-\x1f\u0080-\uFFFF]/;
|
||||
const re = isParam ? /[\x00-\x1f\x7f"\u0080-\uFFFF]/ : /[\x00-\x08\x0b\x0c\x0e-\x1f\u0080-\uFFFF]/;
|
||||
return typeof value === 'string' && !re.test(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wraps a value into a quoted-string. Inside one a quote would end the string early
|
||||
* and a backslash would escape whatever follows it, so both go out as quoted-pairs.
|
||||
*
|
||||
* @param {String} value String to be quoted
|
||||
* @returns {String} The value as a quoted-string, quotes included
|
||||
*/
|
||||
quoteString(value) {
|
||||
return '"' + (value || '').toString().replace(/["\\]/g, '\\$&') + '"';
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if a multi line string containes lines longer than the selected value.
|
||||
*
|
||||
@@ -80,8 +100,10 @@ module.exports = {
|
||||
for (let i = 0, len = encodedStr.length; i < len; i++) {
|
||||
let chr = encodedStr.charAt(i);
|
||||
|
||||
if (/[\ud83c\ud83d\ud83e]/.test(chr) && i < len - 1) {
|
||||
// composite emoji byte, so add the next byte as well
|
||||
if (/[\ud800-\udbff]/.test(chr) && /[\udc00-\udfff]/.test(encodedStr.charAt(i + 1))) {
|
||||
// leading surrogate, so add the trailing surrogate as well
|
||||
// an unpaired one must not swallow the next unit, that would destroy
|
||||
// a valid pair following it
|
||||
chr += encodedStr.charAt(++i);
|
||||
}
|
||||
|
||||
@@ -168,10 +190,12 @@ module.exports = {
|
||||
buildHeaderValue(structured) {
|
||||
const paramsArray = [];
|
||||
|
||||
Object.keys(structured.params || {}).forEach(param => {
|
||||
Object.keys(structured.params || {}).forEach(key => {
|
||||
// filename might include unicode characters so it is a special case
|
||||
// other values probably do not
|
||||
const value = structured.params[param];
|
||||
const value = structured.params[key];
|
||||
// a parameter name is a token too and it is emitted without any quoting around it
|
||||
const param = key.replace(/[\x00-\x1f\x7f]/g, '');
|
||||
if (!this.isPlainText(value, true) || value.length >= 75) {
|
||||
this.buildHeaderParam(param, value, 50).forEach(encodedParam => {
|
||||
if (!/[\s"\\;:/=(),<>@[\]?]|^[-']|'$/.test(encodedParam.value) || encodedParam.key.substr(-1) === '*') {
|
||||
@@ -187,7 +211,11 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
|
||||
return structured.value + (paramsArray.length ? '; ' + paramsArray.join('; ') : '');
|
||||
// the value ahead of the parameters is a token, it has no way to carry a control
|
||||
// char or DEL and there is no quoting construct around it to escape one into
|
||||
const value = typeof structured.value === 'string' ? structured.value.replace(/[\x00-\x1f\x7f]/g, '') : structured.value;
|
||||
|
||||
return value + (paramsArray.length ? '; ' + paramsArray.join('; ') : '');
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -208,7 +236,7 @@ module.exports = {
|
||||
buildHeaderParam(key, data, maxLength) {
|
||||
const list = [];
|
||||
let encodedStr = typeof data === 'string' ? data : (data || '').toString();
|
||||
let chr, ord;
|
||||
let chr;
|
||||
let line;
|
||||
let startPos = 0;
|
||||
let i, len;
|
||||
@@ -245,8 +273,9 @@ module.exports = {
|
||||
const encodedStrArr = [];
|
||||
for (i = 0, len = encodedStr.length; i < len; i++) {
|
||||
chr = encodedStr.charAt(i);
|
||||
ord = chr.charCodeAt(0);
|
||||
if (ord >= 0xd800 && ord <= 0xdbff && i < len - 1) {
|
||||
if (/[\ud800-\udbff]/.test(chr) && /[\udc00-\udfff]/.test(encodedStr.charAt(i + 1))) {
|
||||
// an unpaired leading surrogate must not consume the next unit, that
|
||||
// would tear apart a valid pair following it
|
||||
chr += encodedStr.charAt(i + 1);
|
||||
encodedStrArr.push(chr);
|
||||
i++;
|
||||
@@ -284,8 +313,11 @@ module.exports = {
|
||||
line,
|
||||
encoded
|
||||
});
|
||||
// the line we start here holds an encoded char, so it has to be
|
||||
// flagged as one. otherwise it gets an unstarred continuation key
|
||||
// and a receiver reads the percent escapes as literal text
|
||||
line = '';
|
||||
startPos = i - 1;
|
||||
encoded = true;
|
||||
} else {
|
||||
encoded = true;
|
||||
i = startPos;
|
||||
@@ -350,6 +382,16 @@ module.exports = {
|
||||
value: false,
|
||||
params: {}
|
||||
};
|
||||
|
||||
// Parameter names come from a caller supplied contentType/contentDisposition. A
|
||||
// "__proto__" name would target the prototype chain of the params object instead of
|
||||
// an own property of it, and read back as Object.prototype, so it is dropped.
|
||||
const setParam = (name, value) => {
|
||||
if (!isProtoKey(name)) {
|
||||
response.params[name] = value;
|
||||
}
|
||||
};
|
||||
|
||||
let key = false;
|
||||
let value = '';
|
||||
let type = 'value';
|
||||
@@ -381,7 +423,7 @@ module.exports = {
|
||||
if (key === false) {
|
||||
response.value = value.trim();
|
||||
} else {
|
||||
response.params[key] = value.trim();
|
||||
setParam(key, value.trim());
|
||||
}
|
||||
type = 'key';
|
||||
value = '';
|
||||
@@ -396,10 +438,10 @@ module.exports = {
|
||||
if (key === false) {
|
||||
response.value = value.trim();
|
||||
} else {
|
||||
response.params[key] = value.trim();
|
||||
setParam(key, value.trim());
|
||||
}
|
||||
} else if (value.trim()) {
|
||||
response.params[value.trim().toLowerCase()] = '';
|
||||
setParam(value.trim().toLowerCase(), '');
|
||||
}
|
||||
|
||||
// handle parameter value continuations
|
||||
@@ -412,6 +454,14 @@ module.exports = {
|
||||
actualKey = key.substr(0, match.index);
|
||||
nr = Number(match[2] || match[3]) || 0;
|
||||
|
||||
if (isProtoKey(actualKey)) {
|
||||
// see setParam. Reading it back would yield Object.prototype, which is
|
||||
// an object, so the initializer below would be skipped and the write
|
||||
// that follows would throw out of a header build the caller can not catch
|
||||
delete response.params[key];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.params[actualKey] || typeof response.params[actualKey] !== 'object') {
|
||||
response.params[actualKey] = {
|
||||
charset: false,
|
||||
@@ -538,7 +588,7 @@ module.exports = {
|
||||
*/
|
||||
splitMimeEncodedString: (str, maxlen) => {
|
||||
const lines = [];
|
||||
let curLine, match, chr, done;
|
||||
let curLine, fallbackLine, match, chr, done;
|
||||
|
||||
// require at least 12 symbols to fit possible 4 octet UTF-8 sequences
|
||||
maxlen = Math.max(maxlen || 0, 12);
|
||||
@@ -551,8 +601,14 @@ module.exports = {
|
||||
curLine = curLine.substr(0, match.index);
|
||||
}
|
||||
|
||||
// Malformed input (a run of stray UTF-8 continuation bytes) has no split point
|
||||
// that keeps a character sequence whole, so the loop below walks back to an
|
||||
// empty line looking for one. Keep the widest chunk that at least does not cut
|
||||
// a "=XX" escape in half, so the part stays a decodable encoded word.
|
||||
fallbackLine = curLine.length ? curLine : str.substr(0, maxlen);
|
||||
|
||||
done = false;
|
||||
while (!done) {
|
||||
while (!done && curLine.length) {
|
||||
done = true;
|
||||
// check if not middle of a unicode char sequence
|
||||
if ((match = str.substr(curLine.length).match(/^[=]([0-9A-F]{2})/i))) {
|
||||
@@ -565,9 +621,11 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
if (curLine.length) {
|
||||
lines.push(curLine);
|
||||
if (!curLine.length) {
|
||||
curLine = fallbackLine;
|
||||
}
|
||||
|
||||
lines.push(curLine);
|
||||
str = str.substr(curLine.length);
|
||||
}
|
||||
|
||||
@@ -600,8 +658,11 @@ module.exports = {
|
||||
// might throw if we try to encode invalid sequences, eg. partial emoji
|
||||
str = encodeURIComponent(str);
|
||||
} catch (_E) {
|
||||
// should never run
|
||||
return str.replace(/[^\x00-\x1F *'()<>@,;:\\"[\]?=\u007F-\uFFFF]+/g, '');
|
||||
// an unpaired surrogate has no utf-8 representation, so run the value through a
|
||||
// utf-8 roundtrip to get the same U+FFFD every other encoder here produces and
|
||||
// retry. the value must never come back unencoded, it goes into a header parameter
|
||||
// where a bare quote or semicolon would break it out into a parameter of its own
|
||||
str = encodeURIComponent(Buffer.from(str, 'utf-8').toString('utf-8'));
|
||||
}
|
||||
|
||||
// ensure chars that are not handled by encodeURICompent are converted as well
|
||||
|
||||
Reference in New Issue
Block a user