'use strict';

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var _toArray = _interopDefault(require('@babel/runtime/helpers/toArray'));
var _slicedToArray = _interopDefault(require('@babel/runtime/helpers/slicedToArray'));
var _classCallCheck = _interopDefault(require('@babel/runtime/helpers/classCallCheck'));
var _createClass = _interopDefault(require('@babel/runtime/helpers/createClass'));
var _defineProperty = _interopDefault(require('@babel/runtime/helpers/defineProperty'));
var nodeUtils = require('@forensic-js/node-utils');
var fs = require('fs');
var utils = require('@forensic-js/utils');
var Constants = require('./Constants');
var uuidv1 = _interopDefault(require('uuid/v1'));

var _default =
/*#__PURE__*/
function () {
  function _default(config) {
    _classCallCheck(this, _default);

    _defineProperty(this, "config", void 0);

    this.config = config; //create file storage dir

    nodeUtils.mkDirSync(nodeUtils.resolvePaths(this.config.entryPath, this.config.tempDir));
  }
  /**
   * resolves the field name by removing trailing bracket
   */


  _createClass(_default, [{
    key: "resolveFieldName",
    value: function resolveFieldName(fieldName) {
      if (/^(.+)\[\]$/.test(fieldName)) {
        return {
          name: RegExp.$1,
          isMultiValue: true
        };
      } else {
        return {
          name: fieldName,
          isMultiValue: false
        };
      }
    }
    /**
     * assigns a single field or multi value field value to the body
     */

  }, {
    key: "assignBodyValue",
    value: function assignBodyValue(body, fieldName, value) {
      var _this$resolveFieldNam = this.resolveFieldName(fieldName),
          name = _this$resolveFieldNam.name,
          isMultiValue = _this$resolveFieldNam.isMultiValue;

      var target = value;

      if (isMultiValue) {
        var previous = body[name];
        target = utils.isArray(previous) ? previous : [];
        target.push(value);
      }

      body[name] = target;
    }
    /**
     * stores a file into the given files object
     */

  }, {
    key: "assignFileValue",
    value: function assignFileValue(files, fieldName, value) {
      var _this$resolveFieldNam2 = this.resolveFieldName(fieldName),
          name = _this$resolveFieldNam2.name,
          isMultiValue = _this$resolveFieldNam2.isMultiValue;

      var target = value;

      if (isMultiValue) {
        var previous = files[name];
        target = utils.isObject(previous) ? previous : {
          path: [],
          key: [],
          size: [],
          type: [],
          name: []
        };

        for (var _i = 0, _Object$entries = Object.entries(value); _i < _Object$entries.length; _i++) {
          var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
              key = _Object$entries$_i[0],
              current = _Object$entries$_i[1];

          target[key].push(current);
        }
      }

      files[name] = target;
    }
    /**
     * processes and stores file
     */

  }, {
    key: "processFile",
    value: function processFile(headers, content) {
      var key = uuidv1() + '.tmp';
      var filePath = nodeUtils.resolvePaths(this.config.entryPath, this.config.tempDir, key);
      fs.writeFileSync(filePath, content, headers.encoding);
      return {
        name: headers.fileName.replace(/\.\./g, ''),
        key: key,
        path: filePath,
        size: fs.statSync(filePath).size,
        type: headers.type
      };
    }
    /**
     * parses a multipart part headers.
     */

  }, {
    key: "parseHeaders",
    value: function parseHeaders(headers) {
      //assume a default value if there are no headers sent
      var result = {
        isFile: false,
        type: 'text/plain',
        fileName: '',
        fieldName: utils.generateRandomText(8),
        encoding: this.config.encoding
      };
      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = headers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var header = _step.value;

          if (header !== '') {
            var _header$split = header.split(/\s*:\s*/),
                _header$split2 = _slicedToArray(_header$split, 2),
                name = _header$split2[0],
                value = _header$split2[1];

            switch (name.toLowerCase()) {
              case 'content-disposition':
                //capture file name
                if (/filename="([^"]+)"/.exec(value)) {
                  result.fileName = RegExp.$1;
                } // capture field name


                value = value.replace(/filename="([^"]+)"/, '');
                /* istanbul ignore else */

                if (/name="([^"]+)"/.exec(value)) {
                  result.fieldName = RegExp.$1;
                }

                break;

              case 'content-type':
                result.isFile = true;
                result.type = value.split(/;\s*/)[0];
                break;
            }
          }
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return != null) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }

      return result;
    }
    /**
     * parse multipart form data
     *@see https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
     */

  }, {
    key: "parseMultiPart",
    value: function parseMultiPart(string, boundary) {
      var _this = this;

      var body = {},
          files = {}; //if boundary is null, detect it using the last encapsulation boundary format

      if (utils.isNull(boundary)) {
        if (!/^-{2}(-*[a-z0-9]+)-{2}/gim.test(string)) {
          return {
            body: body,
            files: files
          };
        }

        boundary = RegExp.$1;
      } //some implementations fails to start the first encapsulation boundary
      // with CRLF character when there is no preamble


      var testPattern = new RegExp("^--".concat(boundary), 'gm');
      /* istanbul ignore else */

      if (testPattern.test(string)) {
        string = Constants.CRLF + string;
      }

      var crlfPattern = new RegExp(Constants.CRLF, 'gm');
      var boundaryLinePattern = new RegExp("".concat(Constants.CRLF, "--").concat(boundary), 'gm');
      var blankLinePattern = new RegExp(Constants.BLANK_LINE, 'gm'); //obtain body parts, discard preamble and epilogue

      string.split(boundaryLinePattern).slice(1, -1).forEach(function (bodyPart) {
        var _bodyPart$split = bodyPart.split(blankLinePattern),
            _bodyPart$split2 = _toArray(_bodyPart$split),
            header = _bodyPart$split2[0],
            others = _bodyPart$split2.slice(1);

        var content = others.join(Constants.BLANK_LINE);

        var headers = _this.parseHeaders(header.split(crlfPattern));
        /* istanbul ignore else */


        if (headers.isFile && headers.fileName) {
          _this.assignFileValue(files, headers.fieldName, _this.processFile(headers, content));
        } else if (!headers.isFile) {
          _this.assignBodyValue(body, headers.fieldName, content);
        }
      });
      return {
        body: body,
        files: files
      };
    }
    /**
     * parse json content
     */

  }, {
    key: "parseJSON",
    value: function parseJSON(string) {
      try {
        var result = JSON.parse(string);
        return result;
      } catch (ex) {
        return {};
      }
    }
    /**
     * parse url encoded request body
     */

  }, {
    key: "parseUrlEncoded",
    value: function parseUrlEncoded(string) {
      var body = {};

      if (string) {
        var pairs = string.split('&');
        var _iteratorNormalCompletion2 = true;
        var _didIteratorError2 = false;
        var _iteratorError2 = undefined;

        try {
          for (var _iterator2 = pairs[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
            var pair = _step2.value;

            var _pair$split = pair.split('='),
                _pair$split2 = _slicedToArray(_pair$split, 2),
                name = _pair$split2[0],
                value = _pair$split2[1];

            this.assignBodyValue(body, decodeURIComponent(name), decodeURIComponent(value || ''));
          }
        } catch (err) {
          _didIteratorError2 = true;
          _iteratorError2 = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
              _iterator2.return();
            }
          } finally {
            if (_didIteratorError2) {
              throw _iteratorError2;
            }
          }
        }
      }

      return body;
    }
    /**
     * parse the query parameters in the given url
     */

  }, {
    key: "parseQueryString",
    value: function parseQueryString(url) {
      if (url.indexOf('?') > -1) {
        return this.parseUrlEncoded(url.split('?')[1]);
      } else {
        return {};
      }
    }
    /**
     *@param {Buffer} buffer - the buffer data
     *@param {string} contentType - the request content type
     */

  }, {
    key: "parse",
    value: function parse(buffer, contentType) {
      var content = buffer.toString(this.config.encoding);
      var tokens = contentType.split(/;\s*/);
      var boundary = null;

      switch (tokens[0].toLowerCase()) {
        case 'text/plain':
        case 'application/x-www-form-urlencoded':
          return {
            files: {},
            body: this.parseUrlEncoded(content)
          };

        case 'text/json':
        case 'application/json':
          return {
            files: {},
            body: this.parseJSON(content)
          };

        case 'multipart/form-data':
          if (tokens.length === 2 && /boundary\s*=\s*/.test(tokens[1])) {
            boundary = tokens[1].split('=')[1];
          }

          return this.parseMultiPart(content, boundary);

        default:
          return {
            body: {},
            files: {}
          };
      }
    }
    /**
     * clean up temp files
     */

  }, {
    key: "cleanUpTempFiles",
    value: function cleanUpTempFiles(files) {
      var unlink = function unlink(path) {
        if (fs.existsSync(path)) {
          fs.unlinkSync(path);
        }
      };

      for (var _i2 = 0, _Object$entries2 = Object.entries(files); _i2 < _Object$entries2.length; _i2++) {
        var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
            file = _Object$entries2$_i[1];

        utils.makeArray(file.path).forEach(unlink);
      }
    }
  }]);

  return _default;
}();

module.exports = _default;
//# sourceMappingURL=BodyParser.js.map
