module.exports = function (req, res, opts, cb) {
  var form = new Form();
  var fields = {};
  var files = {};
  var result = {
    fields: fields,
    files: files
  };
  var counter = 1;
  form.parse(req);

  form.on("field", function (key, value) {
    // BUG: do not handle case where name appears multiple
    // times in a form.
    fields[key] = value;
    console.log('on field', fields, value);
  });

  form.on("part", function (part) {
    if (part.filename === null) {
      return;
    }

    counter++;

    opts.handlePart.call(result, part, function (err, result) {
      if (err) {
        return cb(err);
      }

      files[part.name] = result;
      finish();
    });
  });

  function finish() {
    if (--counter === 0) {
      cb(null, {fields: fields, files: files});
    }
  }

  form.once("error", cb);
  form.once("close", finish);
};
Example #2
0
internals.multipart = function (request, source, next) {

    var options = request.route.payload;
    next = Utils.once(next);                                            // Modify next() for async events

    var form = new Multiparty.Form({
        maxFields: Infinity,
        maxFieldsSize: Infinity,
        uploadDir: options.uploads
    });

    form.once('error', function (err) {

        return next(Boom.badRequest('Invalid multipart payload format'));
    });

    var arrayFields = false;
    var finalize = function (data) {

        if (arrayFields) {
            data = Qs.parse(data);
        }

        request.payload = data;
        return next();
    };

    var data = {};
    var set = function (name, value) {

        arrayFields = arrayFields || (name.indexOf('[') !== -1);

        if (!data.hasOwnProperty(name)) {
            data[name] = value;
        }
        else if (Array.isArray(data[name])) {
            data[name].push(value);
        }
        else {
            data[name] = [data[name], value];
        }
    };

    var pendingFiles = {};
    var nextId = 0;
    var closed = false;

    form.on('part', function (part) {

        if (!part.filename) {
            return;
        }

        if (options.output === 'stream') {                                              // Output: 'stream'
            set(part.name, part);
        }
        else if (options.output === 'data') {                                           // Output: 'data'
            Nipple.read(part, {}, function (err, payload) {

                // err handled by form.once('error')

                var contentType = (part.headers && part.headers['content-type']) || '';
                var mime = contentType.split(';')[0].trim().toLowerCase();

                if (!mime) {
                    return set(part.name, payload);
                }

                if (!payload.length) {
                    return set(part.name, {});
                }

                internals.object(payload, mime, function (err, result) {

                    return set(part.name, err ? payload : result);
                });
            });
        }
        else {                                                                          // Output: 'file'
            var id = nextId++;
            pendingFiles[id] = true;
            internals.writeFile(part, options.uploads, function (err, path, bytes) {

                delete pendingFiles[id];

                if (err) {
                    return next(err);
                }

                var item = {
                    filename: part.filename,
                    path: path,
                    headers: part.headers,
                    bytes: bytes
                };

                set(part.name, item);

                if (closed) {
                    return finalize(data);
                }
            });
        }
    });

    form.on('field', function (name, value) {

        set(name, value);
    });

    form.once('close', function () {

        form.removeAllListeners();
        if (Object.keys(pendingFiles).length) {
            closed = true;
            return;
        }

        return finalize(data);
    });

    source.headers = source.headers || request.headers;
    form.parse(source);
};
Example #3
0
internals.parse = function (payload, mime, headers, options, callback) {

    callback = Utils.nextTick(callback);

    // Binary

    if (mime === 'application/octet-stream') {
        return callback(null, payload);
    }

    // Text

    if (mime.match(/^text\/.+$/)) {
        return callback(null, payload.toString('utf-8'));
    }

    // JSON

    if (mime === 'application/json') {
        return internals.jsonParse(payload, callback);                      // Isolate try...catch for V8 optimization
    }

    // Form-encoded

    if (mime === 'application/x-www-form-urlencoded') {
        return callback(null, Querystring.parse(payload.toString('utf-8')));
    }

    // Multipart

    if (mime === 'multipart/form-data' &&
        options.multipart) {

        var stream = new internals.Replay(headers, payload);

        var form = new Multiparty.Form({
            encoding: options.multipart.encoding,
            maxFieldsSize: options.multipart.maxFieldBytes,
            maxFields: options.multipart.maxFields,
            uploadDir: options.multipart.uploadDir,
            hash: options.multipart.hash
        });

        var data = {};

        form.once('error', function (err) {

            return callback(Boom.badRequest('Invalid request multipart payload format'));
        });

        var set = function (name, value) {

            if (!data.hasOwnProperty(name)) {
                data[name] = value;
            }
            else if (Array.isArray(data[name])) {
                data[name].push(value);
            }
            else {
                data[name] = [data[name], value];
            }
        };

        if (options.multipart.mode === 'stream') {
            form.on('part', function (part) {

                if (!part.filename) {
                    return;
                }

                part.type = part.headers && part.headers['content-type'];
                set(part.name, part);
            });
        }
        else {
            form.on('file', function (name, file) {

                var item = {
                    type: file.headers && file.headers['content-type'],
                    fieldName: file.fieldName,
                    originalFilename: file.originalFilename,
                    path: file.path,
                    headers: file.headers,
                    size: file.size
                };

                set(name, item);
            });
        }

        form.on('field', function (name, value) {

            set(name, value);
        });

        form.once('close', function () {

            stream.removeAllListeners();
            form.removeAllListeners();
            return callback(null, data);
        });

        form.parse(stream);

        return;
    }

    return callback(new Boom.unsupportedMediaType());
};
Example #4
0
internals.parse = function (payload, mime, headers, callback) {

    callback = Utils.nextTick(callback);

    // Binary

    if (mime === 'application/octet-stream') {
        return callback(null, payload);
    }

    // Text

    if (mime.match(/^text\/.+$/)) {
        return callback(null, payload.toString('utf-8'));
    }

    // JSON

    if (mime === 'application/json') {
        var obj = null;
        var error = null;

        try {
            obj = JSON.parse(payload.toString('utf-8'));
        }
        catch (exp) {
            error = Boom.badRequest('Invalid request payload format');
        }

        return callback(error, obj);
    }

    // Form-encoded

    if (mime === 'application/x-www-form-urlencoded') {
        return callback(null, Querystring.parse(payload.toString('utf-8')));
    }

    // Multipart

    if (mime === 'multipart/form-data') {

        var stream = new internals.Replay(headers, payload);

        var form = new Multiparty.Form();
        var data = {};

        form.once('error', function (err) {

            return callback(Boom.badRequest('Invalid request multipart payload format'));
        });

        var set = function (name, value) {

            if (!data.hasOwnProperty(name)) {
                data[name] = value;
            }
            else if (data[name] instanceof Array) {
                data[name].push(value);
            }
            else {
                data[name] = [data[name], value];
            }
        };

        form.on('file', function (name, value) {

            value.name = value.originalFilename;                            // For backwards compatibility with formidable
            value.type = value.headers && value.headers['content-type'];
            delete value.ws;
            set(name, value);
        });

        form.on('field', function (name, value) {

            set(name, value);
        });

        form.once('close', function () {

            stream.removeAllListeners();
            form.removeAllListeners();
            return callback(null, data);
        });

        form.parse(stream);

        return;
    }

    return callback(new Boom(415));     // Unsupported Media Type
};