UNPKG

@hola.org/emailjs-mime-codec

Version:

Encode and decode quoted printable and base64 strings

911 lines (800 loc) 1.11 MB
(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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