{"version":3,"sources":["jsdelivr-header.js","/npm/libmime@5.3.8/lib/libmime.js"],"names":[],"mappings":"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","file":"/npm/libmime@5.3.8/lib/libmime.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/libmime@5.3.8/lib/libmime.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/* eslint no-control-regex: 0, no-div-regex: 0, quotes: 0 */\n'use strict';\n\nconst { Buffer } = require('node:buffer');\nconst libcharset = require('./charset');\nconst libbase64 = require('libbase64');\nconst libqp = require('libqp');\nconst mimetypes = require('./mimetypes');\n\nconst STAGE_KEY = 0x1001;\nconst STAGE_VALUE = 0x1002;\n\nclass Libmime {\n    constructor(config) {\n        this.config = config || {};\n    }\n\n    /**\n     * Checks if a value is plaintext string (uses only printable 7bit chars)\n     *\n     * @param {String} value String to be tested\n     * @returns {Boolean} true if it is a plaintext string\n     */\n    isPlainText(value) {\n        if (typeof value !== 'string' || /[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\u0080-\\uFFFF]/.test(value)) {\n            return false;\n        } else {\n            return true;\n        }\n    }\n\n    /**\n     * Checks if a multi line string containes lines longer than the selected value.\n     *\n     * Useful when detecting if a mail message needs any processing at all –\n     * if only plaintext characters are used and lines are short, then there is\n     * no need to encode the values in any way. If the value is plaintext but has\n     * longer lines then allowed, then use format=flowed\n     *\n     * @param {String} str String to be tested\n     * @param {Number} lineLength Max line length to check for\n     * @returns {Boolean} Returns true if there is at least one line longer than lineLength chars\n     */\n    hasLongerLines(str, lineLength) {\n        return new RegExp('^.{' + (lineLength + 1) + ',}', 'm').test(str);\n    }\n\n    /**\n     * Decodes a string from a format=flowed soft wrapping.\n     *\n     * @param {String} str Plaintext string with format=flowed to decode\n     * @param {Boolean} [delSp] If true, delete leading spaces (delsp=yes)\n     * @return {String} Mime decoded string\n     */\n    decodeFlowed(str, delSp) {\n        str = (str || '').toString();\n\n        let lines = str.split(/\\r?\\n/);\n\n        let result = [],\n            buffer = null;\n\n        // remove soft linebreaks\n        // soft linebreaks are added after space symbols\n        for (let i = 0; i < lines.length; i++) {\n            let line = lines[i];\n\n            let isSoftBreak = buffer !== null && / $/.test(buffer) && !/(^|\\n)-- $/.test(buffer);\n\n            if (isSoftBreak) {\n                if (delSp) {\n                    // delsp adds space to text to be able to fold it\n                    // these spaces can be removed once the text is unfolded\n                    buffer = buffer.slice(0, -1) + line;\n                } else {\n                    buffer += line;\n                }\n            } else {\n                if (buffer !== null) {\n                    result.push(buffer);\n                }\n\n                buffer = line;\n            }\n        }\n\n        if (buffer) {\n            result.push(buffer);\n        }\n\n        // remove whitespace stuffing\n        // http://tools.ietf.org/html/rfc3676#section-4.4\n        return result.join('\\n').replace(/^ /gm, '');\n    }\n\n    /**\n     * Adds soft line breaks to content marked with format=flowed to\n     * ensure that no line in the message is never longer than lineLength\n     *\n     * @param {String} str Plaintext string that requires wrapping\n     * @param {Number} [lineLength=76] Maximum length of a line\n     * @return {String} String with forced line breaks\n     */\n    encodeFlowed(str, lineLength) {\n        lineLength = lineLength || 76;\n\n        let flowed = [];\n        str.split(/\\r?\\n/).forEach(line => {\n            flowed.push(\n                this.foldLines(\n                    line\n                        // space stuffing http://tools.ietf.org/html/rfc3676#section-4.2\n                        .replace(/^( |From|>)/gim, ' $1'),\n                    lineLength,\n                    true\n                )\n            );\n        });\n        return flowed.join('\\r\\n');\n    }\n\n    /**\n     * Encodes a string or an Buffer to an UTF-8 MIME Word (rfc2047)\n     *\n     * @param {String|Buffer} data String to be encoded\n     * @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B\n     * @param {Number} [maxLength=0] If set, split mime words into several chunks if needed\n     * @return {String} Single or several mime words joined together\n     */\n    encodeWord(data, mimeWordEncoding, maxLength) {\n        mimeWordEncoding = (mimeWordEncoding || 'Q').toString().toUpperCase().trim().charAt(0);\n        maxLength = maxLength || 0;\n\n        let encodedStr;\n        let toCharset = 'UTF-8';\n\n        if (maxLength && maxLength > 7 + toCharset.length) {\n            maxLength -= 7 + toCharset.length;\n        }\n\n        if (mimeWordEncoding === 'Q') {\n            // https://tools.ietf.org/html/rfc2047#section-5 rule (3)\n            encodedStr = libqp.encode(data).replace(/[^a-z0-9!*+\\-/=]/gi, chr => {\n                let ord = chr.charCodeAt(0).toString(16).toUpperCase();\n                if (chr === ' ') {\n                    return '_';\n                } else {\n                    return '=' + (ord.length === 1 ? '0' + ord : ord);\n                }\n            });\n        } else if (mimeWordEncoding === 'B') {\n            encodedStr = typeof data === 'string' ? data : libbase64.encode(data);\n            maxLength = maxLength ? Math.max(3, ((maxLength - (maxLength % 4)) / 4) * 3) : 0;\n        }\n\n        if (maxLength && (mimeWordEncoding !== 'B' ? encodedStr : libbase64.encode(data)).length > maxLength) {\n            if (mimeWordEncoding === 'Q') {\n                encodedStr = this.splitMimeEncodedString(encodedStr, maxLength).join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');\n            } else {\n                // RFC2047 6.3 (2) states that encoded-word must include an integral number of characters, so no chopping unicode sequences\n                let parts = [];\n                let lpart = '';\n                for (let i = 0, len = encodedStr.length; i < len; i++) {\n                    let chr = encodedStr.charAt(i);\n                    // check if we can add this character to the existing string\n                    // without breaking byte length limit\n\n                    if (/[\\ud83c\\ud83d\\ud83e]/.test(chr) && i < len - 1) {\n                        // composite emoji byte, so add the next byte as well\n                        chr += encodedStr.charAt(++i);\n                    }\n\n                    if (Buffer.byteLength(lpart + chr) <= maxLength || i === 0) {\n                        lpart += chr;\n                    } else {\n                        // we hit the length limit, so push the existing string and start over\n                        parts.push(libbase64.encode(lpart));\n                        lpart = chr;\n                    }\n                }\n                if (lpart) {\n                    parts.push(libbase64.encode(lpart));\n                }\n\n                if (parts.length > 1) {\n                    encodedStr = parts.join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');\n                } else {\n                    encodedStr = parts.join('');\n                }\n            }\n        } else if (mimeWordEncoding === 'B') {\n            encodedStr = libbase64.encode(data);\n        }\n\n        return '=?' + toCharset + '?' + mimeWordEncoding + '?' + encodedStr + (encodedStr.substr(-2) === '?=' ? '' : '?=');\n    }\n\n    /**\n     * Decodes the inner payload of a single mime encoded-word into a unicode string.\n     * Expects the three components of `=?charset?encoding?text?=` already separated.\n     *\n     * @param {String} charset Charset name from the encoded-word (may include an RFC 2231 language tag, which is ignored)\n     * @param {String} encoding Encoding indicator, either 'Q' or 'B' (case insensitive)\n     * @param {String} str Encoded payload between the second `?` and the trailing `?=`\n     * @return {String} Decoded unicode string\n     */\n    decodeWord(charset, encoding, str) {\n        // RFC2231 added language tag to the encoding\n        // see: https://tools.ietf.org/html/rfc2231#section-5\n        // this implementation silently ignores this tag\n        let splitPos = charset.indexOf('*');\n        if (splitPos >= 0) {\n            charset = charset.substr(0, splitPos);\n        }\n        charset = libcharset.normalizeCharset(charset);\n\n        encoding = encoding.toUpperCase();\n\n        if (encoding === 'Q') {\n            str = str\n                // remove spaces between = and hex char, this might indicate invalidly applied line splitting\n                .replace(/=\\s+([0-9a-fA-F])/g, '=$1')\n                // convert all underscores to spaces\n                .replace(/[_\\s]/g, ' ');\n\n            let buf = Buffer.from(str);\n            let bytes = [];\n            for (let i = 0, len = buf.length; i < len; i++) {\n                let c = buf[i];\n                if (i <= len - 2 && c === 0x3d /* = */) {\n                    let c1 = this.getHex(buf[i + 1]);\n                    let c2 = this.getHex(buf[i + 2]);\n                    if (c1 && c2) {\n                        let c = parseInt(c1 + c2, 16);\n                        bytes.push(c);\n                        i += 2;\n                        continue;\n                    }\n                }\n                bytes.push(c);\n            }\n            str = Buffer.from(bytes);\n        } else if (encoding === 'B') {\n            str = Buffer.concat(\n                str\n                    .split('=')\n                    .filter(s => s !== '') // filter empty string\n                    .map(str => Buffer.from(str, 'base64'))\n            );\n        } else {\n            // keep as is, convert Buffer to unicode string, assume utf8\n            str = Buffer.from(str);\n        }\n\n        return libcharset.decode(str, charset);\n    }\n\n    /**\n     * Finds word sequences with non ascii text and converts these to mime words\n     *\n     * @param {String|Buffer} data String to be encoded\n     * @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B\n     * @param {Number} [maxLength=0] If set, split mime words into several chunks if needed\n     * @param {String} [fromCharset='UTF-8'] Source character set\n     * @return {String} String with possible mime words\n     */\n    encodeWords(data, mimeWordEncoding, maxLength, fromCharset) {\n        if (!fromCharset && typeof maxLength === 'string' && !maxLength.match(/^[0-9]+$/)) {\n            fromCharset = maxLength;\n            maxLength = undefined;\n        }\n\n        maxLength = maxLength || 0;\n\n        let decodedValue = libcharset.decode(libcharset.convert(data || '', fromCharset));\n        let encodedValue;\n\n        let firstMatch = decodedValue.match(/(?:^|\\s)([^\\s]*[\\u0080-\\uFFFF])/);\n        if (!firstMatch) {\n            return decodedValue;\n        }\n        let lastMatch = decodedValue.match(/([\\u0080-\\uFFFF][^\\s]*)[^\\u0080-\\uFFFF]*$/);\n        if (!lastMatch) {\n            // should not happen\n            return decodedValue;\n        }\n        let startIndex =\n            firstMatch.index +\n            (\n                firstMatch[0].match(/[^\\s]/) || {\n                    index: 0\n                }\n            ).index;\n        let endIndex = lastMatch.index + (lastMatch[1] || '').length;\n\n        encodedValue =\n            (startIndex ? decodedValue.substr(0, startIndex) : '') +\n            this.encodeWord(decodedValue.substring(startIndex, endIndex), mimeWordEncoding || 'Q', maxLength) +\n            (endIndex < decodedValue.length ? decodedValue.substr(endIndex) : '');\n\n        return encodedValue;\n    }\n\n    /**\n     * Decode a string that might include one or several mime words\n     *\n     * @param {String} str String including some mime words that will be decoded\n     * @return {String} Decoded unicode string\n     */\n    decodeWords(str) {\n        return (\n            (str || '')\n                .toString()\n                // find base64 words that can be joined\n                .replace(/(=\\?([^?]+)\\?[Bb]\\?[^?]*\\?=)\\s*(?==\\?([^?]+)\\?[Bb]\\?[^?]*\\?=)/g, (match, left, chLeft, chRight) => {\n                    // only mark b64 chunks to be joined if charsets match\n                    if (libcharset.normalizeCharset(chLeft || '') === libcharset.normalizeCharset(chRight || '')) {\n                        // set a joiner marker\n                        return left + '__\\x00JOIN\\x00__';\n                    }\n                    return match;\n                })\n                // find QP words that can be joined\n                .replace(/(=\\?([^?]+)\\?[Qq]\\?[^?]*\\?=)\\s*(?==\\?([^?]+)\\?[Qq]\\?[^?]*\\?=)/g, (match, left, chLeft, chRight) => {\n                    // only mark QP chunks to be joined if charsets match\n                    if (libcharset.normalizeCharset(chLeft || '') === libcharset.normalizeCharset(chRight || '')) {\n                        // set a joiner marker\n                        return left + '__\\x00JOIN\\x00__';\n                    }\n                    return match;\n                })\n                // join base64 encoded words\n                .replace(/(\\?=)?__\\x00JOIN\\x00__(=\\?([^?]+)\\?[QqBb]\\?)?/g, '')\n                // remove spaces between mime encoded words\n                .replace(/(=\\?[^?]+\\?[QqBb]\\?[^?]*\\?=)\\s+(?==\\?[^?]+\\?[QqBb]\\?[^?]*\\?=)/g, '$1')\n                // decode words\n                .replace(/=\\?([\\w_\\-*]+)\\?([QqBb])\\?([^?]*)\\?=/g, (m, charset, encoding, text) => this.decodeWord(charset, encoding, text))\n        );\n    }\n\n    getHex(c) {\n        if ((c >= 0x30 /* 0 */ && c <= 0x39) /* 9 */ || (c >= 0x61 /* a */ && c <= 0x66) /* f */ || (c >= 0x41 /* A */ && c <= 0x46) /* F */) {\n            return String.fromCharCode(c);\n        }\n        return false;\n    }\n\n    /**\n     * Splits a string by :\n     * The result is not mime word decoded, you need to do your own decoding based\n     * on the rules for the specific header key\n     *\n     * @param {String} headerLine Single header line, might include linebreaks as well if folded\n     * @return {Object} An object of {key, value}\n     */\n    decodeHeader(headerLine) {\n        let line = (headerLine || '')\n                .toString()\n                .replace(/(?:\\r?\\n|\\r)[ \\t]*/g, ' ')\n                .trim(),\n            match = line.match(/^\\s*([^:]+):(.*)$/),\n            key = ((match && match[1]) || '').trim().toLowerCase(),\n            value = ((match && match[2]) || '').trim();\n\n        return {\n            key,\n            value\n        };\n    }\n\n    /**\n     * Parses a block of header lines. Does not decode mime words as every\n     * header might have its own rules (eg. formatted email addresses and such)\n     *\n     * @param {String} headers Headers string\n     * @return {Object} An object of headers, where header keys are object keys. NB! Several values with the same key make up an Array\n     */\n    decodeHeaders(headers) {\n        let lines = headers.split(/\\r?\\n|\\r/),\n            headersObj = {},\n            header,\n            i,\n            len;\n\n        for (i = lines.length - 1; i >= 0; i--) {\n            if (i && lines[i].match(/^\\s/)) {\n                lines[i - 1] += '\\r\\n' + lines[i];\n                lines.splice(i, 1);\n            }\n        }\n\n        for (i = 0, len = lines.length; i < len; i++) {\n            header = this.decodeHeader(lines[i]);\n            if (!headersObj[header.key]) {\n                headersObj[header.key] = [header.value];\n            } else {\n                headersObj[header.key].push(header.value);\n            }\n        }\n\n        return headersObj;\n    }\n\n    /**\n     * Joins parsed header value together as 'value; param1=value1; param2=value2'\n     * PS: We are following RFC 822 for the list of special characters that we need to keep in quotes.\n     *      Refer: https://www.w3.org/Protocols/rfc1341/4_Content-Type.html\n     * @param {Object} structured Parsed header value\n     * @return {String} joined header value\n     */\n    buildHeaderValue(structured) {\n        let paramsArray = [];\n\n        Object.keys(structured.params || {}).forEach(param => {\n            // filename might include unicode characters so it is a special case\n            let value = structured.params[param];\n            if (!this.isPlainText(value) || value.length >= 75) {\n                this.buildHeaderParam(param, value, 50).forEach(encodedParam => {\n                    if (!/[\\s\"\\\\;:/=(),<>@[\\]?]|^[-']|'$/.test(encodedParam.value) || encodedParam.key.substr(-1) === '*') {\n                        paramsArray.push(encodedParam.key + '=' + encodedParam.value);\n                    } else {\n                        paramsArray.push(encodedParam.key + '=' + JSON.stringify(encodedParam.value));\n                    }\n                });\n            } else if (/[\\s'\"\\\\;:/=(),<>@[\\]?]|^-/.test(value)) {\n                paramsArray.push(param + '=' + JSON.stringify(value));\n            } else {\n                paramsArray.push(param + '=' + value);\n            }\n        });\n\n        return structured.value + (paramsArray.length ? '; ' + paramsArray.join('; ') : '');\n    }\n\n    /**\n     * Parses a header value with key=value arguments into a structured\n     * object.\n     *\n     *   parseHeaderValue('content-type: text/plain; CHARSET='UTF-8'') ->\n     *   {\n     *     'value': 'text/plain',\n     *     'params': {\n     *       'charset': 'UTF-8'\n     *     }\n     *   }\n     *\n     * @param {String} str Header value\n     * @return {Object} Header value as a parsed structure\n     */\n    parseHeaderValue(str) {\n        let response = {\n            value: false,\n            params: {}\n        };\n        let key = false;\n        let value = '';\n        let stage = STAGE_VALUE;\n\n        let quote = false;\n        let escaped = false;\n        let chr;\n\n        for (let i = 0, len = str.length; i < len; i++) {\n            chr = str.charAt(i);\n            switch (stage) {\n                case STAGE_KEY:\n                    if (chr === '=') {\n                        key = value.trim().toLowerCase();\n                        stage = STAGE_VALUE;\n                        value = '';\n                        break;\n                    }\n                    value += chr;\n                    break;\n                case STAGE_VALUE:\n                    if (escaped) {\n                        value += chr;\n                    } else if (chr === '\\\\') {\n                        escaped = true;\n                        continue;\n                    } else if (quote && chr === quote) {\n                        quote = false;\n                    } else if (!quote && chr === '\"') {\n                        quote = chr;\n                    } else if (!quote && chr === ';') {\n                        if (key === false) {\n                            response.value = value.trim();\n                        } else {\n                            response.params[key] = value.trim();\n                        }\n                        stage = STAGE_KEY;\n                        value = '';\n                    } else {\n                        value += chr;\n                    }\n                    escaped = false;\n                    break;\n            }\n        }\n\n        // finalize remainder\n        value = value.trim();\n        if (stage === STAGE_VALUE) {\n            if (key === false) {\n                // default value\n                response.value = value;\n            } else {\n                // subkey value\n                response.params[key] = value;\n            }\n        } else if (value) {\n            // treat as key without value, see emptykey:\n            // Header-Key: somevalue; key=value; emptykey\n            response.params[value.toLowerCase()] = '';\n        }\n\n        // handle parameter value continuations\n        // https://tools.ietf.org/html/rfc2231#section-3\n\n        // preprocess values\n        Object.keys(response.params).forEach(key => {\n            let actualKey;\n            let nr;\n            let value;\n\n            let match = key.match(/\\*((\\d+)\\*?)?$/);\n\n            if (!match) {\n                // nothing to do here, does not seem like a continuation param\n                return;\n            }\n\n            actualKey = key.substr(0, match.index).toLowerCase();\n            nr = Number(match[2]) || 0;\n\n            if (!response.params[actualKey] || typeof response.params[actualKey] !== 'object') {\n                response.params[actualKey] = {\n                    charset: false,\n                    values: []\n                };\n            }\n\n            value = response.params[key];\n\n            if (nr === 0 && match[0].charAt(match[0].length - 1) === '*' && (match = value.match(/^([^']*)'[^']*'(.*)$/))) {\n                response.params[actualKey].charset = match[1] || 'utf-8';\n                value = match[2];\n            }\n\n            response.params[actualKey].values.push({ nr, value });\n\n            // remove the old reference\n            delete response.params[key];\n        });\n\n        // concatenate split rfc2231 strings and convert encoded strings to mime encoded words\n        Object.keys(response.params).forEach(key => {\n            let value;\n            if (response.params[key] && Array.isArray(response.params[key].values)) {\n                value = response.params[key].values\n                    .sort((a, b) => a.nr - b.nr)\n                    .map(val => (val && val.value) || '')\n                    .join('');\n\n                if (response.params[key].charset) {\n                    // convert \"%AB\" to \"=?charset?Q?=AB?=\" and then to unicode\n                    response.params[key] = this.decodeWords(\n                        '=?' +\n                            response.params[key].charset +\n                            '?Q?' +\n                            value\n                                // fix invalidly encoded chars\n                                .replace(/[=?_\\s]/g, s => {\n                                    let c = s.charCodeAt(0).toString(16);\n                                    if (s === ' ') {\n                                        return '_';\n                                    } else {\n                                        return '%' + (c.length < 2 ? '0' : '') + c;\n                                    }\n                                })\n                                // change from urlencoding to percent encoding\n                                .replace(/%/g, '=') +\n                            '?='\n                    );\n                } else {\n                    response.params[key] = this.decodeWords(value);\n                }\n            }\n        });\n\n        return response;\n    }\n\n    /**\n     * Encodes a string or an Buffer to an UTF-8 Parameter Value Continuation encoding (rfc2231)\n     * Useful for splitting long parameter values.\n     *\n     * For example\n     *      title=\"unicode string\"\n     * becomes\n     *     title*0*=utf-8''unicode\n     *     title*1*=%20string\n     *\n     * @param {String} key Parameter name (eg. 'filename')\n     * @param {String|Buffer} data String to be encoded\n     * @param {Number} [maxLength=50] Max length for generated chunks\n     * @param {String} [fromCharset='UTF-8'] Source character set\n     * @return {Array} A list of encoded keys and headers\n     */\n    buildHeaderParam(key, data, maxLength, fromCharset) {\n        let list = [];\n        let encodedStr = typeof data === 'string' ? data : this.decode(data, fromCharset);\n        let encodedStrArr;\n        let chr, ord;\n        let line;\n        let startPos = 0;\n        let isEncoded = false;\n        let i, len;\n\n        maxLength = maxLength || 50;\n\n        // process ascii only text\n        if (this.isPlainText(data)) {\n            // check if conversion is even needed\n            if (encodedStr.length <= maxLength) {\n                return [\n                    {\n                        key,\n                        value: encodedStr\n                    }\n                ];\n            }\n\n            encodedStr = encodedStr.replace(new RegExp('.{' + maxLength + '}', 'g'), str => {\n                list.push({\n                    line: str\n                });\n                return '';\n            });\n\n            if (encodedStr) {\n                list.push({\n                    line: encodedStr\n                });\n            }\n        } else {\n            if (/[\\uD800-\\uDBFF]/.test(encodedStr)) {\n                // string containts surrogate pairs, so normalize it to an array of bytes\n                encodedStrArr = [];\n                for (i = 0, len = encodedStr.length; i < len; i++) {\n                    chr = encodedStr.charAt(i);\n                    ord = chr.charCodeAt(0);\n                    if (ord >= 0xd800 && ord <= 0xdbff && i < len - 1) {\n                        chr += encodedStr.charAt(i + 1);\n                        encodedStrArr.push(chr);\n                        i++;\n                    } else {\n                        encodedStrArr.push(chr);\n                    }\n                }\n                encodedStr = encodedStrArr;\n            }\n\n            // first line includes the charset and language info and needs to be encoded\n            // even if it does not contain any unicode characters\n            line = \"utf-8''\";\n            isEncoded = true;\n            startPos = 0;\n\n            // process text with unicode or special chars\n            for (i = 0, len = encodedStr.length; i < len; i++) {\n                chr = encodedStr[i];\n\n                if (isEncoded) {\n                    chr = this.safeEncodeURIComponent(chr);\n                } else {\n                    // try to urlencode current char\n                    chr = chr === ' ' ? chr : this.safeEncodeURIComponent(chr);\n                    // By default it is not required to encode a line, the need\n                    // only appears when the string contains unicode or special chars\n                    // in this case we start processing the line over and encode all chars\n                    if (chr !== encodedStr[i]) {\n                        // Check if it is even possible to add the encoded char to the line\n                        // If not, there is no reason to use this line, just push it to the list\n                        // and start a new line with the char that needs encoding\n                        if ((this.safeEncodeURIComponent(line) + chr).length >= maxLength) {\n                            list.push({\n                                line,\n                                encoded: isEncoded\n                            });\n                            line = '';\n                            startPos = i - 1;\n                        } else {\n                            isEncoded = true;\n                            i = startPos;\n                            line = '';\n                            continue;\n                        }\n                    }\n                }\n\n                // if the line is already too long, push it to the list and start a new one\n                if ((line + chr).length >= maxLength) {\n                    list.push({\n                        line,\n                        encoded: isEncoded\n                    });\n                    line = chr = encodedStr[i] === ' ' ? ' ' : this.safeEncodeURIComponent(encodedStr[i]);\n                    if (chr === encodedStr[i]) {\n                        isEncoded = false;\n                        startPos = i - 1;\n                    } else {\n                        isEncoded = true;\n                    }\n                } else {\n                    line += chr;\n                }\n            }\n\n            if (line) {\n                list.push({\n                    line,\n                    encoded: isEncoded\n                });\n            }\n        }\n\n        return list.map((item, i) => ({\n            // encoded lines: {name}*{part}*\n            // unencoded lines: {name}*{part}\n            // if any line needs to be encoded then the first line (part==0) is always encoded\n            key: key + '*' + i + (item.encoded ? '*' : ''),\n            value: item.line\n        }));\n    }\n\n    /**\n     * Returns file extension for a content type string. If no suitable extensions\n     * are found, 'bin' is used as the default extension\n     *\n     * @param {String} mimeType Content type to be checked for\n     * @return {String} File extension\n     */\n    detectExtension(mimeType) {\n        mimeType = (mimeType || '').toString().toLowerCase().replace(/\\s/g, '');\n        if (!(mimeType in mimetypes.list)) {\n            return 'bin';\n        }\n\n        if (typeof mimetypes.list[mimeType] === 'string') {\n            return mimetypes.list[mimeType];\n        }\n\n        let mimeParts = mimeType.split('/');\n\n        // search for name match\n        for (let i = 0, len = mimetypes.list[mimeType].length; i < len; i++) {\n            if (mimeParts[1] === mimetypes.list[mimeType][i]) {\n                return mimetypes.list[mimeType][i];\n            }\n        }\n\n        // use the first one\n        return mimetypes.list[mimeType][0] !== '*' ? mimetypes.list[mimeType][0] : 'bin';\n    }\n\n    /**\n     * Returns content type for a file extension. If no suitable content types\n     * are found, 'application/octet-stream' is used as the default content type\n     *\n     * @param {String} extension Extension (or filename) to be checked for\n     * @return {String} Content type\n     */\n    detectMimeType(extension) {\n        extension = (extension || '').toString().toLowerCase().replace(/\\s/g, '').replace(/^\\./g, '').split('.').pop();\n\n        if (!(extension in mimetypes.extensions)) {\n            return 'application/octet-stream';\n        }\n\n        if (typeof mimetypes.extensions[extension] === 'string') {\n            return mimetypes.extensions[extension];\n        }\n\n        let mimeParts;\n\n        // search for name match\n        for (let i = 0, len = mimetypes.extensions[extension].length; i < len; i++) {\n            mimeParts = mimetypes.extensions[extension][i].split('/');\n            if (mimeParts[1] === extension) {\n                return mimetypes.extensions[extension][i];\n            }\n        }\n\n        // use the first one\n        return mimetypes.extensions[extension][0];\n    }\n\n    /**\n     * Folds long lines, useful for folding header lines (afterSpace=false) and\n     * flowed text (afterSpace=true)\n     *\n     * @param {String} str String to be folded\n     * @param {Number} [lineLength=76] Maximum length of a line\n     * @param {Boolean} afterSpace If true, leave a space in the end of a line\n     * @return {String} String with folded lines\n     */\n    foldLines(str, lineLength, afterSpace) {\n        str = (str || '').toString();\n        lineLength = lineLength || 76;\n\n        let pos = 0,\n            len = str.length,\n            result = '',\n            line,\n            match;\n\n        while (pos < len) {\n            line = str.substr(pos, lineLength);\n            if (line.length < lineLength) {\n                result += line;\n                break;\n            }\n            if ((match = line.match(/^[^\\n\\r]*(\\r?\\n|\\r)/))) {\n                line = match[0];\n                result += line;\n                pos += line.length;\n                continue;\n            } else if ((match = line.match(/(\\s+)[^\\s]*$/)) && match[0].length - (afterSpace ? (match[1] || '').length : 0) < line.length) {\n                line = line.substr(0, line.length - (match[0].length - (afterSpace ? (match[1] || '').length : 0)));\n            } else if ((match = str.substr(pos + line.length).match(/^[^\\s]+(\\s*)/))) {\n                line = line + match[0].substr(0, match[0].length - (!afterSpace ? (match[1] || '').length : 0));\n            }\n\n            result += line;\n            pos += line.length;\n            if (pos < len) {\n                result += '\\r\\n';\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Splits a mime encoded string. Needed for dividing mime words into smaller chunks\n     *\n     * @param {String} str Mime encoded string to be split up\n     * @param {Number} maxlen Maximum length of characters for one part (minimum 12)\n     * @return {Array} Split string\n     */\n    splitMimeEncodedString(str, maxlen) {\n        let curLine,\n            match,\n            chr,\n            done,\n            lines = [];\n\n        // require at least 12 symbols to fit possible 4 octet UTF-8 sequences\n        maxlen = Math.max(maxlen || 0, 12);\n\n        while (str.length) {\n            curLine = str.substr(0, maxlen);\n\n            // move incomplete escaped char back to main\n            if ((match = curLine.match(/[=][0-9A-F]?$/i))) {\n                curLine = curLine.substr(0, match.index);\n            }\n\n            done = false;\n            while (!done) {\n                done = true;\n                // check if not middle of a unicode char sequence\n                if ((match = str.substr(curLine.length).match(/^[=]([0-9A-F]{2})/i))) {\n                    chr = parseInt(match[1], 16);\n                    // invalid sequence, move one char back anc recheck\n                    if (chr < 0xc2 && chr > 0x7f) {\n                        curLine = curLine.substr(0, curLine.length - 3);\n                        done = false;\n                    }\n                }\n            }\n\n            if (curLine.length) {\n                lines.push(curLine);\n            }\n            str = str.substr(curLine.length);\n        }\n\n        return lines;\n    }\n\n    encodeURICharComponent(chr) {\n        let res = '';\n        let ord = chr.charCodeAt(0).toString(16).toUpperCase();\n\n        if (ord.length % 2) {\n            ord = '0' + ord;\n        }\n\n        if (ord.length > 2) {\n            for (let i = 0, len = ord.length / 2; i < len; i++) {\n                res += '%' + ord.substr(i, 2);\n            }\n        } else {\n            res += '%' + ord;\n        }\n\n        return res;\n    }\n\n    safeEncodeURIComponent(str) {\n        str = (str || '').toString();\n\n        try {\n            // might throw if we try to encode invalid sequences, eg. partial emoji\n            str = encodeURIComponent(str);\n        } catch (E) {\n            // should never run\n            return str.replace(/[^\\x00-\\x1F *'()<>@,;:\\\\\"[\\]?=\\u007F-\\uFFFF]+/g, '');\n        }\n\n        // ensure chars that are not handled by encodeURICompent are converted as well\n        return str.replace(/[\\x00-\\x1F *'()<>@,;:\\\\\"[\\]?=\\u007F-\\uFFFF]/g, chr => this.encodeURICharComponent(chr));\n    }\n}\n\nmodule.exports = new Libmime();\nmodule.exports.Libmime = Libmime;\n"]}