summaryrefslogtreecommitdiff
path: root/node_modules/busboy/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/busboy/lib')
-rw-r--r--node_modules/busboy/lib/index.js57
-rw-r--r--node_modules/busboy/lib/types/multipart.js653
-rw-r--r--node_modules/busboy/lib/types/urlencoded.js350
-rw-r--r--node_modules/busboy/lib/utils.js596
4 files changed, 1656 insertions, 0 deletions
diff --git a/node_modules/busboy/lib/index.js b/node_modules/busboy/lib/index.js
new file mode 100644
index 0000000..873272d
--- /dev/null
+++ b/node_modules/busboy/lib/index.js
@@ -0,0 +1,57 @@
+'use strict';
+
+const { parseContentType } = require('./utils.js');
+
+function getInstance(cfg) {
+ const headers = cfg.headers;
+ const conType = parseContentType(headers['content-type']);
+ if (!conType)
+ throw new Error('Malformed content type');
+
+ for (const type of TYPES) {
+ const matched = type.detect(conType);
+ if (!matched)
+ continue;
+
+ const instanceCfg = {
+ limits: cfg.limits,
+ headers,
+ conType,
+ highWaterMark: undefined,
+ fileHwm: undefined,
+ defCharset: undefined,
+ defParamCharset: undefined,
+ preservePath: false,
+ };
+ if (cfg.highWaterMark)
+ instanceCfg.highWaterMark = cfg.highWaterMark;
+ if (cfg.fileHwm)
+ instanceCfg.fileHwm = cfg.fileHwm;
+ instanceCfg.defCharset = cfg.defCharset;
+ instanceCfg.defParamCharset = cfg.defParamCharset;
+ instanceCfg.preservePath = cfg.preservePath;
+ return new type(instanceCfg);
+ }
+
+ throw new Error(`Unsupported content type: ${headers['content-type']}`);
+}
+
+// Note: types are explicitly listed here for easier bundling
+// See: https://github.com/mscdex/busboy/issues/121
+const TYPES = [
+ require('./types/multipart'),
+ require('./types/urlencoded'),
+].filter(function(typemod) { return typeof typemod.detect === 'function'; });
+
+module.exports = (cfg) => {
+ if (typeof cfg !== 'object' || cfg === null)
+ cfg = {};
+
+ if (typeof cfg.headers !== 'object'
+ || cfg.headers === null
+ || typeof cfg.headers['content-type'] !== 'string') {
+ throw new Error('Missing Content-Type');
+ }
+
+ return getInstance(cfg);
+};
diff --git a/node_modules/busboy/lib/types/multipart.js b/node_modules/busboy/lib/types/multipart.js
new file mode 100644
index 0000000..cc0d7bb
--- /dev/null
+++ b/node_modules/busboy/lib/types/multipart.js
@@ -0,0 +1,653 @@
+'use strict';
+
+const { Readable, Writable } = require('stream');
+
+const StreamSearch = require('streamsearch');
+
+const {
+ basename,
+ convertToUTF8,
+ getDecoder,
+ parseContentType,
+ parseDisposition,
+} = require('../utils.js');
+
+const BUF_CRLF = Buffer.from('\r\n');
+const BUF_CR = Buffer.from('\r');
+const BUF_DASH = Buffer.from('-');
+
+function noop() {}
+
+const MAX_HEADER_PAIRS = 2000; // From node
+const MAX_HEADER_SIZE = 16 * 1024; // From node (its default value)
+
+const HPARSER_NAME = 0;
+const HPARSER_PRE_OWS = 1;
+const HPARSER_VALUE = 2;
+class HeaderParser {
+ constructor(cb) {
+ this.header = Object.create(null);
+ this.pairCount = 0;
+ this.byteCount = 0;
+ this.state = HPARSER_NAME;
+ this.name = '';
+ this.value = '';
+ this.crlf = 0;
+ this.cb = cb;
+ }
+
+ reset() {
+ this.header = Object.create(null);
+ this.pairCount = 0;
+ this.byteCount = 0;
+ this.state = HPARSER_NAME;
+ this.name = '';
+ this.value = '';
+ this.crlf = 0;
+ }
+
+ push(chunk, pos, end) {
+ let start = pos;
+ while (pos < end) {
+ switch (this.state) {
+ case HPARSER_NAME: {
+ let done = false;
+ for (; pos < end; ++pos) {
+ if (this.byteCount === MAX_HEADER_SIZE)
+ return -1;
+ ++this.byteCount;
+ const code = chunk[pos];
+ if (TOKEN[code] !== 1) {
+ if (code !== 58/* ':' */)
+ return -1;
+ this.name += chunk.latin1Slice(start, pos);
+ if (this.name.length === 0)
+ return -1;
+ ++pos;
+ done = true;
+ this.state = HPARSER_PRE_OWS;
+ break;
+ }
+ }
+ if (!done) {
+ this.name += chunk.latin1Slice(start, pos);
+ break;
+ }
+ // FALLTHROUGH
+ }
+ case HPARSER_PRE_OWS: {
+ // Skip optional whitespace
+ let done = false;
+ for (; pos < end; ++pos) {
+ if (this.byteCount === MAX_HEADER_SIZE)
+ return -1;
+ ++this.byteCount;
+ const code = chunk[pos];
+ if (code !== 32/* ' ' */ && code !== 9/* '\t' */) {
+ start = pos;
+ done = true;
+ this.state = HPARSER_VALUE;
+ break;
+ }
+ }
+ if (!done)
+ break;
+ // FALLTHROUGH
+ }
+ case HPARSER_VALUE:
+ switch (this.crlf) {
+ case 0: // Nothing yet
+ for (; pos < end; ++pos) {
+ if (this.byteCount === MAX_HEADER_SIZE)
+ return -1;
+ ++this.byteCount;
+ const code = chunk[pos];
+ if (FIELD_VCHAR[code] !== 1) {
+ if (code !== 13/* '\r' */)
+ return -1;
+ ++this.crlf;
+ break;
+ }
+ }
+ this.value += chunk.latin1Slice(start, pos++);
+ break;
+ case 1: // Received CR
+ if (this.byteCount === MAX_HEADER_SIZE)
+ return -1;
+ ++this.byteCount;
+ if (chunk[pos++] !== 10/* '\n' */)
+ return -1;
+ ++this.crlf;
+ break;
+ case 2: { // Received CR LF
+ if (this.byteCount === MAX_HEADER_SIZE)
+ return -1;
+ ++this.byteCount;
+ const code = chunk[pos];
+ if (code === 32/* ' ' */ || code === 9/* '\t' */) {
+ // Folded value
+ start = pos;
+ this.crlf = 0;
+ } else {
+ if (++this.pairCount < MAX_HEADER_PAIRS) {
+ this.name = this.name.toLowerCase();
+ if (this.header[this.name] === undefined)
+ this.header[this.name] = [this.value];
+ else
+ this.header[this.name].push(this.value);
+ }
+ if (code === 13/* '\r' */) {
+ ++this.crlf;
+ ++pos;
+ } else {
+ // Assume start of next header field name
+ start = pos;
+ this.crlf = 0;
+ this.state = HPARSER_NAME;
+ this.name = '';
+ this.value = '';
+ }
+ }
+ break;
+ }
+ case 3: { // Received CR LF CR
+ if (this.byteCount === MAX_HEADER_SIZE)
+ return -1;
+ ++this.byteCount;
+ if (chunk[pos++] !== 10/* '\n' */)
+ return -1;
+ // End of header
+ const header = this.header;
+ this.reset();
+ this.cb(header);
+ return pos;
+ }
+ }
+ break;
+ }
+ }
+
+ return pos;
+ }
+}
+
+class FileStream extends Readable {
+ constructor(opts, owner) {
+ super(opts);
+ this.truncated = false;
+ this._readcb = null;
+ this.once('end', () => {
+ // We need to make sure that we call any outstanding _writecb() that is
+ // associated with this file so that processing of the rest of the form
+ // can continue. This may not happen if the file stream ends right after
+ // backpressure kicks in, so we force it here.
+ this._read();
+ if (--owner._fileEndsLeft === 0 && owner._finalcb) {
+ const cb = owner._finalcb;
+ owner._finalcb = null;
+ // Make sure other 'end' event handlers get a chance to be executed
+ // before busboy's 'finish' event is emitted
+ process.nextTick(cb);
+ }
+ });
+ }
+ _read(n) {
+ const cb = this._readcb;
+ if (cb) {
+ this._readcb = null;
+ cb();
+ }
+ }
+}
+
+const ignoreData = {
+ push: (chunk, pos) => {},
+ destroy: () => {},
+};
+
+function callAndUnsetCb(self, err) {
+ const cb = self._writecb;
+ self._writecb = null;
+ if (err)
+ self.destroy(err);
+ else if (cb)
+ cb();
+}
+
+function nullDecoder(val, hint) {
+ return val;
+}
+
+class Multipart extends Writable {
+ constructor(cfg) {
+ const streamOpts = {
+ autoDestroy: true,
+ emitClose: true,
+ highWaterMark: (typeof cfg.highWaterMark === 'number'
+ ? cfg.highWaterMark
+ : undefined),
+ };
+ super(streamOpts);
+
+ if (!cfg.conType.params || typeof cfg.conType.params.boundary !== 'string')
+ throw new Error('Multipart: Boundary not found');
+
+ const boundary = cfg.conType.params.boundary;
+ const paramDecoder = (typeof cfg.defParamCharset === 'string'
+ && cfg.defParamCharset
+ ? getDecoder(cfg.defParamCharset)
+ : nullDecoder);
+ const defCharset = (cfg.defCharset || 'utf8');
+ const preservePath = cfg.preservePath;
+ const fileOpts = {
+ autoDestroy: true,
+ emitClose: true,
+ highWaterMark: (typeof cfg.fileHwm === 'number'
+ ? cfg.fileHwm
+ : undefined),
+ };
+
+ const limits = cfg.limits;
+ const fieldSizeLimit = (limits && typeof limits.fieldSize === 'number'
+ ? limits.fieldSize
+ : 1 * 1024 * 1024);
+ const fileSizeLimit = (limits && typeof limits.fileSize === 'number'
+ ? limits.fileSize
+ : Infinity);
+ const filesLimit = (limits && typeof limits.files === 'number'
+ ? limits.files
+ : Infinity);
+ const fieldsLimit = (limits && typeof limits.fields === 'number'
+ ? limits.fields
+ : Infinity);
+ const partsLimit = (limits && typeof limits.parts === 'number'
+ ? limits.parts
+ : Infinity);
+
+ let parts = -1; // Account for initial boundary
+ let fields = 0;
+ let files = 0;
+ let skipPart = false;
+
+ this._fileEndsLeft = 0;
+ this._fileStream = undefined;
+ this._complete = false;
+ let fileSize = 0;
+
+ let field;
+ let fieldSize = 0;
+ let partCharset;
+ let partEncoding;
+ let partType;
+ let partName;
+ let partTruncated = false;
+
+ let hitFilesLimit = false;
+ let hitFieldsLimit = false;
+
+ this._hparser = null;
+ const hparser = new HeaderParser((header) => {
+ this._hparser = null;
+ skipPart = false;
+
+ partType = 'text/plain';
+ partCharset = defCharset;
+ partEncoding = '7bit';
+ partName = undefined;
+ partTruncated = false;
+
+ let filename;
+ if (!header['content-disposition']) {
+ skipPart = true;
+ return;
+ }
+
+ const disp = parseDisposition(header['content-disposition'][0],
+ paramDecoder);
+ if (!disp || disp.type !== 'form-data') {
+ skipPart = true;
+ return;
+ }
+
+ if (disp.params) {
+ if (disp.params.name)
+ partName = disp.params.name;
+
+ if (disp.params['filename*'])
+ filename = disp.params['filename*'];
+ else if (disp.params.filename)
+ filename = disp.params.filename;
+
+ if (filename !== undefined && !preservePath)
+ filename = basename(filename);
+ }
+
+ if (header['content-type']) {
+ const conType = parseContentType(header['content-type'][0]);
+ if (conType) {
+ partType = `${conType.type}/${conType.subtype}`;
+ if (conType.params && typeof conType.params.charset === 'string')
+ partCharset = conType.params.charset.toLowerCase();
+ }
+ }
+
+ if (header['content-transfer-encoding'])
+ partEncoding = header['content-transfer-encoding'][0].toLowerCase();
+
+ if (partType === 'application/octet-stream' || filename !== undefined) {
+ // File
+
+ if (files === filesLimit) {
+ if (!hitFilesLimit) {
+ hitFilesLimit = true;
+ this.emit('filesLimit');
+ }
+ skipPart = true;
+ return;
+ }
+ ++files;
+
+ if (this.listenerCount('file') === 0) {
+ skipPart = true;
+ return;
+ }
+
+ fileSize = 0;
+ this._fileStream = new FileStream(fileOpts, this);
+ ++this._fileEndsLeft;
+ this.emit(
+ 'file',
+ partName,
+ this._fileStream,
+ { filename,
+ encoding: partEncoding,
+ mimeType: partType }
+ );
+ } else {
+ // Non-file
+
+ if (fields === fieldsLimit) {
+ if (!hitFieldsLimit) {
+ hitFieldsLimit = true;
+ this.emit('fieldsLimit');
+ }
+ skipPart = true;
+ return;
+ }
+ ++fields;
+
+ if (this.listenerCount('field') === 0) {
+ skipPart = true;
+ return;
+ }
+
+ field = [];
+ fieldSize = 0;
+ }
+ });
+
+ let matchPostBoundary = 0;
+ const ssCb = (isMatch, data, start, end, isDataSafe) => {
+retrydata:
+ while (data) {
+ if (this._hparser !== null) {
+ const ret = this._hparser.push(data, start, end);
+ if (ret === -1) {
+ this._hparser = null;
+ hparser.reset();
+ this.emit('error', new Error('Malformed part header'));
+ break;
+ }
+ start = ret;
+ }
+
+ if (start === end)
+ break;
+
+ if (matchPostBoundary !== 0) {
+ if (matchPostBoundary === 1) {
+ switch (data[start]) {
+ case 45: // '-'
+ // Try matching '--' after boundary
+ matchPostBoundary = 2;
+ ++start;
+ break;
+ case 13: // '\r'
+ // Try matching CR LF before header
+ matchPostBoundary = 3;
+ ++start;
+ break;
+ default:
+ matchPostBoundary = 0;
+ }
+ if (start === end)
+ return;
+ }
+
+ if (matchPostBoundary === 2) {
+ matchPostBoundary = 0;
+ if (data[start] === 45/* '-' */) {
+ // End of multipart data
+ this._complete = true;
+ this._bparser = ignoreData;
+ return;
+ }
+ // We saw something other than '-', so put the dash we consumed
+ // "back"
+ const writecb = this._writecb;
+ this._writecb = noop;
+ ssCb(false, BUF_DASH, 0, 1, false);
+ this._writecb = writecb;
+ } else if (matchPostBoundary === 3) {
+ matchPostBoundary = 0;
+ if (data[start] === 10/* '\n' */) {
+ ++start;
+ if (parts >= partsLimit)
+ break;
+ // Prepare the header parser
+ this._hparser = hparser;
+ if (start === end)
+ break;
+ // Process the remaining data as a header
+ continue retrydata;
+ } else {
+ // We saw something other than LF, so put the CR we consumed
+ // "back"
+ const writecb = this._writecb;
+ this._writecb = noop;
+ ssCb(false, BUF_CR, 0, 1, false);
+ this._writecb = writecb;
+ }
+ }
+ }
+
+ if (!skipPart) {
+ if (this._fileStream) {
+ let chunk;
+ const actualLen = Math.min(end - start, fileSizeLimit - fileSize);
+ if (!isDataSafe) {
+ chunk = Buffer.allocUnsafe(actualLen);
+ data.copy(chunk, 0, start, start + actualLen);
+ } else {
+ chunk = data.slice(start, start + actualLen);
+ }
+
+ fileSize += chunk.length;
+ if (fileSize === fileSizeLimit) {
+ if (chunk.length > 0)
+ this._fileStream.push(chunk);
+ this._fileStream.emit('limit');
+ this._fileStream.truncated = true;
+ skipPart = true;
+ } else if (!this._fileStream.push(chunk)) {
+ if (this._writecb)
+ this._fileStream._readcb = this._writecb;
+ this._writecb = null;
+ }
+ } else if (field !== undefined) {
+ let chunk;
+ const actualLen = Math.min(
+ end - start,
+ fieldSizeLimit - fieldSize
+ );
+ if (!isDataSafe) {
+ chunk = Buffer.allocUnsafe(actualLen);
+ data.copy(chunk, 0, start, start + actualLen);
+ } else {
+ chunk = data.slice(start, start + actualLen);
+ }
+
+ fieldSize += actualLen;
+ field.push(chunk);
+ if (fieldSize === fieldSizeLimit) {
+ skipPart = true;
+ partTruncated = true;
+ }
+ }
+ }
+
+ break;
+ }
+
+ if (isMatch) {
+ matchPostBoundary = 1;
+
+ if (this._fileStream) {
+ // End the active file stream if the previous part was a file
+ this._fileStream.push(null);
+ this._fileStream = null;
+ } else if (field !== undefined) {
+ let data;
+ switch (field.length) {
+ case 0:
+ data = '';
+ break;
+ case 1:
+ data = convertToUTF8(field[0], partCharset, 0);
+ break;
+ default:
+ data = convertToUTF8(
+ Buffer.concat(field, fieldSize),
+ partCharset,
+ 0
+ );
+ }
+ field = undefined;
+ fieldSize = 0;
+ this.emit(
+ 'field',
+ partName,
+ data,
+ { nameTruncated: false,
+ valueTruncated: partTruncated,
+ encoding: partEncoding,
+ mimeType: partType }
+ );
+ }
+
+ if (++parts === partsLimit)
+ this.emit('partsLimit');
+ }
+ };
+ this._bparser = new StreamSearch(`\r\n--${boundary}`, ssCb);
+
+ this._writecb = null;
+ this._finalcb = null;
+
+ // Just in case there is no preamble
+ this.write(BUF_CRLF);
+ }
+
+ static detect(conType) {
+ return (conType.type === 'multipart' && conType.subtype === 'form-data');
+ }
+
+ _write(chunk, enc, cb) {
+ this._writecb = cb;
+ this._bparser.push(chunk, 0);
+ if (this._writecb)
+ callAndUnsetCb(this);
+ }
+
+ _destroy(err, cb) {
+ this._hparser = null;
+ this._bparser = ignoreData;
+ if (!err)
+ err = checkEndState(this);
+ const fileStream = this._fileStream;
+ if (fileStream) {
+ this._fileStream = null;
+ fileStream.destroy(err);
+ }
+ cb(err);
+ }
+
+ _final(cb) {
+ this._bparser.destroy();
+ if (!this._complete)
+ return cb(new Error('Unexpected end of form'));
+ if (this._fileEndsLeft)
+ this._finalcb = finalcb.bind(null, this, cb);
+ else
+ finalcb(this, cb);
+ }
+}
+
+function finalcb(self, cb, err) {
+ if (err)
+ return cb(err);
+ err = checkEndState(self);
+ cb(err);
+}
+
+function checkEndState(self) {
+ if (self._hparser)
+ return new Error('Malformed part header');
+ const fileStream = self._fileStream;
+ if (fileStream) {
+ self._fileStream = null;
+ fileStream.destroy(new Error('Unexpected end of file'));
+ }
+ if (!self._complete)
+ return new Error('Unexpected end of form');
+}
+
+const TOKEN = [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+];
+
+const FIELD_VCHAR = [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+];
+
+module.exports = Multipart;
diff --git a/node_modules/busboy/lib/types/urlencoded.js b/node_modules/busboy/lib/types/urlencoded.js
new file mode 100644
index 0000000..5c463a2
--- /dev/null
+++ b/node_modules/busboy/lib/types/urlencoded.js
@@ -0,0 +1,350 @@
+'use strict';
+
+const { Writable } = require('stream');
+
+const { getDecoder } = require('../utils.js');
+
+class URLEncoded extends Writable {
+ constructor(cfg) {
+ const streamOpts = {
+ autoDestroy: true,
+ emitClose: true,
+ highWaterMark: (typeof cfg.highWaterMark === 'number'
+ ? cfg.highWaterMark
+ : undefined),
+ };
+ super(streamOpts);
+
+ let charset = (cfg.defCharset || 'utf8');
+ if (cfg.conType.params && typeof cfg.conType.params.charset === 'string')
+ charset = cfg.conType.params.charset;
+
+ this.charset = charset;
+
+ const limits = cfg.limits;
+ this.fieldSizeLimit = (limits && typeof limits.fieldSize === 'number'
+ ? limits.fieldSize
+ : 1 * 1024 * 1024);
+ this.fieldsLimit = (limits && typeof limits.fields === 'number'
+ ? limits.fields
+ : Infinity);
+ this.fieldNameSizeLimit = (
+ limits && typeof limits.fieldNameSize === 'number'
+ ? limits.fieldNameSize
+ : 100
+ );
+
+ this._inKey = true;
+ this._keyTrunc = false;
+ this._valTrunc = false;
+ this._bytesKey = 0;
+ this._bytesVal = 0;
+ this._fields = 0;
+ this._key = '';
+ this._val = '';
+ this._byte = -2;
+ this._lastPos = 0;
+ this._encode = 0;
+ this._decoder = getDecoder(charset);
+ }
+
+ static detect(conType) {
+ return (conType.type === 'application'
+ && conType.subtype === 'x-www-form-urlencoded');
+ }
+
+ _write(chunk, enc, cb) {
+ if (this._fields >= this.fieldsLimit)
+ return cb();
+
+ let i = 0;
+ const len = chunk.length;
+ this._lastPos = 0;
+
+ // Check if we last ended mid-percent-encoded byte
+ if (this._byte !== -2) {
+ i = readPctEnc(this, chunk, i, len);
+ if (i === -1)
+ return cb(new Error('Malformed urlencoded form'));
+ if (i >= len)
+ return cb();
+ if (this._inKey)
+ ++this._bytesKey;
+ else
+ ++this._bytesVal;
+ }
+
+main:
+ while (i < len) {
+ if (this._inKey) {
+ // Parsing key
+
+ i = skipKeyBytes(this, chunk, i, len);
+
+ while (i < len) {
+ switch (chunk[i]) {
+ case 61: // '='
+ if (this._lastPos < i)
+ this._key += chunk.latin1Slice(this._lastPos, i);
+ this._lastPos = ++i;
+ this._key = this._decoder(this._key, this._encode);
+ this._encode = 0;
+ this._inKey = false;
+ continue main;
+ case 38: // '&'
+ if (this._lastPos < i)
+ this._key += chunk.latin1Slice(this._lastPos, i);
+ this._lastPos = ++i;
+ this._key = this._decoder(this._key, this._encode);
+ this._encode = 0;
+ if (this._bytesKey > 0) {
+ this.emit(
+ 'field',
+ this._key,
+ '',
+ { nameTruncated: this._keyTrunc,
+ valueTruncated: false,
+ encoding: this.charset,
+ mimeType: 'text/plain' }
+ );
+ }
+ this._key = '';
+ this._val = '';
+ this._keyTrunc = false;
+ this._valTrunc = false;
+ this._bytesKey = 0;
+ this._bytesVal = 0;
+ if (++this._fields >= this.fieldsLimit) {
+ this.emit('fieldsLimit');
+ return cb();
+ }
+ continue;
+ case 43: // '+'
+ if (this._lastPos < i)
+ this._key += chunk.latin1Slice(this._lastPos, i);
+ this._key += ' ';
+ this._lastPos = i + 1;
+ break;
+ case 37: // '%'
+ if (this._encode === 0)
+ this._encode = 1;
+ if (this._lastPos < i)
+ this._key += chunk.latin1Slice(this._lastPos, i);
+ this._lastPos = i + 1;
+ this._byte = -1;
+ i = readPctEnc(this, chunk, i + 1, len);
+ if (i === -1)
+ return cb(new Error('Malformed urlencoded form'));
+ if (i >= len)
+ return cb();
+ ++this._bytesKey;
+ i = skipKeyBytes(this, chunk, i, len);
+ continue;
+ }
+ ++i;
+ ++this._bytesKey;
+ i = skipKeyBytes(this, chunk, i, len);
+ }
+ if (this._lastPos < i)
+ this._key += chunk.latin1Slice(this._lastPos, i);
+ } else {
+ // Parsing value
+
+ i = skipValBytes(this, chunk, i, len);
+
+ while (i < len) {
+ switch (chunk[i]) {
+ case 38: // '&'
+ if (this._lastPos < i)
+ this._val += chunk.latin1Slice(this._lastPos, i);
+ this._lastPos = ++i;
+ this._inKey = true;
+ this._val = this._decoder(this._val, this._encode);
+ this._encode = 0;
+ if (this._bytesKey > 0 || this._bytesVal > 0) {
+ this.emit(
+ 'field',
+ this._key,
+ this._val,
+ { nameTruncated: this._keyTrunc,
+ valueTruncated: this._valTrunc,
+ encoding: this.charset,
+ mimeType: 'text/plain' }
+ );
+ }
+ this._key = '';
+ this._val = '';
+ this._keyTrunc = false;
+ this._valTrunc = false;
+ this._bytesKey = 0;
+ this._bytesVal = 0;
+ if (++this._fields >= this.fieldsLimit) {
+ this.emit('fieldsLimit');
+ return cb();
+ }
+ continue main;
+ case 43: // '+'
+ if (this._lastPos < i)
+ this._val += chunk.latin1Slice(this._lastPos, i);
+ this._val += ' ';
+ this._lastPos = i + 1;
+ break;
+ case 37: // '%'
+ if (this._encode === 0)
+ this._encode = 1;
+ if (this._lastPos < i)
+ this._val += chunk.latin1Slice(this._lastPos, i);
+ this._lastPos = i + 1;
+ this._byte = -1;
+ i = readPctEnc(this, chunk, i + 1, len);
+ if (i === -1)
+ return cb(new Error('Malformed urlencoded form'));
+ if (i >= len)
+ return cb();
+ ++this._bytesVal;
+ i = skipValBytes(this, chunk, i, len);
+ continue;
+ }
+ ++i;
+ ++this._bytesVal;
+ i = skipValBytes(this, chunk, i, len);
+ }
+ if (this._lastPos < i)
+ this._val += chunk.latin1Slice(this._lastPos, i);
+ }
+ }
+
+ cb();
+ }
+
+ _final(cb) {
+ if (this._byte !== -2)
+ return cb(new Error('Malformed urlencoded form'));
+ if (!this._inKey || this._bytesKey > 0 || this._bytesVal > 0) {
+ if (this._inKey)
+ this._key = this._decoder(this._key, this._encode);
+ else
+ this._val = this._decoder(this._val, this._encode);
+ this.emit(
+ 'field',
+ this._key,
+ this._val,
+ { nameTruncated: this._keyTrunc,
+ valueTruncated: this._valTrunc,
+ encoding: this.charset,
+ mimeType: 'text/plain' }
+ );
+ }
+ cb();
+ }
+}
+
+function readPctEnc(self, chunk, pos, len) {
+ if (pos >= len)
+ return len;
+
+ if (self._byte === -1) {
+ // We saw a '%' but no hex characters yet
+ const hexUpper = HEX_VALUES[chunk[pos++]];
+ if (hexUpper === -1)
+ return -1;
+
+ if (hexUpper >= 8)
+ self._encode = 2; // Indicate high bits detected
+
+ if (pos < len) {
+ // Both hex characters are in this chunk
+ const hexLower = HEX_VALUES[chunk[pos++]];
+ if (hexLower === -1)
+ return -1;
+
+ if (self._inKey)
+ self._key += String.fromCharCode((hexUpper << 4) + hexLower);
+ else
+ self._val += String.fromCharCode((hexUpper << 4) + hexLower);
+
+ self._byte = -2;
+ self._lastPos = pos;
+ } else {
+ // Only one hex character was available in this chunk
+ self._byte = hexUpper;
+ }
+ } else {
+ // We saw only one hex character so far
+ const hexLower = HEX_VALUES[chunk[pos++]];
+ if (hexLower === -1)
+ return -1;
+
+ if (self._inKey)
+ self._key += String.fromCharCode((self._byte << 4) + hexLower);
+ else
+ self._val += String.fromCharCode((self._byte << 4) + hexLower);
+
+ self._byte = -2;
+ self._lastPos = pos;
+ }
+
+ return pos;
+}
+
+function skipKeyBytes(self, chunk, pos, len) {
+ // Skip bytes if we've truncated
+ if (self._bytesKey > self.fieldNameSizeLimit) {
+ if (!self._keyTrunc) {
+ if (self._lastPos < pos)
+ self._key += chunk.latin1Slice(self._lastPos, pos - 1);
+ }
+ self._keyTrunc = true;
+ for (; pos < len; ++pos) {
+ const code = chunk[pos];
+ if (code === 61/* '=' */ || code === 38/* '&' */)
+ break;
+ ++self._bytesKey;
+ }
+ self._lastPos = pos;
+ }
+
+ return pos;
+}
+
+function skipValBytes(self, chunk, pos, len) {
+ // Skip bytes if we've truncated
+ if (self._bytesVal > self.fieldSizeLimit) {
+ if (!self._valTrunc) {
+ if (self._lastPos < pos)
+ self._val += chunk.latin1Slice(self._lastPos, pos - 1);
+ }
+ self._valTrunc = true;
+ for (; pos < len; ++pos) {
+ if (chunk[pos] === 38/* '&' */)
+ break;
+ ++self._bytesVal;
+ }
+ self._lastPos = pos;
+ }
+
+ return pos;
+}
+
+/* eslint-disable no-multi-spaces */
+const HEX_VALUES = [
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+];
+/* eslint-enable no-multi-spaces */
+
+module.exports = URLEncoded;
diff --git a/node_modules/busboy/lib/utils.js b/node_modules/busboy/lib/utils.js
new file mode 100644
index 0000000..8274f6c
--- /dev/null
+++ b/node_modules/busboy/lib/utils.js
@@ -0,0 +1,596 @@
+'use strict';
+
+function parseContentType(str) {
+ if (str.length === 0)
+ return;
+
+ const params = Object.create(null);
+ let i = 0;
+
+ // Parse type
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (TOKEN[code] !== 1) {
+ if (code !== 47/* '/' */ || i === 0)
+ return;
+ break;
+ }
+ }
+ // Check for type without subtype
+ if (i === str.length)
+ return;
+
+ const type = str.slice(0, i).toLowerCase();
+
+ // Parse subtype
+ const subtypeStart = ++i;
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (TOKEN[code] !== 1) {
+ // Make sure we have a subtype
+ if (i === subtypeStart)
+ return;
+
+ if (parseContentTypeParams(str, i, params) === undefined)
+ return;
+ break;
+ }
+ }
+ // Make sure we have a subtype
+ if (i === subtypeStart)
+ return;
+
+ const subtype = str.slice(subtypeStart, i).toLowerCase();
+
+ return { type, subtype, params };
+}
+
+function parseContentTypeParams(str, i, params) {
+ while (i < str.length) {
+ // Consume whitespace
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (code !== 32/* ' ' */ && code !== 9/* '\t' */)
+ break;
+ }
+
+ // Ended on whitespace
+ if (i === str.length)
+ break;
+
+ // Check for malformed parameter
+ if (str.charCodeAt(i++) !== 59/* ';' */)
+ return;
+
+ // Consume whitespace
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (code !== 32/* ' ' */ && code !== 9/* '\t' */)
+ break;
+ }
+
+ // Ended on whitespace (malformed)
+ if (i === str.length)
+ return;
+
+ let name;
+ const nameStart = i;
+ // Parse parameter name
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (TOKEN[code] !== 1) {
+ if (code !== 61/* '=' */)
+ return;
+ break;
+ }
+ }
+
+ // No value (malformed)
+ if (i === str.length)
+ return;
+
+ name = str.slice(nameStart, i);
+ ++i; // Skip over '='
+
+ // No value (malformed)
+ if (i === str.length)
+ return;
+
+ let value = '';
+ let valueStart;
+ if (str.charCodeAt(i) === 34/* '"' */) {
+ valueStart = ++i;
+ let escaping = false;
+ // Parse quoted value
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (code === 92/* '\\' */) {
+ if (escaping) {
+ valueStart = i;
+ escaping = false;
+ } else {
+ value += str.slice(valueStart, i);
+ escaping = true;
+ }
+ continue;
+ }
+ if (code === 34/* '"' */) {
+ if (escaping) {
+ valueStart = i;
+ escaping = false;
+ continue;
+ }
+ value += str.slice(valueStart, i);
+ break;
+ }
+ if (escaping) {
+ valueStart = i - 1;
+ escaping = false;
+ }
+ // Invalid unescaped quoted character (malformed)
+ if (QDTEXT[code] !== 1)
+ return;
+ }
+
+ // No end quote (malformed)
+ if (i === str.length)
+ return;
+
+ ++i; // Skip over double quote
+ } else {
+ valueStart = i;
+ // Parse unquoted value
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (TOKEN[code] !== 1) {
+ // No value (malformed)
+ if (i === valueStart)
+ return;
+ break;
+ }
+ }
+ value = str.slice(valueStart, i);
+ }
+
+ name = name.toLowerCase();
+ if (params[name] === undefined)
+ params[name] = value;
+ }
+
+ return params;
+}
+
+function parseDisposition(str, defDecoder) {
+ if (str.length === 0)
+ return;
+
+ const params = Object.create(null);
+ let i = 0;
+
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (TOKEN[code] !== 1) {
+ if (parseDispositionParams(str, i, params, defDecoder) === undefined)
+ return;
+ break;
+ }
+ }
+
+ const type = str.slice(0, i).toLowerCase();
+
+ return { type, params };
+}
+
+function parseDispositionParams(str, i, params, defDecoder) {
+ while (i < str.length) {
+ // Consume whitespace
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (code !== 32/* ' ' */ && code !== 9/* '\t' */)
+ break;
+ }
+
+ // Ended on whitespace
+ if (i === str.length)
+ break;
+
+ // Check for malformed parameter
+ if (str.charCodeAt(i++) !== 59/* ';' */)
+ return;
+
+ // Consume whitespace
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (code !== 32/* ' ' */ && code !== 9/* '\t' */)
+ break;
+ }
+
+ // Ended on whitespace (malformed)
+ if (i === str.length)
+ return;
+
+ let name;
+ const nameStart = i;
+ // Parse parameter name
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (TOKEN[code] !== 1) {
+ if (code === 61/* '=' */)
+ break;
+ return;
+ }
+ }
+
+ // No value (malformed)
+ if (i === str.length)
+ return;
+
+ let value = '';
+ let valueStart;
+ let charset;
+ //~ let lang;
+ name = str.slice(nameStart, i);
+ if (name.charCodeAt(name.length - 1) === 42/* '*' */) {
+ // Extended value
+
+ const charsetStart = ++i;
+ // Parse charset name
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (CHARSET[code] !== 1) {
+ if (code !== 39/* '\'' */)
+ return;
+ break;
+ }
+ }
+
+ // Incomplete charset (malformed)
+ if (i === str.length)
+ return;
+
+ charset = str.slice(charsetStart, i);
+ ++i; // Skip over the '\''
+
+ //~ const langStart = ++i;
+ // Parse language name
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (code === 39/* '\'' */)
+ break;
+ }
+
+ // Incomplete language (malformed)
+ if (i === str.length)
+ return;
+
+ //~ lang = str.slice(langStart, i);
+ ++i; // Skip over the '\''
+
+ // No value (malformed)
+ if (i === str.length)
+ return;
+
+ valueStart = i;
+
+ let encode = 0;
+ // Parse value
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (EXTENDED_VALUE[code] !== 1) {
+ if (code === 37/* '%' */) {
+ let hexUpper;
+ let hexLower;
+ if (i + 2 < str.length
+ && (hexUpper = HEX_VALUES[str.charCodeAt(i + 1)]) !== -1
+ && (hexLower = HEX_VALUES[str.charCodeAt(i + 2)]) !== -1) {
+ const byteVal = (hexUpper << 4) + hexLower;
+ value += str.slice(valueStart, i);
+ value += String.fromCharCode(byteVal);
+ i += 2;
+ valueStart = i + 1;
+ if (byteVal >= 128)
+ encode = 2;
+ else if (encode === 0)
+ encode = 1;
+ continue;
+ }
+ // '%' disallowed in non-percent encoded contexts (malformed)
+ return;
+ }
+ break;
+ }
+ }
+
+ value += str.slice(valueStart, i);
+ value = convertToUTF8(value, charset, encode);
+ if (value === undefined)
+ return;
+ } else {
+ // Non-extended value
+
+ ++i; // Skip over '='
+
+ // No value (malformed)
+ if (i === str.length)
+ return;
+
+ if (str.charCodeAt(i) === 34/* '"' */) {
+ valueStart = ++i;
+ let escaping = false;
+ // Parse quoted value
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (code === 92/* '\\' */) {
+ if (escaping) {
+ valueStart = i;
+ escaping = false;
+ } else {
+ value += str.slice(valueStart, i);
+ escaping = true;
+ }
+ continue;
+ }
+ if (code === 34/* '"' */) {
+ if (escaping) {
+ valueStart = i;
+ escaping = false;
+ continue;
+ }
+ value += str.slice(valueStart, i);
+ break;
+ }
+ if (escaping) {
+ valueStart = i - 1;
+ escaping = false;
+ }
+ // Invalid unescaped quoted character (malformed)
+ if (QDTEXT[code] !== 1)
+ return;
+ }
+
+ // No end quote (malformed)
+ if (i === str.length)
+ return;
+
+ ++i; // Skip over double quote
+ } else {
+ valueStart = i;
+ // Parse unquoted value
+ for (; i < str.length; ++i) {
+ const code = str.charCodeAt(i);
+ if (TOKEN[code] !== 1) {
+ // No value (malformed)
+ if (i === valueStart)
+ return;
+ break;
+ }
+ }
+ value = str.slice(valueStart, i);
+ }
+
+ value = defDecoder(value, 2);
+ if (value === undefined)
+ return;
+ }
+
+ name = name.toLowerCase();
+ if (params[name] === undefined)
+ params[name] = value;
+ }
+
+ return params;
+}
+
+function getDecoder(charset) {
+ let lc;
+ while (true) {
+ switch (charset) {
+ case 'utf-8':
+ case 'utf8':
+ return decoders.utf8;
+ case 'latin1':
+ case 'ascii': // TODO: Make these a separate, strict decoder?
+ case 'us-ascii':
+ case 'iso-8859-1':
+ case 'iso8859-1':
+ case 'iso88591':
+ case 'iso_8859-1':
+ case 'windows-1252':
+ case 'iso_8859-1:1987':
+ case 'cp1252':
+ case 'x-cp1252':
+ return decoders.latin1;
+ case 'utf16le':
+ case 'utf-16le':
+ case 'ucs2':
+ case 'ucs-2':
+ return decoders.utf16le;
+ case 'base64':
+ return decoders.base64;
+ default:
+ if (lc === undefined) {
+ lc = true;
+ charset = charset.toLowerCase();
+ continue;
+ }
+ return decoders.other.bind(charset);
+ }
+ }
+}
+
+const decoders = {
+ utf8: (data, hint) => {
+ if (data.length === 0)
+ return '';
+ if (typeof data === 'string') {
+ // If `data` never had any percent-encoded bytes or never had any that
+ // were outside of the ASCII range, then we can safely just return the
+ // input since UTF-8 is ASCII compatible
+ if (hint < 2)
+ return data;
+
+ data = Buffer.from(data, 'latin1');
+ }
+ return data.utf8Slice(0, data.length);
+ },
+
+ latin1: (data, hint) => {
+ if (data.length === 0)
+ return '';
+ if (typeof data === 'string')
+ return data;
+ return data.latin1Slice(0, data.length);
+ },
+
+ utf16le: (data, hint) => {
+ if (data.length === 0)
+ return '';
+ if (typeof data === 'string')
+ data = Buffer.from(data, 'latin1');
+ return data.ucs2Slice(0, data.length);
+ },
+
+ base64: (data, hint) => {
+ if (data.length === 0)
+ return '';
+ if (typeof data === 'string')
+ data = Buffer.from(data, 'latin1');
+ return data.base64Slice(0, data.length);
+ },
+
+ other: (data, hint) => {
+ if (data.length === 0)
+ return '';
+ if (typeof data === 'string')
+ data = Buffer.from(data, 'latin1');
+ try {
+ const decoder = new TextDecoder(this);
+ return decoder.decode(data);
+ } catch {}
+ },
+};
+
+function convertToUTF8(data, charset, hint) {
+ const decode = getDecoder(charset);
+ if (decode)
+ return decode(data, hint);
+}
+
+function basename(path) {
+ if (typeof path !== 'string')
+ return '';
+ for (let i = path.length - 1; i >= 0; --i) {
+ switch (path.charCodeAt(i)) {
+ case 0x2F: // '/'
+ case 0x5C: // '\'
+ path = path.slice(i + 1);
+ return (path === '..' || path === '.' ? '' : path);
+ }
+ }
+ return (path === '..' || path === '.' ? '' : path);
+}
+
+const TOKEN = [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+];
+
+const QDTEXT = [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+];
+
+const CHARSET = [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+];
+
+const EXTENDED_VALUE = [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+];
+
+/* eslint-disable no-multi-spaces */
+const HEX_VALUES = [
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+];
+/* eslint-enable no-multi-spaces */
+
+module.exports = {
+ basename,
+ convertToUTF8,
+ getDecoder,
+ parseContentType,
+ parseDisposition,
+};