diff options
author | Minteck <contact@minteck.org> | 2023-02-23 19:34:56 +0100 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2023-02-23 19:34:56 +0100 |
commit | 3d1cd02f27518f1a04374c7c8320cd5d82ede6e9 (patch) | |
tree | 75be5fba4368472fb11c8015aee026b2b9a71888 /together/node_modules/formidable/src/Formidable.js | |
parent | 8cc1f13c17fa2fb5a4410542d39e650e02945634 (diff) | |
download | pluralconnect-3d1cd02f27518f1a04374c7c8320cd5d82ede6e9.tar.gz pluralconnect-3d1cd02f27518f1a04374c7c8320cd5d82ede6e9.tar.bz2 pluralconnect-3d1cd02f27518f1a04374c7c8320cd5d82ede6e9.zip |
Updated 40 files, added 37 files, deleted 1103 files and renamed 3905 files (automated)
Diffstat (limited to 'together/node_modules/formidable/src/Formidable.js')
-rw-r--r-- | together/node_modules/formidable/src/Formidable.js | 617 |
1 files changed, 0 insertions, 617 deletions
diff --git a/together/node_modules/formidable/src/Formidable.js b/together/node_modules/formidable/src/Formidable.js deleted file mode 100644 index 0542700..0000000 --- a/together/node_modules/formidable/src/Formidable.js +++ /dev/null @@ -1,617 +0,0 @@ -/* eslint-disable class-methods-use-this */ -/* eslint-disable no-underscore-dangle */ - -'use strict'; - -const os = require('os'); -const path = require('path'); -const hexoid = require('hexoid'); -const once = require('once'); -const dezalgo = require('dezalgo'); -const { EventEmitter } = require('events'); -const { StringDecoder } = require('string_decoder'); -const qs = require('qs'); - -const toHexoId = hexoid(25); -const DEFAULT_OPTIONS = { - maxFields: 1000, - maxFieldsSize: 20 * 1024 * 1024, - maxFileSize: 200 * 1024 * 1024, - minFileSize: 1, - allowEmptyFiles: true, - keepExtensions: false, - encoding: 'utf-8', - hashAlgorithm: false, - uploadDir: os.tmpdir(), - multiples: false, - enabledPlugins: ['octetstream', 'querystring', 'multipart', 'json'], - fileWriteStreamHandler: null, - defaultInvalidName: 'invalid-name', - filter: function () { - return true; - }, -}; - -const PersistentFile = require('./PersistentFile'); -const VolatileFile = require('./VolatileFile'); -const DummyParser = require('./parsers/Dummy'); -const MultipartParser = require('./parsers/Multipart'); -const errors = require('./FormidableError.js'); - -const { FormidableError } = errors; - -function hasOwnProp(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} - -class IncomingForm extends EventEmitter { - constructor(options = {}) { - super(); - - this.options = { ...DEFAULT_OPTIONS, ...options }; - - const dir = path.resolve( - this.options.uploadDir || this.options.uploaddir || os.tmpdir(), - ); - - this.uploaddir = dir; - this.uploadDir = dir; - - // initialize with null - [ - 'error', - 'headers', - 'type', - 'bytesExpected', - 'bytesReceived', - '_parser', - ].forEach((key) => { - this[key] = null; - }); - - this._setUpRename(); - - this._flushing = 0; - this._fieldsSize = 0; - this._fileSize = 0; - this._plugins = []; - this.openedFiles = []; - - this.options.enabledPlugins = [] - .concat(this.options.enabledPlugins) - .filter(Boolean); - - if (this.options.enabledPlugins.length === 0) { - throw new FormidableError( - 'expect at least 1 enabled builtin plugin, see options.enabledPlugins', - errors.missingPlugin, - ); - } - - this.options.enabledPlugins.forEach((pluginName) => { - const plgName = pluginName.toLowerCase(); - // eslint-disable-next-line import/no-dynamic-require, global-require - this.use(require(path.join(__dirname, 'plugins', `${plgName}.js`))); - }); - - this._setUpMaxFields(); - } - - use(plugin) { - if (typeof plugin !== 'function') { - throw new FormidableError( - '.use: expect `plugin` to be a function', - errors.pluginFunction, - ); - } - this._plugins.push(plugin.bind(this)); - return this; - } - - parse(req, cb) { - this.pause = () => { - try { - req.pause(); - } catch (err) { - // the stream was destroyed - if (!this.ended) { - // before it was completed, crash & burn - this._error(err); - } - return false; - } - return true; - }; - - this.resume = () => { - try { - req.resume(); - } catch (err) { - // the stream was destroyed - if (!this.ended) { - // before it was completed, crash & burn - this._error(err); - } - return false; - } - - return true; - }; - - // Setup callback first, so we don't miss anything from data events emitted immediately. - if (cb) { - const callback = once(dezalgo(cb)); - const fields = {}; - let mockFields = ''; - const files = {}; - - this.on('field', (name, value) => { - if ( - this.options.multiples && - (this.type === 'multipart' || this.type === 'urlencoded') - ) { - const mObj = { [name]: value }; - mockFields = mockFields - ? `${mockFields}&${qs.stringify(mObj)}` - : `${qs.stringify(mObj)}`; - } else { - fields[name] = value; - } - }); - this.on('file', (name, file) => { - // TODO: too much nesting - if (this.options.multiples) { - if (hasOwnProp(files, name)) { - if (!Array.isArray(files[name])) { - files[name] = [files[name]]; - } - files[name].push(file); - } else { - files[name] = file; - } - } else { - files[name] = file; - } - }); - this.on('error', (err) => { - callback(err, fields, files); - }); - this.on('end', () => { - if (this.options.multiples) { - Object.assign(fields, qs.parse(mockFields)); - } - callback(null, fields, files); - }); - } - - // Parse headers and setup the parser, ready to start listening for data. - this.writeHeaders(req.headers); - - // Start listening for data. - req - .on('error', (err) => { - this._error(err); - }) - .on('aborted', () => { - this.emit('aborted'); - this._error(new FormidableError('Request aborted', errors.aborted)); - }) - .on('data', (buffer) => { - try { - this.write(buffer); - } catch (err) { - this._error(err); - } - }) - .on('end', () => { - if (this.error) { - return; - } - if (this._parser) { - this._parser.end(); - } - this._maybeEnd(); - }); - - return this; - } - - writeHeaders(headers) { - this.headers = headers; - this._parseContentLength(); - this._parseContentType(); - - if (!this._parser) { - this._error( - new FormidableError( - 'no parser found', - errors.noParser, - 415, // Unsupported Media Type - ), - ); - return; - } - - this._parser.once('error', (error) => { - this._error(error); - }); - } - - write(buffer) { - if (this.error) { - return null; - } - if (!this._parser) { - this._error( - new FormidableError('uninitialized parser', errors.uninitializedParser), - ); - return null; - } - - this.bytesReceived += buffer.length; - this.emit('progress', this.bytesReceived, this.bytesExpected); - - this._parser.write(buffer); - - return this.bytesReceived; - } - - pause() { - // this does nothing, unless overwritten in IncomingForm.parse - return false; - } - - resume() { - // this does nothing, unless overwritten in IncomingForm.parse - return false; - } - - onPart(part) { - // this method can be overwritten by the user - this._handlePart(part); - } - - _handlePart(part) { - if (part.originalFilename && typeof part.originalFilename !== 'string') { - this._error( - new FormidableError( - `the part.originalFilename should be string when it exists`, - errors.filenameNotString, - ), - ); - return; - } - - // This MUST check exactly for undefined. You can not change it to !part.originalFilename. - - // todo: uncomment when switch tests to Jest - // console.log(part); - - // ? NOTE(@tunnckocore): no it can be any falsey value, it most probably depends on what's returned - // from somewhere else. Where recently I changed the return statements - // and such thing because code style - // ? NOTE(@tunnckocore): or even better, if there is no mimetype, then it's for sure a field - // ? NOTE(@tunnckocore): originalFilename is an empty string when a field? - if (!part.mimetype) { - let value = ''; - const decoder = new StringDecoder( - part.transferEncoding || this.options.encoding, - ); - - part.on('data', (buffer) => { - this._fieldsSize += buffer.length; - if (this._fieldsSize > this.options.maxFieldsSize) { - this._error( - new FormidableError( - `options.maxFieldsSize (${this.options.maxFieldsSize} bytes) exceeded, received ${this._fieldsSize} bytes of field data`, - errors.maxFieldsSizeExceeded, - 413, // Payload Too Large - ), - ); - return; - } - value += decoder.write(buffer); - }); - - part.on('end', () => { - this.emit('field', part.name, value); - }); - return; - } - - if (!this.options.filter(part)) { - return; - } - - this._flushing += 1; - - const newFilename = this._getNewName(part); - const filepath = this._joinDirectoryName(newFilename); - const file = this._newFile({ - newFilename, - filepath, - originalFilename: part.originalFilename, - mimetype: part.mimetype, - }); - file.on('error', (err) => { - this._error(err); - }); - this.emit('fileBegin', part.name, file); - - file.open(); - this.openedFiles.push(file); - - part.on('data', (buffer) => { - this._fileSize += buffer.length; - if (this._fileSize < this.options.minFileSize) { - this._error( - new FormidableError( - `options.minFileSize (${this.options.minFileSize} bytes) inferior, received ${this._fileSize} bytes of file data`, - errors.smallerThanMinFileSize, - 400, - ), - ); - return; - } - if (this._fileSize > this.options.maxFileSize) { - this._error( - new FormidableError( - `options.maxFileSize (${this.options.maxFileSize} bytes) exceeded, received ${this._fileSize} bytes of file data`, - errors.biggerThanMaxFileSize, - 413, - ), - ); - return; - } - if (buffer.length === 0) { - return; - } - this.pause(); - file.write(buffer, () => { - this.resume(); - }); - }); - - part.on('end', () => { - if (!this.options.allowEmptyFiles && this._fileSize === 0) { - this._error( - new FormidableError( - `options.allowEmptyFiles is false, file size should be greather than 0`, - errors.noEmptyFiles, - 400, - ), - ); - return; - } - - file.end(() => { - this._flushing -= 1; - this.emit('file', part.name, file); - this._maybeEnd(); - }); - }); - } - - // eslint-disable-next-line max-statements - _parseContentType() { - if (this.bytesExpected === 0) { - this._parser = new DummyParser(this, this.options); - return; - } - - if (!this.headers['content-type']) { - this._error( - new FormidableError( - 'bad content-type header, no content-type', - errors.missingContentType, - 400, - ), - ); - return; - } - - const results = []; - const _dummyParser = new DummyParser(this, this.options); - - // eslint-disable-next-line no-plusplus - for (let idx = 0; idx < this._plugins.length; idx++) { - const plugin = this._plugins[idx]; - - let pluginReturn = null; - - try { - pluginReturn = plugin(this, this.options) || this; - } catch (err) { - // directly throw from the `form.parse` method; - // there is no other better way, except a handle through options - const error = new FormidableError( - `plugin on index ${idx} failed with: ${err.message}`, - errors.pluginFailed, - 500, - ); - error.idx = idx; - throw error; - } - - Object.assign(this, pluginReturn); - - // todo: use Set/Map and pass plugin name instead of the `idx` index - this.emit('plugin', idx, pluginReturn); - results.push(pluginReturn); - } - - this.emit('pluginsResults', results); - - // NOTE: probably not needed, because we check options.enabledPlugins in the constructor - // if (results.length === 0 /* && results.length !== this._plugins.length */) { - // this._error( - // new Error( - // `bad content-type header, unknown content-type: ${this.headers['content-type']}`, - // ), - // ); - // } - } - - _error(err, eventName = 'error') { - // if (!err && this.error) { - // this.emit('error', this.error); - // return; - // } - if (this.error || this.ended) { - return; - } - - this.error = err; - this.emit(eventName, err); - - if (Array.isArray(this.openedFiles)) { - this.openedFiles.forEach((file) => { - file.destroy(); - }); - } - } - - _parseContentLength() { - this.bytesReceived = 0; - if (this.headers['content-length']) { - this.bytesExpected = parseInt(this.headers['content-length'], 10); - } else if (this.headers['transfer-encoding'] === undefined) { - this.bytesExpected = 0; - } - - if (this.bytesExpected !== null) { - this.emit('progress', this.bytesReceived, this.bytesExpected); - } - } - - _newParser() { - return new MultipartParser(this.options); - } - - _newFile({ filepath, originalFilename, mimetype, newFilename }) { - return this.options.fileWriteStreamHandler - ? new VolatileFile({ - newFilename, - filepath, - originalFilename, - mimetype, - createFileWriteStream: this.options.fileWriteStreamHandler, - hashAlgorithm: this.options.hashAlgorithm, - }) - : new PersistentFile({ - newFilename, - filepath, - originalFilename, - mimetype, - hashAlgorithm: this.options.hashAlgorithm, - }); - } - - _getFileName(headerValue) { - // matches either a quoted-string or a token (RFC 2616 section 19.5.1) - const m = headerValue.match( - /\bfilename=("(.*?)"|([^()<>{}[\]@,;:"?=\s/\t]+))($|;\s)/i, - ); - if (!m) return null; - - const match = m[2] || m[3] || ''; - let originalFilename = match.substr(match.lastIndexOf('\\') + 1); - originalFilename = originalFilename.replace(/%22/g, '"'); - originalFilename = originalFilename.replace(/&#([\d]{4});/g, (_, code) => - String.fromCharCode(code), - ); - - return originalFilename; - } - - _getExtension(str) { - if (!str) { - return ''; - } - - const basename = path.basename(str); - const firstDot = basename.indexOf('.'); - const lastDot = basename.lastIndexOf('.'); - const extname = path.extname(basename).replace(/(\.[a-z0-9]+).*/i, '$1'); - - if (firstDot === lastDot) { - return extname; - } - - return basename.slice(firstDot, lastDot) + extname; - } - - - - _joinDirectoryName(name) { - const newPath = path.join(this.uploadDir, name); - - // prevent directory traversal attacks - if (!newPath.startsWith(this.uploadDir)) { - return path.join(this.uploadDir, this.options.defaultInvalidName); - } - - return newPath; - } - - _setUpRename() { - const hasRename = typeof this.options.filename === 'function'; - if (hasRename) { - this._getNewName = (part) => { - let ext = ''; - let name = this.options.defaultInvalidName; - if (part.originalFilename) { - // can be null - ({ ext, name } = path.parse(part.originalFilename)); - if (this.options.keepExtensions !== true) { - ext = ''; - } - } - return this.options.filename.call(this, name, ext, part, this); - }; - } else { - this._getNewName = (part) => { - const name = toHexoId(); - - if (part && this.options.keepExtensions) { - const originalFilename = typeof part === 'string' ? part : part.originalFilename; - return `${name}${this._getExtension(originalFilename)}`; - } - - return name; - } - } - } - - _setUpMaxFields() { - if (this.options.maxFields !== 0) { - let fieldsCount = 0; - this.on('field', () => { - fieldsCount += 1; - if (fieldsCount > this.options.maxFields) { - this._error( - new FormidableError( - `options.maxFields (${this.options.maxFields}) exceeded`, - errors.maxFieldsExceeded, - 413, - ), - ); - } - }); - } - } - - _maybeEnd() { - // console.log('ended', this.ended); - // console.log('_flushing', this._flushing); - // console.log('error', this.error); - if (!this.ended || this._flushing || this.error) { - return; - } - - this.emit('end'); - } -} - -IncomingForm.DEFAULT_OPTIONS = DEFAULT_OPTIONS; -module.exports = IncomingForm; |