Source: host/hostLib/PortData.js

const Util = require('../../utility/Util');
const Script = require('./Script');

/**
  * Processes and stores the values and meta data of a given port data structure
  * @param {Object} options - Error, warning and debug function references
  * @param {Object} portStruct - PortData node to process
  */
class PortData {
  constructor(options, portStruct) {
    /**
      * Merge default options with given options
      * @type {Object}
      */
    this.options = Util.mergeDefault({
      onError: () => {}, // @type {Function}
      onWarning: () => {}, // @type {Function}
      onDebug: () => {}, // @type {Function}
    }, options);

    /**
      * Port protocol property default
      * @type {String}
      */
    this.protocol = '';

    /**
      * Port ID property default
      * @type {Number}
      */
    this.portid = -1;

    /**
      * Open / close state property default
      * @type {String}
      */
    this.openState = '';

    /**
      * Reason property default
      * @type {String}
      */
    this.reason = '';

    /**
      * Reason time to live property default
      * @type {Number}
      */
    this.reason_ttl = -1;

    /**
      * Port name property default
      * @type {String}
      */
    this.name = '';

    /**
      * Product name property default
      * @type {String}
      */
    this.product = '';

    /**
      * Version property default
      * @type {String}
      */
    this.version = '';

    /**
      * Extra port info property default
      * @type {String}
      */
    this.extrainfo = '';

    /**
      * OS Type property default
      * @type {String}
      */
    this.ostype = '';

    /**
      * Service FP property default
      * @type {String}
      */
    this.servicefp = '';

    /**
      * Connection method property default
      * @type {String}
      */
    this.method = '';

    /**
      * Conf property default
      * @type {Number}
      */
    this.conf = -1;

    /**
      * CPE property default
      * @type {Array}
      */
    this.cpe = [];

    /**
      * Ports used property default
      * @type {Array}
      */
    this.portsUsed = [];

    /**
      * Port scripts property default
      * @type {Array}
      */
    this.scripts = [];

    // Attempt parsing or fail with error
    try {
      this.parseStruct(portStruct);
    } catch (err) {
      this.options.onError(`${this.constructor.name} Class Error:
        Error parsing port data: ${err}`);
    }
  }

  /**
    * Recursive parsing function, sets the value of this classes properties (if
    * it exists), or calls the proper processing function, or builds required
    * child classes for further processing
    * @param {Object} curStruct - Current node being processed
    */
  parseStruct(curStruct) {
    const props = Object.keys(curStruct);

    for (let i = 0, j = props.length; i < j; i += 1) {
      if (typeof this[props[i]] === 'undefined') {
        this.options.onDebug(`${this.constructor.name} Class:
          Unknown property in structure: ${props[i]}`);
      } else {
        this[props[i]] = curStruct[props[i]];
      }
    }
  }

  /**
    * Provides default `attrib` property to ensure node processing, always returns
    * current meta values
    * @type {Object}
    * @readonly
    */
  get attrib() {
    return this.data;
  }

  /**
    * Continues node walking recursion through the current attrib node
    * @param {Object} meta - Current `attrib` node
    */
  set attrib(meta) {
    this.parseStruct(meta);
  }

  /**
    * Returns all processed data, including child data, of the this class
    * @type {Object}
    * @readonly
    */
  get data() {
    return {
      protocol: this.protocol, // @type {String}
      portid: this.number, // @type {Number}
      state: this.openState, // @type {String}
      reason: this.reason, // @type {String}
      reason_ttl: this.reason_ttl, // @type {Number}
      name: this.name, // @type {String}
      product: this.product, // @type {String}
      version: this.version, // @type {String}
      extrainfo: this.extrainfo, // @type {String}
      ostype: this.ostype, // @type {String}
      servicefp: this.servicefp, // @type {String}
      method: this.method, // @type {String}
      conf: this.conf, // @type {Number}
      cpe: this.cpe, // @type {Array}
      scripts: this.scripts.map((s) => s.data || []), // @type {Array}
    };
  }

  /**
    * Provides default `openState` property to ensure node processing, always returns
    * current openState values
    * @type {Object}
    * @readonly
    */
  get state() {
    return this.openState;
  }

  /**
    * Sets state based on type of node data
    * @param {Object||String} stateData - Current `state` node
    */
  set state(stateData) {
    if (typeof stateData.attrib !== 'undefined') {
      this.parseStruct(stateData.attrib);
    } else if (typeof stateData === 'string') {
      this.openState = stateData;
    }
  }

  /**
    * Provides default `service` property to ensure node processing, always returns
    * current data values
    * @type {Object}
    * @readonly
    */
  get service() {
    return this.data;
  }

  /**
    * Sets service details, continues node processing
    * @param {Object} serviceStruct - Current `service` node
    */
  set service(serviceStruct) {
    this.parseStruct(serviceStruct);
  }

  /**
    * Gets the scripts used to test this port
    * @type {Array}
    * @readonly
    */
  get script() {
    return this.scripts;
  }

  /**
    * Sets script details, creating script children as needed
    * @param {Object||Array} scriptStruct - Current `script` node
    */
  set script(scriptStruct) {
    if (Array.isArray(scriptStruct)) {
      for (let i = 0, j = scriptStruct.length; i < j; i += 1) {
        this.scripts.push(new Script(this.options, scriptStruct[i]));
      }
    } else {
      this.scripts.push(new Script(this.options, scriptStruct));
    }
  }

  /**
    * Gets this port's number
    * @type {Number}
    * @readonly
    */
  get number() {
    return this.portid;
  }
}

module.exports = PortData;