@hola.org/emailjs-mime-codec
Version:
Encode and decode quoted printable and base64 strings
911 lines (800 loc) • 1.11 MB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.emailjsMimeCodec = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.convert = exports.decode = exports.arr2str = exports.encode = undefined;
exports.decodeStream = decodeStream;
var _textEncoding = require('text-encoding');
/**
* Encodes an unicode string into an Uint8Array object as UTF-8
*
* @param {String} str String to be encoded
* @return {Uint8Array} UTF-8 encoded typed array
*/
var encode = exports.encode = function encode(str) {
return new _textEncoding.TextEncoder('UTF-8').encode(str);
};
var arr2str = exports.arr2str = function arr2str(arr) {
return String.fromCharCode.apply(null, arr);
};
/**
* Decodes a string from Uint8Array to an unicode string using specified encoding
*
* @param {Uint8Array} buf Binary data to be decoded
* @param {String} fromCharset Binary data is decoded into string using this charset
* @return {String} Decoded string
*/
var decode = exports.decode = function decode(buf) {
var fromCharset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'utf-8';
return decodeStream(buf, fromCharset).result;
};
/**
* Decodes a string from Uint8Array to an unicode string using specified encoding or specified decoder
*
* @param {Uint8Array} buf Binary data to be decoded
* @param {String} fromCharset Binary data is decoded into string using this charset
* @param {TextDecoder} decoder Decoder to be used
* @param {Boolean} stream If true, store undecodable trailing bytes until next call
* @return {Object} A pair {decoder, result}, this decoder can be used for further streaming calls
*/
function decodeStream(buf) {
var fromCharset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'utf-8';
var decoder = arguments[2];
var stream = arguments[3];
var charsets = [{ dec: decoder }, { charset: normalizeCharset(fromCharset), fatal: false }, { charset: 'utf-8', fatal: true }, { charset: 'iso-8859-15', fatal: false }];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = charsets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = _step.value,
dec = _step$value.dec,
charset = _step$value.charset,
fatal = _step$value.fatal;
if (!dec && !charset) {
continue;
}
try {
dec = dec || new _textEncoding.TextDecoder(charset, { fatal: fatal });
var result = dec.decode(buf, { stream: stream });
return { decoder: dec, result: result };
} catch (e) {}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return { result: arr2str(buf) // all else fails, treat it as binary
};
}
/**
* Convert a string from specific encoding to UTF-8 Uint8Array
*
* @param {String|Uint8Array} data Data to be encoded
* @param {String} Source encoding for the string (optional for data of type String)
* @return {Uint8Array} UTF-8 encoded typed array
*/
var convert = exports.convert = function convert(data, fromCharset) {
return typeof data === 'string' ? encode(data) : encode(decode(data, fromCharset));
};
function normalizeCharset() {
var charset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'utf-8';
var match = void 0;
if (match = charset.match(/^utf[-_]?(\d+)$/i)) {
return 'UTF-' + match[1];
}
if (match = charset.match(/^win[-_]?(\d+)$/i)) {
return 'WINDOWS-' + match[1];
}
if (match = charset.match(/^latin[-_]?(\d+)$/i)) {
return 'ISO-8859-' + match[1];
}
return charset;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9jaGFyc2V0LmpzIl0sIm5hbWVzIjpbImRlY29kZVN0cmVhbSIsImVuY29kZSIsInN0ciIsImFycjJzdHIiLCJTdHJpbmciLCJmcm9tQ2hhckNvZGUiLCJhcHBseSIsImFyciIsImRlY29kZSIsImJ1ZiIsImZyb21DaGFyc2V0IiwicmVzdWx0IiwiZGVjb2RlciIsInN0cmVhbSIsImNoYXJzZXRzIiwiZGVjIiwiY2hhcnNldCIsIm5vcm1hbGl6ZUNoYXJzZXQiLCJmYXRhbCIsImUiLCJjb252ZXJ0IiwiZGF0YSIsIm1hdGNoIl0sIm1hcHBpbmdzIjoiOzs7Ozs7UUE4QmdCQSxZLEdBQUFBLFk7O0FBOUJoQjs7QUFFQTs7Ozs7O0FBTU8sSUFBTUMsMEJBQVMsU0FBVEEsTUFBUztBQUFBLFNBQU8sOEJBQWdCLE9BQWhCLEVBQXlCQSxNQUF6QixDQUFnQ0MsR0FBaEMsQ0FBUDtBQUFBLENBQWY7O0FBRUEsSUFBTUMsNEJBQVUsU0FBVkEsT0FBVTtBQUFBLFNBQU9DLE9BQU9DLFlBQVAsQ0FBb0JDLEtBQXBCLENBQTBCLElBQTFCLEVBQWdDQyxHQUFoQyxDQUFQO0FBQUEsQ0FBaEI7O0FBRVA7Ozs7Ozs7QUFPTyxJQUFNQywwQkFBUyxTQUFUQSxNQUFTLENBQUNDLEdBQUQ7QUFBQSxNQUFNQyxXQUFOLHVFQUFvQixPQUFwQjtBQUFBLFNBQWdDVixhQUFhUyxHQUFiLEVBQWtCQyxXQUFsQixFQUErQkMsTUFBL0Q7QUFBQSxDQUFmOztBQUVQOzs7Ozs7Ozs7QUFTTyxTQUFTWCxZQUFULENBQXVCUyxHQUF2QixFQUFvRTtBQUFBLE1BQXhDQyxXQUF3Qyx1RUFBMUIsT0FBMEI7QUFBQSxNQUFqQkUsT0FBaUI7QUFBQSxNQUFSQyxNQUFROztBQUN6RSxNQUFNQyxXQUFXLENBQ2YsRUFBRUMsS0FBS0gsT0FBUCxFQURlLEVBRWYsRUFBRUksU0FBU0MsaUJBQWlCUCxXQUFqQixDQUFYLEVBQTBDUSxPQUFPLEtBQWpELEVBRmUsRUFHZixFQUFFRixTQUFTLE9BQVgsRUFBb0JFLE9BQU8sSUFBM0IsRUFIZSxFQUlmLEVBQUVGLFNBQVMsYUFBWCxFQUEwQkUsT0FBTyxLQUFqQyxFQUplLENBQWpCOztBQUR5RTtBQUFBO0FBQUE7O0FBQUE7QUFRekUseUJBQWtDSixRQUFsQyw4SEFBNEM7QUFBQTtBQUFBLFVBQWxDQyxHQUFrQyxlQUFsQ0EsR0FBa0M7QUFBQSxVQUE3QkMsT0FBNkIsZUFBN0JBLE9BQTZCO0FBQUEsVUFBcEJFLEtBQW9CLGVBQXBCQSxLQUFvQjs7QUFDMUMsVUFBSSxDQUFDSCxHQUFELElBQVEsQ0FBQ0MsT0FBYixFQUFzQjtBQUNwQjtBQUNEO0FBQ0QsVUFBSTtBQUNGRCxjQUFNQSxPQUFPLDhCQUFnQkMsT0FBaEIsRUFBeUIsRUFBRUUsWUFBRixFQUF6QixDQUFiO0FBQ0EsWUFBTVAsU0FBU0ksSUFBSVAsTUFBSixDQUFXQyxHQUFYLEVBQWdCLEVBQUVJLGNBQUYsRUFBaEIsQ0FBZjtBQUNBLGVBQU8sRUFBRUQsU0FBU0csR0FBWCxFQUFnQkosY0FBaEIsRUFBUDtBQUNELE9BSkQsQ0FJRSxPQUFPUSxDQUFQLEVBQVUsQ0FBRztBQUNoQjtBQWpCd0U7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFtQnpFLFNBQU8sRUFBRVIsUUFBUVIsUUFBUU0sR0FBUixDQUFWLENBQXlCO0FBQXpCLEdBQVA7QUFDRDs7QUFFRDs7Ozs7OztBQU9PLElBQU1XLDRCQUFVLFNBQVZBLE9BQVUsQ0FBQ0MsSUFBRCxFQUFPWCxXQUFQO0FBQUEsU0FBdUIsT0FBT1csSUFBUCxLQUFnQixRQUFoQixHQUEyQnBCLE9BQU9vQixJQUFQLENBQTNCLEdBQTBDcEIsT0FBT08sT0FBT2EsSUFBUCxFQUFhWCxXQUFiLENBQVAsQ0FBakU7QUFBQSxDQUFoQjs7QUFFUCxTQUFTTyxnQkFBVCxHQUE4QztBQUFBLE1BQW5CRCxPQUFtQix1RUFBVCxPQUFTOztBQUM1QyxNQUFJTSxjQUFKOztBQUVBLE1BQUtBLFFBQVFOLFFBQVFNLEtBQVIsQ0FBYyxrQkFBZCxDQUFiLEVBQWlEO0FBQy9DLFdBQU8sU0FBU0EsTUFBTSxDQUFOLENBQWhCO0FBQ0Q7O0FBRUQsTUFBS0EsUUFBUU4sUUFBUU0sS0FBUixDQUFjLGtCQUFkLENBQWIsRUFBaUQ7QUFDL0MsV0FBTyxhQUFhQSxNQUFNLENBQU4sQ0FBcEI7QUFDRDs7QUFFRCxNQUFLQSxRQUFRTixRQUFRTSxLQUFSLENBQWMsb0JBQWQsQ0FBYixFQUFtRDtBQUNqRCxXQUFPLGNBQWNBLE1BQU0sQ0FBTixDQUFyQjtBQUNEOztBQUVELFNBQU9OLE9BQVA7QUFDRCIsImZpbGUiOiJjaGFyc2V0LmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgVGV4dERlY29kZXIsIFRleHRFbmNvZGVyIH0gZnJvbSAndGV4dC1lbmNvZGluZydcblxuLyoqXG4gKiBFbmNvZGVzIGFuIHVuaWNvZGUgc3RyaW5nIGludG8gYW4gVWludDhBcnJheSBvYmplY3QgYXMgVVRGLThcbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gc3RyIFN0cmluZyB0byBiZSBlbmNvZGVkXG4gKiBAcmV0dXJuIHtVaW50OEFycmF5fSBVVEYtOCBlbmNvZGVkIHR5cGVkIGFycmF5XG4gKi9cbmV4cG9ydCBjb25zdCBlbmNvZGUgPSBzdHIgPT4gbmV3IFRleHRFbmNvZGVyKCdVVEYtOCcpLmVuY29kZShzdHIpXG5cbmV4cG9ydCBjb25zdCBhcnIyc3RyID0gYXJyID0+IFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkobnVsbCwgYXJyKVxuXG4vKipcbiAqIERlY29kZXMgYSBzdHJpbmcgZnJvbSBVaW50OEFycmF5IHRvIGFuIHVuaWNvZGUgc3RyaW5nIHVzaW5nIHNwZWNpZmllZCBlbmNvZGluZ1xuICpcbiAqIEBwYXJhbSB7VWludDhBcnJheX0gYnVmIEJpbmFyeSBkYXRhIHRvIGJlIGRlY29kZWRcbiAqIEBwYXJhbSB7U3RyaW5nfSBmcm9tQ2hhcnNldCBCaW5hcnkgZGF0YSBpcyBkZWNvZGVkIGludG8gc3RyaW5nIHVzaW5nIHRoaXMgY2hhcnNldFxuICogQHJldHVybiB7U3RyaW5nfSBEZWNvZGVkIHN0cmluZ1xuICovXG5leHBvcnQgY29uc3QgZGVjb2RlID0gKGJ1ZiwgZnJvbUNoYXJzZXQgPSAndXRmLTgnKSA9PiBkZWNvZGVTdHJlYW0oYnVmLCBmcm9tQ2hhcnNldCkucmVzdWx0XG5cbi8qKlxuICogRGVjb2RlcyBhIHN0cmluZyBmcm9tIFVpbnQ4QXJyYXkgdG8gYW4gdW5pY29kZSBzdHJpbmcgdXNpbmcgc3BlY2lmaWVkIGVuY29kaW5nIG9yIHNwZWNpZmllZCBkZWNvZGVyXG4gKlxuICogQHBhcmFtIHtVaW50OEFycmF5fSBidWYgQmluYXJ5IGRhdGEgdG8gYmUgZGVjb2RlZFxuICogQHBhcmFtIHtTdHJpbmd9IGZyb21DaGFyc2V0IEJpbmFyeSBkYXRhIGlzIGRlY29kZWQgaW50byBzdHJpbmcgdXNpbmcgdGhpcyBjaGFyc2V0XG4gKiBAcGFyYW0ge1RleHREZWNvZGVyfSBkZWNvZGVyIERlY29kZXIgdG8gYmUgdXNlZFxuICogQHBhcmFtIHtCb29sZWFufSBzdHJlYW0gSWYgdHJ1ZSwgc3RvcmUgdW5kZWNvZGFibGUgdHJhaWxpbmcgYnl0ZXMgdW50aWwgbmV4dCBjYWxsXG4gKiBAcmV0dXJuIHtPYmplY3R9IEEgcGFpciB7ZGVjb2RlciwgcmVzdWx0fSwgdGhpcyBkZWNvZGVyIGNhbiBiZSB1c2VkIGZvciBmdXJ0aGVyIHN0cmVhbWluZyBjYWxsc1xuICovXG5leHBvcnQgZnVuY3Rpb24gZGVjb2RlU3RyZWFtIChidWYsIGZyb21DaGFyc2V0ID0gJ3V0Zi04JywgZGVjb2Rlciwgc3RyZWFtKSB7XG4gIGNvbnN0IGNoYXJzZXRzID0gW1xuICAgIHsgZGVjOiBkZWNvZGVyIH0sXG4gICAgeyBjaGFyc2V0OiBub3JtYWxpemVDaGFyc2V0KGZyb21DaGFyc2V0KSwgZmF0YWw6IGZhbHNlIH0sXG4gICAgeyBjaGFyc2V0OiAndXRmLTgnLCBmYXRhbDogdHJ1ZSB9LFxuICAgIHsgY2hhcnNldDogJ2lzby04ODU5LTE1JywgZmF0YWw6IGZhbHNlIH1cbiAgXVxuXG4gIGZvciAobGV0IHtkZWMsIGNoYXJzZXQsIGZhdGFsfSBvZiBjaGFyc2V0cykge1xuICAgIGlmICghZGVjICYmICFjaGFyc2V0KSB7XG4gICAgICBjb250aW51ZVxuICAgIH1cbiAgICB0cnkge1xuICAgICAgZGVjID0gZGVjIHx8IG5ldyBUZXh0RGVjb2RlcihjaGFyc2V0LCB7IGZhdGFsIH0pXG4gICAgICBjb25zdCByZXN1bHQgPSBkZWMuZGVjb2RlKGJ1ZiwgeyBzdHJlYW0gfSlcbiAgICAgIHJldHVybiB7IGRlY29kZXI6IGRlYywgcmVzdWx0IH1cbiAgICB9IGNhdGNoIChlKSB7IH1cbiAgfVxuXG4gIHJldHVybiB7IHJlc3VsdDogYXJyMnN0cihidWYpIH0gLy8gYWxsIGVsc2UgZmFpbHMsIHRyZWF0IGl0IGFzIGJpbmFyeVxufVxuXG4vKipcbiAqIENvbnZlcnQgYSBzdHJpbmcgZnJvbSBzcGVjaWZpYyBlbmNvZGluZyB0byBVVEYtOCBVaW50OEFycmF5XG4gKlxuICogQHBhcmFtIHtTdHJpbmd8VWludDhBcnJheX0gZGF0YSBEYXRhIHRvIGJlIGVuY29kZWRcbiAqIEBwYXJhbSB7U3RyaW5nfSBTb3VyY2UgZW5jb2RpbmcgZm9yIHRoZSBzdHJpbmcgKG9wdGlvbmFsIGZvciBkYXRhIG9mIHR5cGUgU3RyaW5nKVxuICogQHJldHVybiB7VWludDhBcnJheX0gVVRGLTggZW5jb2RlZCB0eXBlZCBhcnJheVxuICovXG5leHBvcnQgY29uc3QgY29udmVydCA9IChkYXRhLCBmcm9tQ2hhcnNldCkgPT4gdHlwZW9mIGRhdGEgPT09ICdzdHJpbmcnID8gZW5jb2RlKGRhdGEpIDogZW5jb2RlKGRlY29kZShkYXRhLCBmcm9tQ2hhcnNldCkpXG5cbmZ1bmN0aW9uIG5vcm1hbGl6ZUNoYXJzZXQgKGNoYXJzZXQgPSAndXRmLTgnKSB7XG4gIGxldCBtYXRjaFxuXG4gIGlmICgobWF0Y2ggPSBjaGFyc2V0Lm1hdGNoKC9edXRmWy1fXT8oXFxkKykkL2kpKSkge1xuICAgIHJldHVybiAnVVRGLScgKyBtYXRjaFsxXVxuICB9XG5cbiAgaWYgKChtYXRjaCA9IGNoYXJzZXQubWF0Y2goL153aW5bLV9dPyhcXGQrKSQvaSkpKSB7XG4gICAgcmV0dXJuICdXSU5ET1dTLScgKyBtYXRjaFsxXVxuICB9XG5cbiAgaWYgKChtYXRjaCA9IGNoYXJzZXQubWF0Y2goL15sYXRpblstX10/KFxcZCspJC9pKSkpIHtcbiAgICByZXR1cm4gJ0lTTy04ODU5LScgKyBtYXRjaFsxXVxuICB9XG5cbiAgcmV0dXJuIGNoYXJzZXRcbn1cbiJdfQ==
},{"text-encoding":325}],2:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.convert = exports.encode = exports.decode = undefined;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
exports.mimeEncode = mimeEncode;
exports.mimeDecode = mimeDecode;
exports.base64Encode = base64Encode;
exports.base64Decode = base64Decode;
exports.quotedPrintableEncode = quotedPrintableEncode;
exports.quotedPrintableDecode = quotedPrintableDecode;
exports.mimeWordEncode = mimeWordEncode;
exports.mimeWordsEncode = mimeWordsEncode;
exports.mimeWordDecode = mimeWordDecode;
exports.mimeWordsDecode = mimeWordsDecode;
exports.foldLines = foldLines;
exports.headerLineEncode = headerLineEncode;
exports.headerLineDecode = headerLineDecode;
exports.headerLinesDecode = headerLinesDecode;
exports.parseHeaderValue = parseHeaderValue;
exports.continuationEncode = continuationEncode;
var _emailjsBase = require('emailjs-base64');
var _charset = require('./charset');
var _ramda = require('ramda');
// Lines can't be longer than 76 + <CR><LF> = 78 bytes
// http://tools.ietf.org/html/rfc2045#section-6.7
var MAX_LINE_LENGTH = 76;
var MAX_MIME_WORD_LENGTH = 52;
var MAX_B64_MIME_WORD_BYTE_LENGTH = 39;
/**
* Encodes all non printable and non ascii bytes to =XX form, where XX is the
* byte value in hex. This function does not convert linebreaks etc. it
* only escapes character sequences
*
* @param {String|Uint8Array} data Either a string or an Uint8Array
* @param {String} [fromCharset='UTF-8'] Source encoding
* @return {String} Mime encoded string
*/
function mimeEncode() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var fromCharset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'UTF-8';
var buffer = (0, _charset.convert)(data, fromCharset);
return buffer.reduce(function (aggregate, ord, index) {
return _checkRanges(ord) && !((ord === 0x20 || ord === 0x09) && (index === buffer.length - 1 || buffer[index + 1] === 0x0a || buffer[index + 1] === 0x0d)) ? aggregate + String.fromCharCode(ord) // if the char is in allowed range, then keep as is, unless it is a ws in the end of a line
: aggregate + '=' + (ord < 0x10 ? '0' : '') + ord.toString(16).toUpperCase();
}, '');
function _checkRanges(nr) {
var ranges = [// https://tools.ietf.org/html/rfc2045#section-6.7
[0x09], // <TAB>
[0x0A], // <LF>
[0x0D], // <CR>
[0x20, 0x3C], // <SP>!"#$%&'()*+,-./0123456789:;
[0x3E, 0x7E] // >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
];
return ranges.reduce(function (val, range) {
return val || range.length === 1 && nr === range[0] || range.length === 2 && nr >= range[0] && nr <= range[1];
}, false);
}
}
/**
* Decodes mime encoded string to an unicode string
*
* @param {String} str Mime encoded string
* @param {String} [fromCharset='UTF-8'] Source encoding
* @return {String} Decoded unicode string
*/
function mimeDecode() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var fromCharset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'UTF-8';
var encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length;
var buffer = new Uint8Array(str.length - encodedBytesCount * 2);
for (var i = 0, len = str.length, bufferPos = 0; i < len; i++) {
var hex = str.substr(i + 1, 2);
var chr = str.charAt(i);
if (chr === '=' && hex && /[\da-fA-F]{2}/.test(hex)) {
buffer[bufferPos++] = parseInt(hex, 16);
i += 2;
} else {
buffer[bufferPos++] = chr.charCodeAt(0);
}
}
return (0, _charset.decode)(buffer, fromCharset);
}
/**
* Encodes a string or an typed array of given charset into unicode
* base64 string. Also adds line breaks
*
* @param {String|Uint8Array} data String or typed array to be base64 encoded
* @param {String} Initial charset, e.g. 'binary'. Defaults to 'UTF-8'
* @return {String} Base64 encoded string
*/
function base64Encode(data) {
var fromCharset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'UTF-8';
var buf = typeof data !== 'string' && fromCharset === 'binary' ? data : (0, _charset.convert)(data, fromCharset);
var b64 = (0, _emailjsBase.encode)(buf);
return _addBase64SoftLinebreaks(b64);
}
/**
* Decodes a base64 string of any charset into an unicode string
*
* @param {String} str Base64 encoded string
* @param {String} [fromCharset='UTF-8'] Original charset of the base64 encoded string
* @param {TextDecoder} decoder Decoder to be used
* @param {Boolean} stream If true, store undecodable trailing bytes until next call
* @return {Object} A pair {result, decoder}, this decoder can be used for further decoding calls
*/
function base64Decode(str, fromCharset, decoder, stream) {
var buf = (0, _emailjsBase.decode)(str, _emailjsBase.OUTPUT_TYPED_ARRAY);
return fromCharset === 'binary' ? { result: (0, _charset.arr2str)(buf) } : (0, _charset.decodeStream)(buf, fromCharset, decoder, stream);
}
/**
* Encodes a string or an Uint8Array into a quoted printable encoding
* This is almost the same as mimeEncode, except line breaks will be changed
* as well to ensure that the lines are never longer than allowed length
*
* @param {String|Uint8Array} data String or an Uint8Array to mime encode
* @param {String} [fromCharset='UTF-8'] Original charset of the string
* @return {String} Mime encoded string
*/
function quotedPrintableEncode() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var fromCharset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'UTF-8';
var mimeEncodedStr = mimeEncode(data, fromCharset).replace(/\r?\n|\r/g, '\r\n') // fix line breaks, ensure <CR><LF>
.replace(/[\t ]+$/gm, function (spaces) {
return spaces.replace(/ /g, '=20').replace(/\t/g, '=09');
}); // replace spaces in the end of lines
return _addQPSoftLinebreaks(mimeEncodedStr); // add soft line breaks to ensure line lengths sjorter than 76 bytes
}
/**
* Decodes a string from a quoted printable encoding. This is almost the
* same as mimeDecode, except line breaks will be changed as well
*
* @param {String} str Mime encoded string to decode
* @param {String} [fromCharset='UTF-8'] Original charset of the string
* @return {String} Mime decoded string
*/
function quotedPrintableDecode() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var fromCharset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'UTF-8';
var rawString = str.replace(/[\t ]+$/gm, '') // remove invalid whitespace from the end of lines
.replace(/=(?:\r?\n|$)/g, ''); // remove soft line breaks
return mimeDecode(rawString, fromCharset);
}
/**
* Encodes a string or an Uint8Array to an UTF-8 MIME Word
* https://tools.ietf.org/html/rfc2047
*
* @param {String|Uint8Array} data String to be encoded
* @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B
* @param {String} [fromCharset='UTF-8'] Source sharacter set
* @return {String} Single or several mime words joined together
*/
function mimeWordEncode(data) {
var mimeWordEncoding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'Q';
var fromCharset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'UTF-8';
var parts = [];
var str = typeof data === 'string' ? data : (0, _charset.decode)(data, fromCharset);
if (mimeWordEncoding === 'Q') {
var _str = typeof data === 'string' ? data : (0, _charset.decode)(data, fromCharset);
var encodedStr = (0, _ramda.pipe)(mimeEncode, qEncodeForbiddenHeaderChars)(_str);
parts = encodedStr.length < MAX_MIME_WORD_LENGTH ? [encodedStr] : _splitMimeEncodedString(encodedStr, MAX_MIME_WORD_LENGTH);
} else {
// Fits as much as possible into every line without breaking utf-8 multibyte characters' octets up across lines
var j = 0;
var i = 0;
while (i < str.length) {
if ((0, _charset.encode)(str.substring(j, i)).length > MAX_B64_MIME_WORD_BYTE_LENGTH) {
// we went one character too far, substring at the char before
parts.push(str.substring(j, i - 1));
j = i - 1;
} else {
i++;
}
}
// add the remainder of the string
str.substring(j) && parts.push(str.substring(j));
parts = parts.map(_charset.encode).map(_emailjsBase.encode);
}
var prefix = '=?UTF-8?' + mimeWordEncoding + '?';
var suffix = '?= ';
return parts.map(function (p) {
return prefix + p + suffix;
}).join('').trim();
}
/**
* Q-Encodes remaining forbidden header chars
* https://tools.ietf.org/html/rfc2047#section-5
*/
var qEncodeForbiddenHeaderChars = function qEncodeForbiddenHeaderChars(str) {
var qEncode = function qEncode(chr) {
return chr === ' ' ? '_' : '=' + (chr.charCodeAt(0) < 0x10 ? '0' : '') + chr.charCodeAt(0).toString(16).toUpperCase();
};
return str.replace(/[^a-z0-9!*+\-/=]/ig, qEncode);
};
/**
* Finds word sequences with non ascii text and converts these to mime words
*
* @param {String|Uint8Array} data String to be encoded
* @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B
* @param {String} [fromCharset='UTF-8'] Source sharacter set
* @return {String} String with possible mime words
*/
function mimeWordsEncode() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var mimeWordEncoding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'Q';
var fromCharset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'UTF-8';
var regex = /([^\s\u0080-\uFFFF]*[\u0080-\uFFFF]+[^\s\u0080-\uFFFF]*(?:\s+[^\s\u0080-\uFFFF]*[\u0080-\uFFFF]+[^\s\u0080-\uFFFF]*\s*)?)+(?=\s|$)/g;
return (0, _charset.decode)((0, _charset.convert)(data, fromCharset)).replace(regex, function (match) {
return match.length ? mimeWordEncode(match, mimeWordEncoding, fromCharset) : '';
});
}
/**
* Decode a complete mime word encoded string
*
* @param {String} str Mime word encoded string
* @return {String} Decoded unicode string
*/
function mimeWordDecode() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var match = str.match(/^=\?([\w_\-*]+)\?([QqBb])\?([^?]*)\?=$/i);
if (!match) return str;
// RFC2231 added language tag to the encoding
// see: https://tools.ietf.org/html/rfc2231#section-5
// this implementation silently ignores this tag
var fromCharset = match[1].split('*').shift();
var encoding = (match[2] || 'Q').toString().toUpperCase();
var rawString = (match[3] || '').replace(/_/g, ' ');
if (encoding === 'B') {
return base64Decode(rawString, fromCharset).result;
} else if (encoding === 'Q') {
return mimeDecode(rawString, fromCharset);
} else {
return str;
}
}
/**
* Decode a string that might include one or several mime words
*
* @param {String} str String including some mime words that will be encoded
* @return {String} Decoded unicode string
*/
function mimeWordsDecode() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
str = str.toString().replace(/(=\?[^?]+\?[QqBb]\?[^?]+\?=)\s+(?==\?[^?]+\?[QqBb]\?[^?]*\?=)/g, '$1');
str = str.replace(/\?==\?[uU][tT][fF]-8\?[QqBb]\?/g, ''); // join bytes of multi-byte UTF-8
str = str.replace(/=\?[\w_\-*]+\?[QqBb]\?[^?]*\?=/g, function (mimeWord) {
return mimeWordDecode(mimeWord.replace(/\s+/g, ''));
});
return str;
}
/**
* Folds long lines, useful for folding header lines (afterSpace=false) and
* flowed text (afterSpace=true)
*
* @param {String} str String to be folded
* @param {Boolean} afterSpace If true, leave a space in th end of a line
* @return {String} String with folded lines
*/
function foldLines() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var afterSpace = arguments[1];
var pos = 0;
var len = str.length;
var result = '';
var line = void 0,
match = void 0;
while (pos < len) {
line = str.substr(pos, MAX_LINE_LENGTH);
if (line.length < MAX_LINE_LENGTH) {
result += line;
break;
}
if (match = line.match(/^[^\n\r]*(\r?\n|\r)/)) {
line = match[0];
result += line;
pos += line.length;
continue;
} else if ((match = line.match(/(\s+)[^\s]*$/)) && match[0].length - (afterSpace ? (match[1] || '').length : 0) < line.length) {
line = line.substr(0, line.length - (match[0].length - (afterSpace ? (match[1] || '').length : 0)));
} else if (match = str.substr(pos + line.length).match(/^[^\s]+(\s*)/)) {
line = line + match[0].substr(0, match[0].length - (!afterSpace ? (match[1] || '').length : 0));
}
result += line;
pos += line.length;
if (pos < len) {
result += '\r\n';
}
}
return result;
}
/**
* Encodes and folds a header line for a MIME message header.
* Shorthand for mimeWordsEncode + foldLines
*
* @param {String} key Key name, will not be encoded
* @param {String|Uint8Array} value Value to be encoded
* @param {String} [fromCharset='UTF-8'] Character set of the value
* @return {String} encoded and folded header line
*/
function headerLineEncode(key, value, fromCharset) {
var encodedValue = mimeWordsEncode(value, 'Q', fromCharset);
return foldLines(key + ': ' + encodedValue);
}
/**
* The result is not mime word decoded, you need to do your own decoding based
* on the rules for the specific header key
*
* @param {String} headerLine Single header line, might include linebreaks as well if folded
* @return {Object} And object of {key, value}
*/
function headerLineDecode() {
var headerLine = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var line = headerLine.toString().replace(/(?:\r?\n|\r)[ \t]*/g, ' ').trim();
var match = line.match(/^\s*([^:]+):(.*)$/);
return {
key: (match && match[1] || '').trim(),
value: (match && match[2] || '').trim()
};
}
/**
* Parses a block of header lines. Does not decode mime words as every
* header might have its own rules (eg. formatted email addresses and such)
*
* @param {String} headers Headers string
* @return {Object} An object of headers, where header keys are object keys. NB! Several values with the same key make up an Array
*/
function headerLinesDecode(headers) {
var lines = headers.split(/\r?\n|\r/);
var headersObj = {};
for (var i = lines.length - 1; i >= 0; i--) {
if (i && lines[i].match(/^\s/)) {
lines[i - 1] += '\r\n' + lines[i];
lines.splice(i, 1);
}
}
for (var _i = 0, len = lines.length; _i < len; _i++) {
var header = headerLineDecode(lines[_i]);
var key = header.key.toLowerCase();
var value = header.value;
if (!headersObj[key]) {
headersObj[key] = value;
} else {
headersObj[key] = [].concat(headersObj[key], value);
}
}
return headersObj;
}
/**
* Parses a header value with key=value arguments into a structured
* object.
*
* parseHeaderValue('content-type: text/plain; CHARSET='UTF-8'') ->
* {
* 'value': 'text/plain',
* 'params': {
* 'charset': 'UTF-8'
* }
* }
*
* @param {String} str Header value
* @return {Object} Header value as a parsed structure
*/
function parseHeaderValue(str) {
var response = {
value: false,
params: {}
};
var key = false;
var value = '';
var type = 'value';
var quote = false;
var escaped = false;
var chr = void 0;
for (var i = 0, len = str.length; i < len; i++) {
chr = str.charAt(i);
if (type === 'key') {
if (chr === '=') {
key = value.trim().toLowerCase();
type = 'value';
value = '';
continue;
}
value += chr;
} else {
if (escaped) {
value += chr;
} else if (chr === '\\') {
escaped = true;
continue;
} else if (quote && chr === quote) {
quote = false;
} else if (!quote && chr === '"') {
quote = chr;
} else if (!quote && chr === ';') {
if (key === false) {
response.value = value.trim();
} else {
response.params[key] = value.trim();
}
type = 'key';
value = '';
} else {
value += chr;
}
escaped = false;
}
}
if (type === 'value') {
if (key === false) {
response.value = value.trim();
} else {
response.params[key] = value.trim();
}
} else if (value.trim()) {
response.params[value.trim().toLowerCase()] = '';
}
// handle parameter value continuations
// https://tools.ietf.org/html/rfc2231#section-3
// preprocess values
Object.keys(response.params).forEach(function (key) {
var actualKey, nr, match, value;
if (match = key.match(/(\*(\d+)|\*(\d+)\*|\*)$/)) {
actualKey = key.substr(0, match.index);
nr = Number(match[2] || match[3]) || 0;
if (!response.params[actualKey] || _typeof(response.params[actualKey]) !== 'object') {
response.params[actualKey] = {
charset: false,
values: []
};
}
value = response.params[key];
if (nr === 0 && match[0].substr(-1) === '*' && (match = value.match(/^([^']*)'[^']*'(.*)$/))) {
response.params[actualKey].charset = match[1] || 'iso-8859-1';
value = match[2];
}
response.params[actualKey].values[nr] = value;
// remove the old reference
delete response.params[key];
}
});
// concatenate split rfc2231 strings and convert encoded strings to mime encoded words
Object.keys(response.params).forEach(function (key) {
var value;
if (response.params[key] && Array.isArray(response.params[key].values)) {
value = response.params[key].values.map(function (val) {
return val || '';
}).join('');
if (response.params[key].charset) {
// convert "%AB" to "=?charset?Q?=AB?="
response.params[key] = '=?' + response.params[key].charset + '?Q?' + value.replace(/[=?_\s]/g, function (s) {
// fix invalidly encoded chars
var c = s.charCodeAt(0).toString(16);
return s === ' ' ? '_' : '%' + (c.length < 2 ? '0' : '') + c;
}).replace(/%/g, '=') + '?='; // change from urlencoding to percent encoding
} else {
response.params[key] = value;
}
}
});
return response;
}
/**
* Encodes a string or an Uint8Array to an UTF-8 Parameter Value Continuation encoding (rfc2231)
* Useful for splitting long parameter values.
*
* For example
* title="unicode string"
* becomes
* title*0*="utf-8''unicode"
* title*1*="%20string"
*
* @param {String|Uint8Array} data String to be encoded
* @param {Number} [maxLength=50] Max length for generated chunks
* @param {String} [fromCharset='UTF-8'] Source sharacter set
* @return {Array} A list of encoded keys and headers
*/
function continuationEncode(key, data, maxLength, fromCharset) {
var list = [];
var encodedStr = typeof data === 'string' ? data : (0, _charset.decode)(data, fromCharset);
var line;
var startPos = 0;
var isEncoded = false;
maxLength = maxLength || 50;
// process ascii only text
if (/^[\w.\- ]*$/.test(data)) {
// check if conversion is even needed
if (encodedStr.length <= maxLength) {
return [{
key: key,
value: /[\s";=]/.test(encodedStr) ? '"' + encodedStr + '"' : encodedStr
}];
}
encodedStr = encodedStr.replace(new RegExp('.{' + maxLength + '}', 'g'), function (str) {
list.push({
line: str
});
return '';
});
if (encodedStr) {
list.push({
line: encodedStr
});
}
} else {
// first line includes the charset and language info and needs to be encoded
// even if it does not contain any unicode characters
line = 'utf-8\'\'';
isEncoded = true;
startPos = 0;
// process text with unicode or special chars
for (var i = 0, len = encodedStr.length; i < len; i++) {
var chr = encodedStr[i];
if (isEncoded) {
chr = encodeURIComponent(chr);
} else {
// try to urlencode current char
chr = chr === ' ' ? chr : encodeURIComponent(chr);
// By default it is not required to encode a line, the need
// only appears when the string contains unicode or special chars
// in this case we start processing the line over and encode all chars
if (chr !== encodedStr[i]) {
// Check if it is even possible to add the encoded char to the line
// If not, there is no reason to use this line, just push it to the list
// and start a new line with the char that needs encoding
if ((encodeURIComponent(line) + chr).length >= maxLength) {
list.push({
line: line,
encoded: isEncoded
});
line = '';
startPos = i - 1;
} else {
isEncoded = true;
i = startPos;
line = '';
continue;
}
}
}
// if the line is already too long, push it to the list and start a new one
if ((line + chr).length >= maxLength) {
list.push({
line: line,
encoded: isEncoded
});
line = chr = encodedStr[i] === ' ' ? ' ' : encodeURIComponent(encodedStr[i]);
if (chr === encodedStr[i]) {
isEncoded = false;
startPos = i - 1;
} else {
isEncoded = true;
}
} else {
line += chr;
}
}
if (line) {
list.push({
line: line,
encoded: isEncoded
});
}
}
return list.map(function (item, i) {
return {
// encoded lines: {name}*{part}*
// unencoded lines: {name}*{part}
// if any line needs to be encoded then the first line (part==0) is always encoded
key: key + '*' + i + (item.encoded ? '*' : ''),
value: /[\s";=]/.test(item.line) ? '"' + item.line + '"' : item.line
};
});
}
/**
* Splits a mime encoded string. Needed for dividing mime words into smaller chunks
*
* @param {String} str Mime encoded string to be split up
* @param {Number} maxlen Maximum length of characters for one part (minimum 12)
* @return {Array} Split string
*/
function _splitMimeEncodedString(str) {
var maxlen = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 12;
var minWordLength = 12; // require at least 12 symbols to fit possible 4 octet UTF-8 sequences
var maxWordLength = Math.max(maxlen, minWordLength);
var lines = [];
while (str.length) {
var curLine = str.substr(0, maxWordLength);
var match = curLine.match(/=[0-9A-F]?$/i); // skip incomplete escaped char
if (match) {
curLine = curLine.substr(0, match.index);
}
var done = false;
while (!done) {
var chr = void 0;
done = true;
var _match = str.substr(curLine.length).match(/^=([0-9A-F]{2})/i); // check if not middle of a unicode char sequence
if (_match) {
chr = parseInt(_match[1], 16);
// invalid sequence, move one char back anc recheck
if (chr < 0xC2 && chr > 0x7F) {
curLine = curLine.substr(0, curLine.length - 3);
done = false;
}
}
}
if (curLine.length) {
lines.push(curLine);
}
str = str.substr(curLine.length);
}
return lines;
}
function _addBase64SoftLinebreaks() {
var base64EncodedStr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
return base64EncodedStr.trim().replace(new RegExp('.{' + MAX_LINE_LENGTH + '}', 'g'), '$&\r\n').trim();
}
/**
* Adds soft line breaks(the ones that will be stripped out when decoding QP)
*
* @param {String} qpEncodedStr String in Quoted-Printable encoding
* @return {String} String with forced line breaks
*/
function _addQPSoftLinebreaks() {
var qpEncodedStr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var pos = 0;
var len = qpEncodedStr.length;
var lineMargin = Math.floor(MAX_LINE_LENGTH / 3);
var result = '';
var match = void 0,
line = void 0;
// insert soft linebreaks where needed
while (pos < len) {
line = qpEncodedStr.substr(pos, MAX_LINE_LENGTH);
if (match = line.match(/\r\n/)) {
line = line.substr(0, match.index + match[0].length);
result += line;
pos += line.length;
continue;
}
if (line.substr(-1) === '\n') {
// nothing to change here
result += line;
pos += line.length;
continue;
} else if (match = line.substr(-lineMargin).match(/\n.*?$/)) {
// truncate to nearest line break
line = line.substr(0, line.length - (match[0].length - 1));
result += line;
pos += line.length;
continue;
} else if (line.length > MAX_LINE_LENGTH - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
// truncate to nearest space
line = line.substr(0, line.length - (match[0].length - 1));
} else if (line.substr(-1) === '\r') {
line = line.substr(0, line.length - 1);
} else {
if (line.match(/=[\da-f]{0,2}$/i)) {
// push incomplete encoding sequences to the next line
if (match = line.match(/=[\da-f]{0,1}$/i)) {
line = line.substr(0, line.length - match[0].length);
}
// ensure that utf-8 sequences are not split
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/=[\da-f]{2}$/ig))) {
var code = parseInt(match[0].substr(1, 2), 16);
if (code < 128) {
break;
}
line = line.substr(0, line.length - 3);
if (code >= 0xC0) {
break;
}
}
}
}
if (pos + line.length < len && line.substr(-1) !== '\n') {
if (line.length === MAX_LINE_LENGTH && line.match(/=[\da-f]{2}$/i)) {
line = line.substr(0, line.length - 3);
} else if (line.length === MAX_LINE_LENGTH) {
line = line.substr(0, line.length - 1);
}
pos += line.length;
line += '=\r\n';
} else {
pos += line.length;
}
result += line;
}
return result;
}
exports.decode = _charset.decode;
exports.encode = _charset.encode;
exports.convert = _charset.convert;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9taW1lY29kZWMuanMiXSwibmFtZXMiOlsibWltZUVuY29kZSIsIm1pbWVEZWNvZGUiLCJiYXNlNjRFbmNvZGUiLCJiYXNlNjREZWNvZGUiLCJxdW90ZWRQcmludGFibGVFbmNvZGUiLCJxdW90ZWRQcmludGFibGVEZWNvZGUiLCJtaW1lV29yZEVuY29kZSIsIm1pbWVXb3Jkc0VuY29kZSIsIm1pbWVXb3JkRGVjb2RlIiwibWltZVdvcmRzRGVjb2RlIiwiZm9sZExpbmVzIiwiaGVhZGVyTGluZUVuY29kZSIsImhlYWRlckxpbmVEZWNvZGUiLCJoZWFkZXJMaW5lc0RlY29kZSIsInBhcnNlSGVhZGVyVmFsdWUiLCJjb250aW51YXRpb25FbmNvZGUiLCJNQVhfTElORV9MRU5HVEgiLCJNQVhfTUlNRV9XT1JEX0xFTkdUSCIsIk1BWF9CNjRfTUlNRV9XT1JEX0JZVEVfTEVOR1RIIiwiZGF0YSIsImZyb21DaGFyc2V0IiwiYnVmZmVyIiwicmVkdWNlIiwiYWdncmVnYXRlIiwib3JkIiwiaW5kZXgiLCJfY2hlY2tSYW5nZXMiLCJsZW5ndGgiLCJTdHJpbmciLCJmcm9tQ2hhckNvZGUiLCJ0b1N0cmluZyIsInRvVXBwZXJDYXNlIiwibnIiLCJyYW5nZXMiLCJ2YWwiLCJyYW5nZSIsInN0ciIsImVuY29kZWRCeXRlc0NvdW50IiwibWF0Y2giLCJVaW50OEFycmF5IiwiaSIsImxlbiIsImJ1ZmZlclBvcyIsImhleCIsInN1YnN0ciIsImNociIsImNoYXJBdCIsInRlc3QiLCJwYXJzZUludCIsImNoYXJDb2RlQXQiLCJidWYiLCJiNjQiLCJfYWRkQmFzZTY0U29mdExpbmVicmVha3MiLCJkZWNvZGVyIiwic3RyZWFtIiwicmVzdWx0IiwibWltZUVuY29kZWRTdHIiLCJyZXBsYWNlIiwic3BhY2VzIiwiX2FkZFFQU29mdExpbmVicmVha3MiLCJyYXdTdHJpbmciLCJtaW1lV29yZEVuY29kaW5nIiwicGFydHMiLCJlbmNvZGVkU3RyIiwicUVuY29kZUZvcmJpZGRlbkhlYWRlckNoYXJzIiwiX3NwbGl0TWltZUVuY29kZWRTdHJpbmciLCJqIiwic3Vic3RyaW5nIiwicHVzaCIsIm1hcCIsInByZWZpeCIsInN1ZmZpeCIsInAiLCJqb2luIiwidHJpbSIsInFFbmNvZGUiLCJyZWdleCIsInNwbGl0Iiwic2hpZnQiLCJlbmNvZGluZyIsIm1pbWVXb3JkIiwiYWZ0ZXJTcGFjZSIsInBvcyIsImxpbmUiLCJrZXkiLCJ2YWx1ZSIsImVuY29kZWRWYWx1ZSIsImhlYWRlckxpbmUiLCJoZWFkZXJzIiwibGluZXMiLCJoZWFkZXJzT2JqIiwic3BsaWNlIiwiaGVhZGVyIiwidG9Mb3dlckNhc2UiLCJjb25jYXQiLCJyZXNwb25zZSIsInBhcmFtcyIsInR5cGUiLCJxdW90ZSIsImVzY2FwZWQiLCJPYmplY3QiLCJrZXlzIiwiZm9yRWFjaCIsImFjdHVhbEtleSIsIk51bWJlciIsImNoYXJzZXQiLCJ2YWx1ZXMiLCJBcnJheSIsImlzQXJyYXkiLCJzIiwiYyIsIm1heExlbmd0aCIsImxpc3QiLCJzdGFydFBvcyIsImlzRW5jb2RlZCIsIlJlZ0V4cCIsImVuY29kZVVSSUNvbXBvbmVudCIsImVuY29kZWQiLCJpdGVtIiwibWF4bGVuIiwibWluV29yZExlbmd0aCIsIm1heFdvcmRMZW5ndGgiLCJNYXRoIiwibWF4IiwiY3VyTGluZSIsImRvbmUiLCJiYXNlNjRFbmNvZGVkU3RyIiwicXBFbmNvZGVkU3RyIiwibGluZU1hcmdpbiIsImZsb29yIiwiY29kZSIsImRlY29kZSIsImVuY29kZSIsImNvbnZlcnQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztRQW1CZ0JBLFUsR0FBQUEsVTtRQTBCQUMsVSxHQUFBQSxVO1FBMEJBQyxZLEdBQUFBLFk7UUFlQUMsWSxHQUFBQSxZO1FBY0FDLHFCLEdBQUFBLHFCO1FBZ0JBQyxxQixHQUFBQSxxQjtRQWlCQUMsYyxHQUFBQSxjO1FBZ0RBQyxlLEdBQUFBLGU7UUFXQUMsYyxHQUFBQSxjO1FBMEJBQyxlLEdBQUFBLGU7UUFnQkFDLFMsR0FBQUEs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