<commit_before>var net    = require('net')
  , Socket = net.Socket
  , async  = require('async')

var portscanner = exports

/**
 * Finds the first port with a status of 'open', implying the port is in use and
 * there is likely a service listening on it.
 *
 * @param {Number} startPort  - Port to begin status check on (inclusive).
 * @param {Number} endPort    - Last port to check status on (inclusive).
 *                              Defaults to 65535.
 * @param {String} host       - Where to scan. Defaults to '127.0.0.1'.
 * @param {Function} callback - function (error, port) { ... }
 *   - {Object|null} error    - Any errors that occurred while port scanning.
 *   - {Number|Boolean} port  - The first open port found. Note, this is the
 *                              first port that returns status as 'open', not
 *                              necessarily the first open port checked. If no
 *                              open port is found, the value is false.
 */
portscanner.findAPortInUse = function(startPort, endPort, host, callback) {
  findAPortWithStatus('open', startPort, endPort, host, callback)
}

/**
 * Finds the first port with a status of 'closed', implying the port is not in
 * use.
 *
 * @param {Number} startPort  - Port to begin status check on (inclusive).
 * @param {Number} endPort    - Last port to check status on (inclusive).
 *                              Defaults to 65535.
 * @param {String} host       - Where to scan. Defaults to '127.0.0.1'.
 * @param {Function} callback - function (error, port) { ... }
 *   - {Object|null} error    - Any errors that occurred while port scanning.
 *   - {Number|Boolean} port  - The first closed port found. Note, this is the
 *                              first port that returns status as 'closed', not
 *                              necessarily the first closed port checked. If no
 *                              closed port is found, the value is false.
 */
portscanner.findAPortNotInUse = function(startPort, endPort, host, callback) {
  findAPortWithStatus('closed', startPort, endPort, host, callback)
}

/**
 * Checks the status of an individual port.
 *
 * @param {Number} port           - Port to check status on.
 * @param {String|Object} options - host or options
 *   - {String} host              - Host of where to scan. Defaults to '127.0.0.1'.
 *   - {Object} options
 *     - {String} host            - Host of where to scan. Defaults to '127.0.0.1'.
 *     - {Number} timeout         - Connection timeout. Defaults to 400ms.
 * @param {Function} callback     - function (error, port) { ... }
 *   - {Object|null} error        - Any errors that occurred while port scanning.
 *   - {String} status            - 'open' if the port is in use.
 *                                  'closed' if the port is available.
 */
portscanner.checkPortStatus = function(port, options, callback) {
  if (typeof options === 'string') {
    // Assume this param is the host option
    options = {host: options}
  }

  var host = options.host || '127.0.0.1'
  var timeout = options.timeout || 400

  var socket = new Socket()
    , status = null
    , error = null

  // Socket connection established, port is open
  socket.on('connect', function() {
    status = 'open'
    socket.destroy()
  })

  // If no response, assume port is not listening
  socket.setTimeout(timeout)
  socket.on('timeout', function() {
    status = 'closed'
    error = new Error('Timeout (' + timeout + 'ms) occurred waiting for ' + host + ':' + port + ' to be available')
    socket.destroy()
  })

  // Assuming the port is not open if an error. May need to refine based on
  // exception
  socket.on('error', function(exception) {
    error = exception
    status = 'closed'
  })

  // Return after the socket has closed
  socket.on('close', function() {
    callback(error, status)
  })

  socket.connect(port, host)
}

function findAPortWithStatus(status, startPort, endPort, host, callback) {
  endPort = endPort || 65535
  var foundPort = false
  var numberOfPortsChecked = 0
  var port = startPort

  // Returns true if a port with matching status has been found or if checked
  // the entire range of ports
  var hasFoundPort = function() {
    return foundPort || numberOfPortsChecked === (endPort - startPort + 1)
  }

  // Checks the status of the port
  var checkNextPort = function(callback) {
    portscanner.checkPortStatus(port, host, function(error, statusOfPort) {
      numberOfPortsChecked++
      if (statusOfPort === status) {
        foundPort = true
        callback(error)
      }
      else {
        port++
        callback(null)
      }
    })
  }

  // Check the status of each port until one with a matching status has been
  // found or the range of ports has been exhausted
  async.until(hasFoundPort, checkNextPort, function(error) {
    if (error) {
      callback(error, port)
    }
    else if (foundPort) {
      callback(null, port)
    }
    else {
      callback(null, false)
    }
  })
}

<commit_msg>Changed socket error handler to handle 'ECONNREFUSED'. In event of ECONNREFUSED the port is available<commit_after>var net    = require('net')
  , Socket = net.Socket
  , async  = require('async')

var portscanner = exports

/**
 * Finds the first port with a status of 'open', implying the port is in use and
 * there is likely a service listening on it.
 *
 * @param {Number} startPort  - Port to begin status check on (inclusive).
 * @param {Number} endPort    - Last port to check status on (inclusive).
 *                              Defaults to 65535.
 * @param {String} host       - Where to scan. Defaults to '127.0.0.1'.
 * @param {Function} callback - function (error, port) { ... }
 *   - {Object|null} error    - Any errors that occurred while port scanning.
 *   - {Number|Boolean} port  - The first open port found. Note, this is the
 *                              first port that returns status as 'open', not
 *                              necessarily the first open port checked. If no
 *                              open port is found, the value is false.
 */
portscanner.findAPortInUse = function(startPort, endPort, host, callback) {
  findAPortWithStatus('open', startPort, endPort, host, callback)
}

/**
 * Finds the first port with a status of 'closed', implying the port is not in
 * use.
 *
 * @param {Number} startPort  - Port to begin status check on (inclusive).
 * @param {Number} endPort    - Last port to check status on (inclusive).
 *                              Defaults to 65535.
 * @param {String} host       - Where to scan. Defaults to '127.0.0.1'.
 * @param {Function} callback - function (error, port) { ... }
 *   - {Object|null} error    - Any errors that occurred while port scanning.
 *   - {Number|Boolean} port  - The first closed port found. Note, this is the
 *                              first port that returns status as 'closed', not
 *                              necessarily the first closed port checked. If no
 *                              closed port is found, the value is false.
 */
portscanner.findAPortNotInUse = function(startPort, endPort, host, callback) {
  findAPortWithStatus('closed', startPort, endPort, host, callback)
}

/**
 * Checks the status of an individual port.
 *
 * @param {Number} port           - Port to check status on.
 * @param {String|Object} options - host or options
 *   - {String} host              - Host of where to scan. Defaults to '127.0.0.1'.
 *   - {Object} options
 *     - {String} host            - Host of where to scan. Defaults to '127.0.0.1'.
 *     - {Number} timeout         - Connection timeout. Defaults to 400ms.
 * @param {Function} callback     - function (error, port) { ... }
 *   - {Object|null} error        - Any errors that occurred while port scanning.
 *   - {String} status            - 'open' if the port is in use.
 *                                  'closed' if the port is available.
 */
portscanner.checkPortStatus = function(port, options, callback) {
  if (typeof options === 'string') {
    // Assume this param is the host option
    options = {host: options}
  }

  var host = options.host || '127.0.0.1'
  var timeout = options.timeout || 400
  var connectionRefused = false;

  var socket = new Socket()
    , status = null
    , error = null

  // Socket connection established, port is open
  socket.on('connect', function() {
    status = 'open'
    socket.destroy()
  })

  // If no response, assume port is not listening
  socket.setTimeout(timeout)
  socket.on('timeout', function() {
    status = 'closed'
    error = new Error('Timeout (' + timeout + 'ms) occurred waiting for ' + host + ':' + port + ' to be available')
    socket.destroy()
  })

  // Assuming the port is not open if an error. May need to refine based on
  // exception
  socket.on('error', function(exception) {
    if(exception.code !== "ECONNREFUSED") {
		error = exception
    }
	else
		connectionRefused = true;
	status = 'closed'
  })

  // Return after the socket has closed
  socket.on('close', function(exception) {
	if(exception && !connectionRefused)
		error = exception;
	else
		error = null;
    callback(error, status)
  })

  socket.connect(port, host)
}

function findAPortWithStatus(status, startPort, endPort, host, callback) {
  endPort = endPort || 65535
  var foundPort = false
  var numberOfPortsChecked = 0
  var port = startPort

  // Returns true if a port with matching status has been found or if checked
  // the entire range of ports
  var hasFoundPort = function() {
    return foundPort || numberOfPortsChecked === (endPort - startPort + 1)
  }

  // Checks the status of the port
  var checkNextPort = function(callback) {
    portscanner.checkPortStatus(port, host, function(error, statusOfPort) {
      numberOfPortsChecked++
      if (statusOfPort === status) {
        foundPort = true
        callback(error)
      }
      else {
        port++
        callback(null)
      }
    })
  }

  // Check the status of each port until one with a matching status has been
  // found or the range of ports has been exhausted
  async.until(hasFoundPort, checkNextPort, function(error) {
    if (error) {
      callback(error, port)
    }
    else if (foundPort) {
      callback(null, port)
    }
    else {
      callback(null, false)
    }
  })
}

<|endoftext|>


<commit_before>var async     = require("async");
var replace   = require("async-replace");
var Utils     = require("./utils");
var wordnet   = require("./wordnet");
var _         = require("underscore");
var Message   = require("./message");

var debug     = require("debug")("ProcessTags");
var dWarn     = require("debug")("ProcessTags:Warning");

var parseCustomFunctions = function(reply, plugins, scope, callback ) {
  // New Custom Function Handle
  // This matches the Redirect finder above
  var match = reply.match(/\^(\w+)\(([\w<>,\s]*)\)/);

  // We use async to capture multiple matches in the same reply
  return async.whilst(
    function () { return match; },
    function (cb) {
      // Call Function here

      var main = match[0];
      var pluginName = Utils.trim(match[1]);
      var partsStr = Utils.trim(match[2]);
      var parts = partsStr.split(",");

      var args  = [];
      for (var i = 0; i < parts.length; i++) {
        if (parts[i] !== "") {
          args.push(parts[i].trim());
        }
      }

      if (plugins[pluginName]) {

        // SubReply is the results of the object coming back
        // TODO. Subreply should be optional and could be undefined, or null
        args.push(function customFunctionHandle(err, subreply) {

          match = false;
          reply = reply.replace(main, subreply);
          match = reply.match(/\^(\w+)\(([~\w<>,\s]*)\)/);
          if (err) {
            cb(err);
          } else {
            cb();
          }
        });

        debug("Calling Plugin Function", pluginName);
        plugins[pluginName].apply(scope, args);

      } else {
        // If a function is missing, we kill the line and return empty handed
        dWarn("Custom Function not-found", pluginName);
        match = false;
        cb(null, "");
      }
    },
    function(err) {
      if (err) {
        callback(null, "");
      } else {
        callback(null, Utils.trim(reply));
      }
    }
  );
};

var processAlternates = function(reply) {
  // Reply Alternates.
  var match  = reply.match(/\((.+?)\)/);
  var giveup = 0;
  while (match) {
    debug("Reply has Alternates");

    giveup++;
    if (giveup >= 50) {
      dWarn("Infinite loop when trying to process optionals in trigger!");
      return "";
    }

    var parts = match[1].split("|");
    var opts  = [];
    for (var i = 0; i < parts.length; i++) {
      opts.push(parts[i].trim());
    }

    var resp = Utils.getRandomInt(0, opts.length - 1);
    reply = reply.replace(new RegExp("\\(" + Utils.quotemeta(match[1]) + "\\)\\s*"), opts[resp]);
    match  = reply.match(/\((.+?)\)/);
  }

  return reply;
};

// Handle WordNet in Replies
var wordnetReplace = function(match, sym, word, p3, offset, done) {
  wordnet.lookup(word, sym, function(err, words){
    words = words.map(function(item) {return item.replace(/_/g, " ");});
    debug("Wordnet Replies", words);
    var resp = Utils.pickItem(words);
    done(null, resp);
  });
};

// Process tags in a reply element.
module.exports = function (replyObj, user, options, callback) {

  var reply = replyObj.reply.reply;
  var plugins = options.plugins;
  var globalScope = options.scope;
  var gDepth = options.depth;
  var gNormalize = options.normalize;
  var gFacts = options.facts;
  var gQtypes = options.qtypes;

  var match = false;
  var redirectMatch = false;

  // Kinda hacky to pluck the msg from scope.
  var msg = globalScope.message;
  var duplicateMessage = false;

  var rt = topicSetter(reply);
  reply = rt[0];
  if (rt[1] !== "") {
    newtopic = rt[1];
  }

  // Check for reply keep flag, this will override the topicFlag
  // TODO - Strip this off in getReply.
  match = reply.match(/\{keep\}/i);
  if (match) {
    reply = reply.replace(new RegExp("{keep}","ig"), "");
  }

  var stars = [""];
  stars.push.apply(stars, replyObj.stars);

  // Replace <cap> and <capN>
  reply = reply.replace(/<cap>/ig, stars[1]);
  for (var i = 1; i <= stars.length; i++) {
    reply = reply.replace(new RegExp("<cap" + i + ">","ig"), stars[i]);
  }

  // Inline redirector.
  // This is a real bitch because we no longer return
  redirectMatch = reply.match(/\{@(.+?)\}/);

  if (redirectMatch) {
    var options = {
      user: user,
      depth: gDepth+1,
      system: {
        plugins:plugins,
        scope:globalScope,
        topicsSystem: options.topicSystem,

        // Message
        question: gQtypes,
        normalize: gNormalize,
        facts: gFacts
      }
    };

    // We use async to capture multiple matches in the same reply
    var getreply  = require("./getreply");

    return async.whilst(
      function () { return redirectMatch; },
      function (cb) {
        // var target = Utils.trim(redirectMatch[1]);
        var target = redirectMatch[1];
        debug("Inline redirection to: '" + target + "'");
        options.aTopics = [];
        options.aTopics.push(replyObj.topic);

        var messageOptions = {
          qtypes: gQtypes,
          norm: gNormalize,
          facts: gFacts
        };

        new Message(target, messageOptions,  function(replyMessageObject) {

          options.message = replyMessageObject;
          getreply(options, function(err, subreply) {

            if (subreply) {
              debug("CallBack from inline redirect, lets replace", subreply.string, "with '" + target + "'");
              debug("Replace!", "{@" + Utils.quotemeta(target) + "\\}");
              reply = reply.replace(new RegExp("\\{@" + Utils.quotemeta(target) + "\\}", "i"), subreply.string);
              debug("Replaced", reply);
              redirectMatch = reply.match(/\{@(.+?)\}/);
            } else {
              redirectMatch = false;
              reply = reply.replace(new RegExp("\\{@" + Utils.quotemeta(target) + "\\}", "i"), "");
            }

            if (gDepth == 50) {
              cb("Depth Error");
            } else {
              cb(null);
            }
          }); // getReply
        }); // Message
      },
      function(err) {
        if (err) {
          return callback(err, "", globalScope.message.props);
        } else {
          debug("CallBack from inline redirect", Utils.trim(reply));

          parseCustomFunctions(reply, plugins, globalScope, function(err, reply) {
            return callback(null, Utils.trim(reply), globalScope.message.props);
          });

        }
      }
    );
  }

  reply = Utils.trim(reply);
  reply = reply.replace(/\\s/ig, " ");
  reply = reply.replace(/\\n/ig, "\n");
  reply = reply.replace(/\\#/ig, "#");

  // <input> and <reply>

  // Special case, we have no items in the history yet,
  // This could only happen if we are trying to match the first input.
  // Kinda edgy.

  if (!_.isNull(msg)) {
    reply = reply.replace(/<input>/ig, msg.clean);
  }

  reply = reply.replace(/<reply>/ig, user.__history__.reply[0]);
  for (var i = 1; i <= 9; i++) {
    if (reply.indexOf("<input" + i + ">")) {
      reply = reply.replace(new RegExp("<input" + i + ">","ig"),
        user.__history__.input[i-1]);
    }
    if (reply.indexOf("<reply" + i + ">")) {
      reply = reply.replace(new RegExp("<reply" + i + ">","ig"),
        user.__history__.reply[i-1]);
    }
  }

  var redirectMatch = reply.match(/\^topicRedirect\(\s*([~\w<>\s]*),([~\w<>\s]*)\s*\)/);

  if (redirectMatch) {

    var options = {
      user: user,
      depth: gDepth+1,
      system: {
        plugins:plugins,
        scope:globalScope,
        topicsSystem: options.topicSystem,

        // Message
        question: gQtypes,
        normalize: gNormalize,
        facts: gFacts

      }
    };

    // We use async to capture multiple matches in the same reply
    var getreply  = require("./getreply");

    return async.whilst(
      function () { return redirectMatch; },
      function (cb) {
        var main = Utils.trim(redirectMatch[0]);
        var topic = Utils.trim(redirectMatch[1]);
        var target = Utils.trim(redirectMatch[2]);
        debug("Topic Redirection to: " + target + " topic:" + topic);
        options.aTopics = [];
        options.aTopics.push(topic);
        user.setTopic(topic);
        // options.message = {stars:stars.slice(1), clean: target, qtype: "SYSTEM:sys", lemString: "___SYSTEM___" };

        var messageOptions = {
          qtypes: gQtypes,
          norm: gNormalize,
          facts: gFacts
        };

        new Message(target, messageOptions,  function(replyMessageObject) {

          options.message = replyMessageObject;
          // Pass the stars forward
          options.message.stars = stars.slice(1);
          getreply(options, function(err, subreply) {

            if (subreply) {
              debug("CallBack from inline redirect", subreply.string);
              reply = reply.replace(main, subreply.string);
              // reply = reply.replace(new RegExp("\\{@" + Utils.quotemeta(target) + "\\}", "i"), subreply.string);
              redirectMatch = reply.match(/\^topicRedirect\(\s*([~\w<>\s]*),([~\w<>\s]*)\s*\)/);
            } else {
              redirectMatch = false;
              reply = reply.replace(main, "");
            }

            if (gDepth == 50) {
              cb("Depth Error");
            } else {
              cb(null);
            }
          }); // getReply

        }); // Message
      },
      function(err) {
        if (err) {
          return callback(err, "");
        } else {
          // debug("CallBack from inline redirect", Utils.trim(reply));
          // return callback(null, Utils.trim(reply));

          debug("CallBack from inline redirect", reply);
          return callback(null, reply, globalScope.message.props);
        }
      }
    );
  }

  // System function for checking matches in a specific topic
  // This is like redirect accept we search matches in a new topic so it is more fuzzy
  // This would be used to access topics in a unlisted system function
  var respondMatch = reply.match(/\^respond\(\s*([\w~]*)\s*\)/);

  if (respondMatch) {
    var getreply  = require("./getreply");

    var options = {
      user: user,
      depth: gDepth+1,
      system: {
        plugins:plugins,
        scope:globalScope,
        topicsSystem: options.topicSystem,

        // Message
        question: gQtypes,
        normalize: gNormalize,
        facts: gFacts
      }
    };

    return async.whilst(
      function () { return respondMatch; },
      function (cb) {
        var newTopic = Utils.trim(respondMatch[1]);
        debug("Topic Check with new Topic: " + newTopic);
        options.aTopics = [];
        options.aTopics.push(newTopic);
        // Pass the same message in
        options.message = msg;
        getreply(options, function(err, subreply) {
          debug("CallBack from respond topic", subreply);

          if (subreply) {
            reply = subreply.string || "";
            replyObj = subreply;
            respondMatch = reply.match(/\^respond\(\s*([\w~]*)\s*\)/);
          } else {
            respondMatch = false;
            reply = "";
            replyObj = {};
          }

          if (gDepth == 50) {
            cb("Depth Error");
          } else {
            cb(null);
          }
        });
      },
      function(err) {
        if (err) {
          return callback(err, "", globalScope.message.props, replyObj);
        } else {
          debug("CallBack from Respond Function", reply);
          return callback(null, reply, globalScope.message.props, replyObj);
        }
      }
    );
  }

  // Async Replace because normal replace callback is SYNC.
  replace(reply, /(~)(\w[\w]+)/g, wordnetReplace, function(err, reply) {

    if (match = reply.match(/\^(\w+)\(([\w<>,\s]*)\)/)) {

      parseCustomFunctions(reply, plugins, globalScope, function(err, reply) {
        reply = processAlternates(reply);

        // Check that we dont have another topic change
        var rt = topicSetter(reply);
        reply = rt[0];
        if (rt[1] !== "") {
          newtopic = rt[1];
        }

        // We only want to change topics if the message has not been exausted
        if (newtopic && !duplicateMessage) {
          debug("Topic Change", newtopic);
          user.setTopic(newtopic);
        }

        debug("Calling back with", reply);
        callback(err, reply, globalScope.message.props);

      });

    } else {
      reply = processAlternates(reply);

      // We only want to change topics if the message has not been exausted
      if (newtopic && !duplicateMessage) {
        debug("Topic Change", newtopic);
        user.setTopic(newtopic);
      }

      debug("Calling back with", reply);
      callback(null, reply, globalScope.message.props);
    }
  });
};

var topicSetter = function(reply) {

  // TODO - Topic Setter should have its own property
  var match = reply.match(/\{topic=(.+?)\}/i);
  var giveup = 0;
  var newtopic;

  while (match) {
    giveup++;
    if (giveup >= 50) {
      dWarn("Infinite loop looking for topic tag!");
      break;
    }
    var name = match[1];
    newtopic = name;
    reply = reply.replace(new RegExp("{topic=" + Utils.quotemeta(name) + "}","ig"), "");
    match = reply.match(/\{topic=(.+?)\}/i); // Look for more
  }
  return [reply, newtopic];
};
<commit_msg>Removed extra unused debug statement - cleanup<commit_after>var async     = require("async");
var replace   = require("async-replace");
var Utils     = require("./utils");
var wordnet   = require("./wordnet");
var _         = require("underscore");
var Message   = require("./message");

var debug     = require("debug")("ProcessTags");
var dWarn     = require("debug")("ProcessTags:Warning");

var parseCustomFunctions = function(reply, plugins, scope, callback ) {
  // New Custom Function Handle
  // This matches the Redirect finder above
  var match = reply.match(/\^(\w+)\(([\w<>,\s]*)\)/);

  // We use async to capture multiple matches in the same reply
  return async.whilst(
    function () { return match; },
    function (cb) {
      // Call Function here

      var main = match[0];
      var pluginName = Utils.trim(match[1]);
      var partsStr = Utils.trim(match[2]);
      var parts = partsStr.split(",");

      var args  = [];
      for (var i = 0; i < parts.length; i++) {
        if (parts[i] !== "") {
          args.push(parts[i].trim());
        }
      }

      if (plugins[pluginName]) {

        // SubReply is the results of the object coming back
        // TODO. Subreply should be optional and could be undefined, or null
        args.push(function customFunctionHandle(err, subreply) {

          match = false;
          reply = reply.replace(main, subreply);
          match = reply.match(/\^(\w+)\(([~\w<>,\s]*)\)/);
          if (err) {
            cb(err);
          } else {
            cb();
          }
        });

        debug("Calling Plugin Function", pluginName);
        plugins[pluginName].apply(scope, args);

      } else {
        // If a function is missing, we kill the line and return empty handed
        dWarn("Custom Function not-found", pluginName);
        match = false;
        cb(null, "");
      }
    },
    function(err) {
      if (err) {
        callback(null, "");
      } else {
        callback(null, Utils.trim(reply));
      }
    }
  );
};

var processAlternates = function(reply) {
  // Reply Alternates.
  var match  = reply.match(/\((.+?)\)/);
  var giveup = 0;
  while (match) {
    debug("Reply has Alternates");

    giveup++;
    if (giveup >= 50) {
      dWarn("Infinite loop when trying to process optionals in trigger!");
      return "";
    }

    var parts = match[1].split("|");
    var opts  = [];
    for (var i = 0; i < parts.length; i++) {
      opts.push(parts[i].trim());
    }

    var resp = Utils.getRandomInt(0, opts.length - 1);
    reply = reply.replace(new RegExp("\\(" + Utils.quotemeta(match[1]) + "\\)\\s*"), opts[resp]);
    match  = reply.match(/\((.+?)\)/);
  }

  return reply;
};

// Handle WordNet in Replies
var wordnetReplace = function(match, sym, word, p3, offset, done) {
  wordnet.lookup(word, sym, function(err, words){
    words = words.map(function(item) {return item.replace(/_/g, " ");});
    debug("Wordnet Replies", words);
    var resp = Utils.pickItem(words);
    done(null, resp);
  });
};

// Process tags in a reply element.
module.exports = function (replyObj, user, options, callback) {

  var reply = replyObj.reply.reply;
  var plugins = options.plugins;
  var globalScope = options.scope;
  var gDepth = options.depth;
  var gNormalize = options.normalize;
  var gFacts = options.facts;
  var gQtypes = options.qtypes;

  var match = false;
  var redirectMatch = false;

  // Kinda hacky to pluck the msg from scope.
  var msg = globalScope.message;
  var duplicateMessage = false;

  var rt = topicSetter(reply);
  reply = rt[0];
  if (rt[1] !== "") {
    newtopic = rt[1];
  }

  // Check for reply keep flag, this will override the topicFlag
  // TODO - Strip this off in getReply.
  match = reply.match(/\{keep\}/i);
  if (match) {
    reply = reply.replace(new RegExp("{keep}","ig"), "");
  }

  var stars = [""];
  stars.push.apply(stars, replyObj.stars);

  // Replace <cap> and <capN>
  reply = reply.replace(/<cap>/ig, stars[1]);
  for (var i = 1; i <= stars.length; i++) {
    reply = reply.replace(new RegExp("<cap" + i + ">","ig"), stars[i]);
  }

  // Inline redirector.
  // This is a real bitch because we no longer return
  redirectMatch = reply.match(/\{@(.+?)\}/);

  if (redirectMatch) {
    var options = {
      user: user,
      depth: gDepth+1,
      system: {
        plugins:plugins,
        scope:globalScope,
        topicsSystem: options.topicSystem,

        // Message
        question: gQtypes,
        normalize: gNormalize,
        facts: gFacts
      }
    };

    // We use async to capture multiple matches in the same reply
    var getreply  = require("./getreply");

    return async.whilst(
      function () { return redirectMatch; },
      function (cb) {
        var target = redirectMatch[1];
        debug("Inline redirection to: '" + target + "'");
        options.aTopics = [];
        options.aTopics.push(replyObj.topic);

        var messageOptions = {
          qtypes: gQtypes,
          norm: gNormalize,
          facts: gFacts
        };

        new Message(target, messageOptions,  function(replyMessageObject) {

          options.message = replyMessageObject;
          getreply(options, function(err, subreply) {

            if (subreply) {
              debug("CallBack from inline redirect, lets replace", subreply.string, "with '" + target + "'");
              reply = reply.replace(new RegExp("\\{@" + Utils.quotemeta(target) + "\\}", "i"), subreply.string);
              redirectMatch = reply.match(/\{@(.+?)\}/);
            } else {
              redirectMatch = false;
              reply = reply.replace(new RegExp("\\{@" + Utils.quotemeta(target) + "\\}", "i"), "");
            }

            if (gDepth == 50) {
              cb("Depth Error");
            } else {
              cb(null);
            }
          }); // getReply
        }); // Message
      },
      function(err) {
        if (err) {
          return callback(err, "", globalScope.message.props);
        } else {
          debug("CallBack from inline redirect", Utils.trim(reply));

          parseCustomFunctions(reply, plugins, globalScope, function(err, reply) {
            return callback(null, Utils.trim(reply), globalScope.message.props);
          });

        }
      }
    );
  }

  reply = Utils.trim(reply);
  reply = reply.replace(/\\s/ig, " ");
  reply = reply.replace(/\\n/ig, "\n");
  reply = reply.replace(/\\#/ig, "#");

  // <input> and <reply>

  // Special case, we have no items in the history yet,
  // This could only happen if we are trying to match the first input.
  // Kinda edgy.

  if (!_.isNull(msg)) {
    reply = reply.replace(/<input>/ig, msg.clean);
  }

  reply = reply.replace(/<reply>/ig, user.__history__.reply[0]);
  for (var i = 1; i <= 9; i++) {
    if (reply.indexOf("<input" + i + ">")) {
      reply = reply.replace(new RegExp("<input" + i + ">","ig"),
        user.__history__.input[i-1]);
    }
    if (reply.indexOf("<reply" + i + ">")) {
      reply = reply.replace(new RegExp("<reply" + i + ">","ig"),
        user.__history__.reply[i-1]);
    }
  }

  var redirectMatch = reply.match(/\^topicRedirect\(\s*([~\w<>\s]*),([~\w<>\s]*)\s*\)/);

  if (redirectMatch) {

    var options = {
      user: user,
      depth: gDepth+1,
      system: {
        plugins:plugins,
        scope:globalScope,
        topicsSystem: options.topicSystem,

        // Message
        question: gQtypes,
        normalize: gNormalize,
        facts: gFacts

      }
    };

    // We use async to capture multiple matches in the same reply
    var getreply  = require("./getreply");

    return async.whilst(
      function () { return redirectMatch; },
      function (cb) {
        var main = Utils.trim(redirectMatch[0]);
        var topic = Utils.trim(redirectMatch[1]);
        var target = Utils.trim(redirectMatch[2]);
        debug("Topic Redirection to: " + target + " topic:" + topic);
        options.aTopics = [];
        options.aTopics.push(topic);
        user.setTopic(topic);
        // options.message = {stars:stars.slice(1), clean: target, qtype: "SYSTEM:sys", lemString: "___SYSTEM___" };

        var messageOptions = {
          qtypes: gQtypes,
          norm: gNormalize,
          facts: gFacts
        };

        new Message(target, messageOptions,  function(replyMessageObject) {

          options.message = replyMessageObject;
          // Pass the stars forward
          options.message.stars = stars.slice(1);
          getreply(options, function(err, subreply) {

            if (subreply) {
              debug("CallBack from inline redirect", subreply.string);
              reply = reply.replace(main, subreply.string);
              // reply = reply.replace(new RegExp("\\{@" + Utils.quotemeta(target) + "\\}", "i"), subreply.string);
              redirectMatch = reply.match(/\^topicRedirect\(\s*([~\w<>\s]*),([~\w<>\s]*)\s*\)/);
            } else {
              redirectMatch = false;
              reply = reply.replace(main, "");
            }

            if (gDepth == 50) {
              cb("Depth Error");
            } else {
              cb(null);
            }
          }); // getReply

        }); // Message
      },
      function(err) {
        if (err) {
          return callback(err, "");
        } else {
          // debug("CallBack from inline redirect", Utils.trim(reply));
          // return callback(null, Utils.trim(reply));

          debug("CallBack from inline redirect", reply);
          return callback(null, reply, globalScope.message.props);
        }
      }
    );
  }

  // System function for checking matches in a specific topic
  // This is like redirect accept we search matches in a new topic so it is more fuzzy
  // This would be used to access topics in a unlisted system function
  var respondMatch = reply.match(/\^respond\(\s*([\w~]*)\s*\)/);

  if (respondMatch) {
    var getreply  = require("./getreply");

    var options = {
      user: user,
      depth: gDepth+1,
      system: {
        plugins:plugins,
        scope:globalScope,
        topicsSystem: options.topicSystem,

        // Message
        question: gQtypes,
        normalize: gNormalize,
        facts: gFacts
      }
    };

    return async.whilst(
      function () { return respondMatch; },
      function (cb) {
        var newTopic = Utils.trim(respondMatch[1]);
        debug("Topic Check with new Topic: " + newTopic);
        options.aTopics = [];
        options.aTopics.push(newTopic);
        // Pass the same message in
        options.message = msg;
        getreply(options, function(err, subreply) {
          debug("CallBack from respond topic", subreply);

          if (subreply) {
            reply = subreply.string || "";
            replyObj = subreply;
            respondMatch = reply.match(/\^respond\(\s*([\w~]*)\s*\)/);
          } else {
            respondMatch = false;
            reply = "";
            replyObj = {};
          }

          if (gDepth == 50) {
            cb("Depth Error");
          } else {
            cb(null);
          }
        });
      },
      function(err) {
        if (err) {
          return callback(err, "", globalScope.message.props, replyObj);
        } else {
          debug("CallBack from Respond Function", reply);
          return callback(null, reply, globalScope.message.props, replyObj);
        }
      }
    );
  }

  // Async Replace because normal replace callback is SYNC.
  replace(reply, /(~)(\w[\w]+)/g, wordnetReplace, function(err, reply) {

    if (match = reply.match(/\^(\w+)\(([\w<>,\s]*)\)/)) {

      parseCustomFunctions(reply, plugins, globalScope, function(err, reply) {
        reply = processAlternates(reply);

        // Check that we dont have another topic change
        var rt = topicSetter(reply);
        reply = rt[0];
        if (rt[1] !== "") {
          newtopic = rt[1];
        }

        // We only want to change topics if the message has not been exausted
        if (newtopic && !duplicateMessage) {
          debug("Topic Change", newtopic);
          user.setTopic(newtopic);
        }

        debug("Calling back with", reply);
        callback(err, reply, globalScope.message.props);

      });

    } else {
      reply = processAlternates(reply);

      // We only want to change topics if the message has not been exausted
      if (newtopic && !duplicateMessage) {
        debug("Topic Change", newtopic);
        user.setTopic(newtopic);
      }

      debug("Calling back with", reply);
      callback(null, reply, globalScope.message.props);
    }
  });
};

var topicSetter = function(reply) {

  // TODO - Topic Setter should have its own property
  var match = reply.match(/\{topic=(.+?)\}/i);
  var giveup = 0;
  var newtopic;

  while (match) {
    giveup++;
    if (giveup >= 50) {
      dWarn("Infinite loop looking for topic tag!");
      break;
    }
    var name = match[1];
    newtopic = name;
    reply = reply.replace(new RegExp("{topic=" + Utils.quotemeta(name) + "}","ig"), "");
    match = reply.match(/\{topic=(.+?)\}/i); // Look for more
  }
  return [reply, newtopic];
};
<|endoftext|>


<commit_before>import * as core from '../../core';
import LightSpriteRenderer from './LightSpriteRenderer';

const RenderTexture = core.RenderTexture;
const WebGLRenderer = core.WebGLRenderer;
const Sprite = core.Sprite;

// const tempArray = new Float32Array(4);

export default class LightSpriteRendererWithRenderTexture extends core.ObjectRenderer
{
    constructor(renderer)
    {
        super(renderer);
        this.gl = renderer.gl;

        this.middleRenderTexture = RenderTexture.create(renderer.width, renderer.height);
        this.middleSprite = new Sprite(this.middleRenderTexture);
        this.diffuseRenderTexture = RenderTexture.create(2, 2);
        this.normalRenderTexture = RenderTexture.create(2, 2);
    }

    onContextChange()
    {
        this.contextChanged = true;
    }

    render(sprite)
    {
        if (!sprite.texture._uvs || !sprite.lights) {
            return;
        }

        const renderer = this.renderer;
        const gl = renderer.gl;

        let width;
        let height;

        if (sprite.diffuseTexture) {
            width = sprite.diffuseTexture.width;
            height = sprite.diffuseTexture.height;
            this.diffuseRenderTexture.resize(width, height);
            renderer.render(sprite.diffuseTexture, this.diffuseRenderTexture, true);
        } else {
            // TODO
        }

        if (sprite.normalTexture) {
            width = sprite.normalTexture.width;
            height = sprite.normalTexture.height;
            this.normalRenderTexture.resize(width, height);
            renderer.render(sprite.normalTexture, this.normalRenderTexture, true);
        } else {
            // TODO
        }
        renderer.setObjectRenderer(renderer.plugins.lightsprite);

        const lightCount = sprite.lights.length;

        if (lightCount > 1) {
            renderer.bindRenderTexture(this.middleRenderTexture);
            gl.disable(gl.SCISSOR_TEST);
            renderer.clear(); // [1, 1, 1, 1]);
            gl.enable(gl.SCISSOR_TEST);
        } else {
            renderer.bindRenderTarget(renderer.rootRenderTarget);
        }

        const vertexData = sprite.computedGeometry ? sprite.computedGeometry.vertices : sprite.vertexData;
        const uvsData = sprite._texture._uvs;
        const uvsDataNormal = sprite.normalTexture._texture._uvs;

        const diffuseBaseTexture = this.diffuseRenderTexture.baseTexture;
        const normalBaseTexture = this.normalRenderTexture.baseTexture;

        const lights = sprite.lights;

        for (let i = 0; i < lights.length; i++)
        {
            const light = lights[i];
            light.init(renderer, this.contextChanged);

            const shader = light.shader;
            const quad = light.quad;

            const vertices = quad.vertices;
            const uvs = quad.uvs;

            for (let i = 0; i < 8; i++) {
                vertices[i] = vertexData[i];
            }

            uvs[0] = uvsData.x0;
            uvs[1] = uvsData.y0;
            uvs[2] = uvsData.x1;
            uvs[3] = uvsData.y1;
            uvs[4] = uvsData.x2;
            uvs[5] = uvsData.y2;
            uvs[6] = uvsData.x3;
            uvs[7] = uvsData.y3;

            uvs[8] = uvsDataNormal.x0;
            uvs[9] = uvsDataNormal.y0;
            uvs[10] = uvsDataNormal.x1;
            uvs[11] = uvsDataNormal.y1;
            uvs[12] = uvsDataNormal.x2;
            uvs[13] = uvsDataNormal.y2;
            uvs[14] = uvsDataNormal.x3;
            uvs[15] = uvsDataNormal.y3;
            quad.upload();

            renderer.bindShader(shader);
            renderer.bindVao(quad.vao);

            light.syncShader();

            shader.uniforms.uSampler = renderer.bindTexture(diffuseBaseTexture);
            shader.uniforms.uNormalSampler = renderer.bindTexture(normalBaseTexture);

            renderer.state.setBlendMode(light.blendMode);

            light.quad.vao.draw(renderer.drawModes[light.drawMode], 6, 0);

            renderer.drawCount++;
        }

        if (lightCount > 1) {
            renderer.bindRenderTarget(renderer.rootRenderTarget);
            renderer.render(this.middleSprite, null, false);
            renderer.drawCount++;
        }

        this.contextChanged = false;
    }

    destroy()
    {
        super.destroy();
        this.diffuseRenderTexture.destroy();
        this.normalRenderTexture.destroy();
    }

    static applyTo(sprite)
    {
        sprite._renderWebGLBakLightSpriteRenderer = sprite._renderWebGL;
        sprite._renderWebGL = LightSpriteRenderer.__renderWebGLSprite;
    }

    static unapplyTo(sprite)
    {
        if (sprite._renderWebGLBakLightSpriteRenderer) {
            sprite._renderWebGL = sprite._renderWebGLBakLightSpriteRenderer;
            sprite._renderWebGLBakLightSpriteRenderer = null;
        }
    }

    static __renderWebGLSprite(renderer)
    {
        const sprite = this;
        // sprite.calculateVerticesWithoutTransform(true);
        sprite.calculateVertices();
        renderer.setObjectRenderer(renderer.plugins.lightsprite);
        renderer.plugins.lightsprite.render(sprite);
    }
}

WebGLRenderer.registerPlugin('lightsprite2', LightSpriteRendererWithRenderTexture);

<commit_msg>remove useless files<commit_after><|endoftext|>


<commit_before>'use strict';

var fs = require('fs-extra');
var path = require('path');
require('shelljs/global');

//TODO(markus): Needs some refactoring
//Try to implement these not as functions but as attributes of the object and set it at each sane call
//BUG: projectRoot changes when we do a cd() command
var self = {
  binaryPath: function() {
    return which('sane');
  },

  sanePath: function() {
    return self.binaryPath().slice(0, -4) + fs.readlinkSync(self.binaryPath()).slice(0, -4) + '../';
  },

  version: function() {
    return JSON.parse(fs.readFileSync(path.join(self.sanePath(), 'package.json'), 'utf8')).version;
  } //,

  // projectRoot: function() {
  //   return process.cwd();
  // },

  // serverRoot: function() {
  //   return path.join(self.projectRoot(), 'server');
  // },

  // clientRoot: function() {
  //   return path.join(self.projectRoot(), 'client')
  // }
};

module.exports = self;<commit_msg>fix sanePath folder in linux global install<commit_after>'use strict';

var fs = require('fs-extra');
var path = require('path');
require('shelljs/global');

//TODO(markus): Needs some refactoring
//Try to implement these not as functions but as attributes of the object and set it at each sane call
//BUG: projectRoot changes when we do a cd() command
var self = {
  binaryPath: function() {
    return which('sane');
  },

  sanePath: function() {
    return  path.resolve(__dirname , '../');
  },

  version: function() {
    return require(self.sanePath() + '/package.json').version;
  } //,

  // projectRoot: function() {
  //   return process.cwd();
  // },

  // serverRoot: function() {
  //   return path.join(self.projectRoot(), 'server');
  // },

  // clientRoot: function() {
  //   return path.join(self.projectRoot(), 'client')
  // }
};

module.exports = self;<|endoftext|>


<commit_before>/* ------------------------------------------------------------------------- *
 * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems               *
 *                                                                           *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may   *
 * not use this file except in compliance with the License. You may obtain   *
 * a copy of the License at                                                  *
 *                                                                           *
 * http://www.apache.org/licenses/LICENSE-2.0                                *
 *                                                                           *
 * Unless required by applicable law or agreed to in writing, software       *
 * distributed under the License is distributed on an "AS IS" BASIS,         *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
 * See the License for the specific language governing permissions and       *
 * limitations under the License.                                            *
 * ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback, ReactElement } from 'react'
import PropTypes from 'prop-types'
import { useHistory, generatePath } from 'react-router-dom'

import {
  AppleImac2021 as VncIcon,
  TerminalOutline as SshIcon,
  Windows as RdpIcon,
} from 'iconoir-react'
import { SubmitButton } from 'client/components/FormControl'

import { useViews } from 'client/features/Auth'
import { useLazyGetGuacamoleSessionQuery } from 'client/features/OneApi/vm'
import { useLazyGetVMRCSessionQuery } from 'client/features/OneApi/vcenter'
import {
  isVCenter,
  getHypervisor,
  nicsIncludesTheConnectionType,
  isAvailableAction,
} from 'client/models/VirtualMachine'
import { Translate } from 'client/components/HOC'
import { T, VM, RESOURCE_NAMES, VM_ACTIONS } from 'client/constants'
import { PATH } from 'client/apps/sunstone/routes'

const GUACAMOLE_BUTTONS = {
  [VM_ACTIONS.VNC]: { tooltip: T.Vnc, icon: <VncIcon /> },
  [VM_ACTIONS.SSH]: { tooltip: T.Ssh, icon: <SshIcon /> },
  [VM_ACTIONS.RDP]: { tooltip: T.Rdp, icon: <RdpIcon /> },
}

const GuacamoleButton = memo(({ vm, connectionType, onClick }) => {
  const { icon, tooltip } = GUACAMOLE_BUTTONS[connectionType]
  const history = useHistory()
  const [getSession, { isLoading }] = useLazyGetGuacamoleSessionQuery()

  const goToConsole = useCallback(
    async (evt) => {
      try {
        evt.stopPropagation()

        const params = { id: vm?.ID, type: connectionType }
        const session = await getSession(params)

        typeof onClick === 'function'
          ? onClick(session)
          : history.push(generatePath(PATH.GUACAMOLE, params))
      } catch {}
    },
    [vm?.ID, connectionType, history, onClick]
  )

  return (
    <SubmitButton
      data-cy={`${vm?.ID}-${connectionType}`}
      icon={icon}
      tooltip={<Translate word={tooltip} />}
      isSubmitting={isLoading}
      onClick={goToConsole}
    />
  )
})

const VMRCButton = memo(({ vm, onClick }) => {
  const history = useHistory()
  const [getSession, { isLoading }] = useLazyGetVMRCSessionQuery()

  const goToConsole = useCallback(
    async (evt) => {
      try {
        evt.stopPropagation()

        const params = { id: vm?.ID }
        const session = await getSession(params)

        typeof onClick === 'function'
          ? onClick(session)
          : history.push(generatePath(PATH.WMKS, params))
      } catch {}
    },
    [vm?.ID, history, onClick]
  )

  return (
    <SubmitButton
      data-cy={`${vm?.ID}-vmrc`}
      icon={<VncIcon />}
      tooltip={<Translate word={T.Vmrc} />}
      isSubmitting={isLoading}
      onClick={goToConsole}
    />
  )
})

const PreConsoleButton = memo(
  /**
   * @param {object} props - Props
   * @param {VM} props.vm - Virtual machine
   * @param {'vnc'|'ssh'|'rdp'|'vmrc'} props.connectionType - Connection type
   * @param {Function} [props.onClick] - Handle click for button
   * @returns {ReactElement} - Returns button if current user has permissions about the connection type
   */
  (props) => {
    const { vm, connectionType } = props
    const { view, [RESOURCE_NAMES.VM]: vmView } = useViews()

    const isDisabled = useMemo(() => {
      const noAction = vmView?.actions?.[connectionType] !== true
      const noAvailable = isAvailableAction(connectionType)(vm)
      const notHypervisor = !getHypervisor(vm)

      return noAction || noAvailable || notHypervisor
    }, [view, vm])

    const needNicConfig = useMemo(
      () => ![VM_ACTIONS.VNC, VM_ACTIONS.VMRC].includes(connectionType),
      [connectionType]
    )

    if (
      isDisabled ||
      (needNicConfig && !nicsIncludesTheConnectionType(vm, connectionType))
    ) {
      return null
    }

    if (isVCenter(vm)) {
      return connectionType === VM_ACTIONS.VMRC ? (
        <VMRCButton {...props} />
      ) : null
    }

    return <GuacamoleButton {...props} />
  }
)

const ButtonPropTypes = {
  vm: PropTypes.object,
  connectionType: PropTypes.string,
  onClick: PropTypes.func,
}

GuacamoleButton.propTypes = ButtonPropTypes
VMRCButton.propTypes = ButtonPropTypes
PreConsoleButton.propTypes = ButtonPropTypes

GuacamoleButton.displayName = 'GuacamoleButton'
VMRCButton.displayName = 'VMRCButton'
PreConsoleButton.displayName = 'PreConsoleButton'

export { PreConsoleButton as GuacamoleButton }
<commit_msg>M #~: Fix minor to console actions (#1878)<commit_after>/* ------------------------------------------------------------------------- *
 * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems               *
 *                                                                           *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may   *
 * not use this file except in compliance with the License. You may obtain   *
 * a copy of the License at                                                  *
 *                                                                           *
 * http://www.apache.org/licenses/LICENSE-2.0                                *
 *                                                                           *
 * Unless required by applicable law or agreed to in writing, software       *
 * distributed under the License is distributed on an "AS IS" BASIS,         *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
 * See the License for the specific language governing permissions and       *
 * limitations under the License.                                            *
 * ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback, ReactElement } from 'react'
import PropTypes from 'prop-types'
import { useHistory, generatePath } from 'react-router-dom'

import {
  AppleImac2021 as VncIcon,
  TerminalOutline as SshIcon,
  Windows as RdpIcon,
} from 'iconoir-react'
import { SubmitButton } from 'client/components/FormControl'

import { useViews } from 'client/features/Auth'
import { useLazyGetGuacamoleSessionQuery } from 'client/features/OneApi/vm'
import { useLazyGetVMRCSessionQuery } from 'client/features/OneApi/vcenter'
import {
  isVCenter,
  getHypervisor,
  nicsIncludesTheConnectionType,
  isAvailableAction,
} from 'client/models/VirtualMachine'
import { Translate } from 'client/components/HOC'
import { T, VM, RESOURCE_NAMES, VM_ACTIONS } from 'client/constants'
import { PATH } from 'client/apps/sunstone/routes'

const GUACAMOLE_BUTTONS = {
  [VM_ACTIONS.VNC]: { tooltip: T.Vnc, icon: <VncIcon /> },
  [VM_ACTIONS.SSH]: { tooltip: T.Ssh, icon: <SshIcon /> },
  [VM_ACTIONS.RDP]: { tooltip: T.Rdp, icon: <RdpIcon /> },
}

const GuacamoleButton = memo(({ vm, connectionType, onClick }) => {
  const { icon, tooltip } = GUACAMOLE_BUTTONS[connectionType]
  const history = useHistory()
  const [getSession, { isLoading }] = useLazyGetGuacamoleSessionQuery()

  const goToConsole = useCallback(
    async (evt) => {
      try {
        evt.stopPropagation()

        const params = { id: vm?.ID, type: connectionType }
        const session = await getSession(params).unwrap()

        typeof onClick === 'function'
          ? onClick(session)
          : history.push(generatePath(PATH.GUACAMOLE, params))
      } catch {}
    },
    [vm?.ID, connectionType, history, onClick]
  )

  return (
    <SubmitButton
      data-cy={`${vm?.ID}-${connectionType}`}
      icon={icon}
      tooltip={<Translate word={tooltip} />}
      isSubmitting={isLoading}
      onClick={goToConsole}
    />
  )
})

const VMRCButton = memo(({ vm, onClick }) => {
  const history = useHistory()
  const [getSession, { isLoading }] = useLazyGetVMRCSessionQuery()

  const goToConsole = useCallback(
    async (evt) => {
      try {
        evt.stopPropagation()

        const params = { id: vm?.ID }
        const session = await getSession(params).unwrap()

        typeof onClick === 'function'
          ? onClick(session)
          : history.push(generatePath(PATH.WMKS, params))
      } catch {}
    },
    [vm?.ID, history, onClick]
  )

  return (
    <SubmitButton
      data-cy={`${vm?.ID}-vmrc`}
      icon={<VncIcon />}
      tooltip={<Translate word={T.Vmrc} />}
      isSubmitting={isLoading}
      onClick={goToConsole}
    />
  )
})

const PreConsoleButton = memo(
  /**
   * @param {object} props - Props
   * @param {VM} props.vm - Virtual machine
   * @param {'vnc'|'ssh'|'rdp'|'vmrc'} props.connectionType - Connection type
   * @param {Function} [props.onClick] - Handle click for button
   * @returns {ReactElement} - Returns button if current user has permissions about the connection type
   */
  (props) => {
    const { vm, connectionType } = props
    const { view, [RESOURCE_NAMES.VM]: vmView } = useViews()

    const isDisabled = useMemo(() => {
      const noAction = vmView?.actions?.[connectionType] !== true
      const noAvailable = isAvailableAction(connectionType)(vm)
      const notHypervisor = !getHypervisor(vm)

      return noAction || noAvailable || notHypervisor
    }, [view, vm])

    const needNicConfig = useMemo(
      () => ![VM_ACTIONS.VNC, VM_ACTIONS.VMRC].includes(connectionType),
      [connectionType]
    )

    if (
      isDisabled ||
      (needNicConfig && !nicsIncludesTheConnectionType(vm, connectionType))
    ) {
      return null
    }

    if (isVCenter(vm)) {
      return connectionType === VM_ACTIONS.VMRC ? (
        <VMRCButton {...props} />
      ) : null
    }

    return <GuacamoleButton {...props} />
  }
)

const ButtonPropTypes = {
  vm: PropTypes.object,
  connectionType: PropTypes.string,
  onClick: PropTypes.func,
}

GuacamoleButton.propTypes = ButtonPropTypes
VMRCButton.propTypes = ButtonPropTypes
PreConsoleButton.propTypes = ButtonPropTypes

GuacamoleButton.displayName = 'GuacamoleButton'
VMRCButton.displayName = 'VMRCButton'
PreConsoleButton.displayName = 'PreConsoleButton'

export { PreConsoleButton as GuacamoleButton }
<|endoftext|>


<commit_before>var superagent = require( 'superagent' );
var sha1 = require('./util').sha1;

var BASEURL	  = 'http://api.cn.ronghub.com';
var BASEURL_SMS	  = 'http://api.sms.ronghub.com';
var APPKEY 	  = undefined;
var APPSECRET = undefined;
var TIMESTAMP = undefined;
var SIGNATURE = undefined;
var NONCE 	  = undefined;
var HEADERS	  = {};

var FORMAT 	  = 'json';

exports.init = function( appKey, appSecret ) {
	APPKEY 	  = appKey;
	APPSECRET = appSecret;
	NONCE	    = parseInt( Math.random() * 0xffffff );
	TIMESTAMP = Date.parse( new Date() )/1000;
    SIGNATURE = sha1( APPSECRET + NONCE + TIMESTAMP  );

	HEADERS['App-Key'] 	 	= APPKEY;
	HEADERS['Nonce'] 		= NONCE;
	HEADERS['Timestamp']	= TIMESTAMP;
	HEADERS['Signature']	= SIGNATURE;
}

exports.request = function( api, params, format, callback ) {
	return requestWithDomain( BASEURL, api, params, format, callback );
}

exports.requestSMS = function( api, params, format, callback ) {
	return requestWithDomain( BASEURL_SMS, api, params, format, callback );
}

requestWithDomain = function( base_url, api, params, format, callback ) {
	var f = 'json';
	if( (typeof format ) == 'function' ) { // Use default format 'json'
		callback = format;
	}
	else {
		f = ( format === 'xml' ? 'xml' : 'json' );
	}

	superagent.agent()
	.post( base_url + api + '.' + f )
	.set( HEADERS )
	.type( 'form' )
	.send( params )
	.end( function( err, result ) {
		if( err ) {
			return callback( err, result ? result.text : null );
		}
		else {
			return callback( null, result.text );
		}
	} );
}

exports.requestWithSameFields = function( api, params, fieldAndValues, format, callback ) {
	var f = 'json';
	if( (typeof format ) == 'function' ) { // Use default format 'json'
		callback = format;
	}
	else {
		f = ( format === 'xml' ? 'xml' : 'json' );
	}

	var agent = superagent.agent()
        .post( BASEURL + api + '.' + f )
        .set( HEADERS )
        .type( 'form' )
        .send( params );

	for( var i=0; i<fieldAndValues.length; ++i ) {
		var field = fieldAndValues[i].field;
		var values = fieldAndValues[i].values;
		for( var j=0; j<values.length; ++j ) {
                        var payload = {};
                        payload[field] = values[j];
                        agent = agent.send(payload);
		}
	}

  agent.end( function( err, result ) {
		if( err ) {
			return callback( err, result ? result.text : null );
		}
		else {
			return callback( null, result.text );
		}
	} );
}

exports.validateSignature = function( nonce, timestamp, signature ) {
	return sha1( APPSECRET + nonce + timestamp ) == signature;
}

exports.requestWithHeaders = function( api, params, format, headers, callback ) {
	var f = 'json';
	if( (typeof format ) == 'function' ) { // Use default format 'json'
		callback = format;
	}
	else {
		f = ( format === 'xml' ? 'xml' : 'json' );
	}

  // Attach the specified headers.
  var _headers = {};
  for( var k in HEADERS ) {
    _headers[k] = headers[k] ? headers[k] : HEADERS[k];
  }

	superagent.agent()
	.post( BASEURL + api + '.' + f )
	.set( _headers )
	.send( params )
	.end( function( err, result ) {
		if( err ) {
			return callback( err, result ? result.text : null );
		}
		else {
			return callback( null, result.text );
		}
	} );
}

exports.get = function( api, params, callback ) {
	superagent.agent()
	.get( BASEURL_SMS + api + '.json' )
	.query( params )
	.end( function( err, result ) {
		if( err ) {
			return callback( err, result ? result.text : null );
		}
		else {
			return callback( null, result.text );
		}
	} );
}
<commit_msg>Fixed: can not add multi same name parameters.<commit_after>var superagent = require( 'superagent' );
var sha1 = require('./util').sha1;

var BASEURL	  = 'http://api.cn.ronghub.com';
var BASEURL_SMS	  = 'http://api.sms.ronghub.com';
var APPKEY 	  = undefined;
var APPSECRET = undefined;
var TIMESTAMP = undefined;
var SIGNATURE = undefined;
var NONCE 	  = undefined;
var HEADERS	  = {};

var FORMAT 	  = 'json';

exports.init = function( appKey, appSecret ) {
	APPKEY 	  = appKey;
	APPSECRET = appSecret;
	NONCE	    = parseInt( Math.random() * 0xffffff );
	TIMESTAMP = Date.parse( new Date() )/1000;
    SIGNATURE = sha1( APPSECRET + NONCE + TIMESTAMP  );

	HEADERS['App-Key'] 	 	= APPKEY;
	HEADERS['Nonce'] 		= NONCE;
	HEADERS['Timestamp']	= TIMESTAMP;
	HEADERS['Signature']	= SIGNATURE;
}

exports.request = function( api, params, format, callback ) {
	return requestWithDomain( BASEURL, api, params, format, callback );
}

exports.requestSMS = function( api, params, format, callback ) {
	return requestWithDomain( BASEURL_SMS, api, params, format, callback );
}

requestWithDomain = function( base_url, api, params, format, callback ) {
	var f = 'json';
	if( (typeof format ) == 'function' ) { // Use default format 'json'
		callback = format;
	}
	else {
		f = ( format === 'xml' ? 'xml' : 'json' );
	}

	superagent.agent()
	.post( base_url + api + '.' + f )
	.set( HEADERS )
	.type( 'form' )
	.send( params )
	.end( function( err, result ) {
		if( err ) {
			return callback( err, result ? result.text : null );
		}
		else {
			return callback( null, result.text );
		}
	} );
}

exports.requestWithSameFields = function( api, params, fieldAndValues, format, callback ) {
	var f = 'json';
	if( (typeof format ) == 'function' ) { // Use default format 'json'
		callback = format;
	}
	else {
		f = ( format === 'xml' ? 'xml' : 'json' );
	}

	var agent = superagent.agent()
        .post( BASEURL + api + '.' + f )
        .set( HEADERS )
        .type( 'form' )
        .send( params );

	for( var i=0; i<fieldAndValues.length; ++i ) {
		var field = fieldAndValues[i].field;
		var values = fieldAndValues[i].values;
		for( var j=0; j<values.length; ++j ) {
			agent.send( encodeURIComponent(field) + '=' + encodeURIComponent(values[j]) );
		}
	}

  agent.end( function( err, result ) {
		if( err ) {
			return callback( err, result ? result.text : null );
		}
		else {
			return callback( null, result.text );
		}
	} );
}

exports.validateSignature = function( nonce, timestamp, signature ) {
	return sha1( APPSECRET + nonce + timestamp ) == signature;
}

exports.requestWithHeaders = function( api, params, format, headers, callback ) {
	var f = 'json';
	if( (typeof format ) == 'function' ) { // Use default format 'json'
		callback = format;
	}
	else {
		f = ( format === 'xml' ? 'xml' : 'json' );
	}

  // Attach the specified headers.
  var _headers = {};
  for( var k in HEADERS ) {
    _headers[k] = headers[k] ? headers[k] : HEADERS[k];
  }

	superagent.agent()
	.post( BASEURL + api + '.' + f )
	.set( _headers )
	.send( params )
	.end( function( err, result ) {
		if( err ) {
			return callback( err, result ? result.text : null );
		}
		else {
			return callback( null, result.text );
		}
	} );
}

exports.get = function( api, params, callback ) {
	superagent.agent()
	.get( BASEURL_SMS + api + '.json' )
	.query( params )
	.end( function( err, result ) {
		if( err ) {
			return callback( err, result ? result.text : null );
		}
		else {
			return callback( null, result.text );
		}
	} );
}
<|endoftext|>


<commit_before>'use strict';

describe('filterUtils', function(){
    var filterUtils, utilService;

    beforeEach(module('owh'));

    beforeEach(inject(function ($injector) {
        filterUtils = $injector.get('filterUtils');
        utilService = $injector.get('utilService');
    }));

    describe('test getBridgeDataFilters', function() {
        it('when I call getBridgeDataFilters, I should get bridge race filters', function () {
            var bridgeRaceFilters = filterUtils.getBridgeDataFilters();
            expect(bridgeRaceFilters[0].key).toEqual('current_year');
            expect(bridgeRaceFilters[1].key).toEqual('sex');
            expect(bridgeRaceFilters[2].key).toEqual('agegroup');
            expect(bridgeRaceFilters[3].key).toEqual('race');
            expect(bridgeRaceFilters[5].autoCompleteOptions[0].title).toEqual('Alabama');
            expect(bridgeRaceFilters[5].autoCompleteOptions[1].title).toEqual('Alaska');
        });

        it('should provide me correct slider intervals', function () {

            var bridgeRaceFilters = filterUtils.getBridgeDataFilters();
            //Slider lower and upper bounds
            expect(bridgeRaceFilters[2].sliderOptions.from).toEqual(0);
            expect(bridgeRaceFilters[2].sliderOptions.to).toEqual(85);

            bridgeRaceFilters[2].sliderOptions.callback('5;19');

            var ageGroupFilter = utilService.findByKeyAndValue(bridgeRaceFilters, 'key', 'agegroup');
            expect(ageGroupFilter.value).toEqual(['5-9 years', '10-14 years', '15-19 years']);
        });
    });

    describe('test natality filters', function() {
        it('when I call getNatalityDataFilters, I should get natality filters', function () {
            var natalityFilters = filterUtils.getNatalityDataFilters();
            expect(natalityFilters[0].key).toEqual('hispanic_origin');
            expect(natalityFilters[1].key).toEqual('state');
            expect(natalityFilters[2].key).toEqual('mother_age_1year_interval');
            expect(natalityFilters[3].key).toEqual('mother_age_5year_interval');
            expect(natalityFilters[4].key).toEqual('race');
            expect(natalityFilters[5].key).toEqual('marital_status');
        });
    });

    describe('test infant mortality filters', function() {
        it('when I call getInfantMortalityDataFilters, I should get infant mortality filters', function () {
            var InfantMortalityData= filterUtils.getInfantMortalityDataFilters();
            expect(InfantMortalityData[0].key).toEqual('year_of_death');
            expect(InfantMortalityData[0].value).toEqual(['2014']);
            expect(InfantMortalityData[1].key).toEqual('sex');
            expect(InfantMortalityData[2].key).toEqual('infant_age_at_death');
            expect(InfantMortalityData[3].key).toEqual('race');
            expect(InfantMortalityData[4].key).toEqual('hispanic_origin');
            expect(InfantMortalityData[5].key).toEqual('mother_age_5_interval');
            expect(InfantMortalityData[6].key).toEqual('marital_status');
            expect(InfantMortalityData[7].key).toEqual('mother_education');
        });
    });

    describe('STD filters -> ', function() {
        it('when I call getSTDDataFilters, I should get all std filters', function () {
            var stdDataFilters= filterUtils.getSTDDataFilters();
            expect(stdDataFilters[0].key).toEqual('current_year');
            expect(stdDataFilters[0].value).toEqual('2015');
            expect(stdDataFilters[0].helpText).toEqual('label.std.help.text.year');
            expect(stdDataFilters[1].key).toEqual('disease');
            expect(stdDataFilters[1].value).toEqual('Chlamydia');
            expect(stdDataFilters[1].helpText).toEqual('label.std.help.text.disease');
            expect(stdDataFilters[2].key).toEqual('state');
            expect(stdDataFilters[2].value).toEqual('National');
            expect(stdDataFilters[2].helpText).toEqual('label.std.help.text.state');
            expect(stdDataFilters[3].key).toEqual('age_group');
            expect(stdDataFilters[3].value).toEqual('All age groups');
            expect(stdDataFilters[3].helpText).toEqual('label.std.help.text.age.group');
            expect(stdDataFilters[3].autoCompleteOptions.length).toEqual(11);
            expect(stdDataFilters[3].autoCompleteOptions[0].key).toEqual("All age groups");
            expect(stdDataFilters[3].autoCompleteOptions[1].key).toEqual("0-14");
            expect(stdDataFilters[3].autoCompleteOptions[2].key).toEqual("15-19");
            expect(stdDataFilters[3].autoCompleteOptions[3].key).toEqual("20-24");
            expect(stdDataFilters[3].autoCompleteOptions[4].key).toEqual("25-29");
            expect(stdDataFilters[3].autoCompleteOptions[5].key).toEqual("30-34");
            expect(stdDataFilters[3].autoCompleteOptions[6].key).toEqual("35-39");
            expect(stdDataFilters[3].autoCompleteOptions[7].key).toEqual("40-44");
            expect(stdDataFilters[3].autoCompleteOptions[8].key).toEqual("45-54");
            expect(stdDataFilters[3].autoCompleteOptions[9].key).toEqual("55-64");
            expect(stdDataFilters[3].autoCompleteOptions[10].key).toEqual("65+");
            expect(stdDataFilters[4].key).toEqual('race');
            expect(stdDataFilters[4].value).toEqual('All races/ethnicities');
            expect(stdDataFilters[4].helpText).toEqual('label.std.help.text.race.ethnicity');
            expect(stdDataFilters[5].key).toEqual('sex');
            expect(stdDataFilters[5].value).toEqual('Both sexes');
            expect(stdDataFilters[5].helpText).toEqual('label.std.help.text.sex');
        });

        it('When I call getAllOptionValues, I should get list of All option values', function(){
            var values = filterUtils.getAllOptionValues();
            expect(values[0]).toEqual("Both sexes");
            expect(values[1]).toEqual("All races/ethnicities");
            expect(values[2]).toEqual("All age groups");
            expect(values[3]).toEqual("National");
        });
    });

    describe('TB filters -> ', function() {
        it('when I call getTBDataFilters, I should get all TB filters', function () {
            var stdDataFilters= filterUtils.getTBDataFilters();
            expect(stdDataFilters[0].key).toEqual('current_year');
            expect(stdDataFilters[0].value).toEqual('2015');
            expect(stdDataFilters[1].key).toEqual('age_group');
            expect(stdDataFilters[2].key).toEqual('race');
            expect(stdDataFilters[2].value).toEqual('All races/ethnicities');
            expect(stdDataFilters[3].key).toEqual('sex');
            expect(stdDataFilters[3].value).toEqual('Both sexes');
            expect(stdDataFilters[4].key).toEqual('state');
            expect(stdDataFilters[4].value).toEqual('National');
            expect(stdDataFilters[5].key).toEqual('transmission');
            expect(stdDataFilters[4].value).toEqual('');
        });
    });
});<commit_msg>fixed controller tests<commit_after>'use strict';

describe('filterUtils', function(){
    var filterUtils, utilService;

    beforeEach(module('owh'));

    beforeEach(inject(function ($injector) {
        filterUtils = $injector.get('filterUtils');
        utilService = $injector.get('utilService');
    }));

    describe('test getBridgeDataFilters', function() {
        it('when I call getBridgeDataFilters, I should get bridge race filters', function () {
            var bridgeRaceFilters = filterUtils.getBridgeDataFilters();
            expect(bridgeRaceFilters[0].key).toEqual('current_year');
            expect(bridgeRaceFilters[1].key).toEqual('sex');
            expect(bridgeRaceFilters[2].key).toEqual('agegroup');
            expect(bridgeRaceFilters[3].key).toEqual('race');
            expect(bridgeRaceFilters[5].autoCompleteOptions[0].title).toEqual('Alabama');
            expect(bridgeRaceFilters[5].autoCompleteOptions[1].title).toEqual('Alaska');
        });

        it('should provide me correct slider intervals', function () {

            var bridgeRaceFilters = filterUtils.getBridgeDataFilters();
            //Slider lower and upper bounds
            expect(bridgeRaceFilters[2].sliderOptions.from).toEqual(0);
            expect(bridgeRaceFilters[2].sliderOptions.to).toEqual(85);

            bridgeRaceFilters[2].sliderOptions.callback('5;19');

            var ageGroupFilter = utilService.findByKeyAndValue(bridgeRaceFilters, 'key', 'agegroup');
            expect(ageGroupFilter.value).toEqual(['5-9 years', '10-14 years', '15-19 years']);
        });
    });

    describe('test natality filters', function() {
        it('when I call getNatalityDataFilters, I should get natality filters', function () {
            var natalityFilters = filterUtils.getNatalityDataFilters();
            expect(natalityFilters[0].key).toEqual('hispanic_origin');
            expect(natalityFilters[1].key).toEqual('state');
            expect(natalityFilters[2].key).toEqual('mother_age_1year_interval');
            expect(natalityFilters[3].key).toEqual('mother_age_5year_interval');
            expect(natalityFilters[4].key).toEqual('race');
            expect(natalityFilters[5].key).toEqual('marital_status');
        });
    });

    describe('test infant mortality filters', function() {
        it('when I call getInfantMortalityDataFilters, I should get infant mortality filters', function () {
            var InfantMortalityData= filterUtils.getInfantMortalityDataFilters();
            expect(InfantMortalityData[0].key).toEqual('year_of_death');
            expect(InfantMortalityData[0].value).toEqual(['2014']);
            expect(InfantMortalityData[1].key).toEqual('sex');
            expect(InfantMortalityData[2].key).toEqual('infant_age_at_death');
            expect(InfantMortalityData[3].key).toEqual('race');
            expect(InfantMortalityData[4].key).toEqual('hispanic_origin');
            expect(InfantMortalityData[5].key).toEqual('mother_age_5_interval');
            expect(InfantMortalityData[6].key).toEqual('marital_status');
            expect(InfantMortalityData[7].key).toEqual('mother_education');
        });
    });

    describe('STD filters -> ', function() {
        it('when I call getSTDDataFilters, I should get all std filters', function () {
            var stdDataFilters= filterUtils.getSTDDataFilters();
            expect(stdDataFilters[0].key).toEqual('current_year');
            expect(stdDataFilters[0].value).toEqual('2015');
            expect(stdDataFilters[0].helpText).toEqual('label.std.help.text.year');
            expect(stdDataFilters[1].key).toEqual('disease');
            expect(stdDataFilters[1].value).toEqual('Chlamydia');
            expect(stdDataFilters[1].helpText).toEqual('label.std.help.text.disease');
            expect(stdDataFilters[2].key).toEqual('state');
            expect(stdDataFilters[2].value).toEqual('National');
            expect(stdDataFilters[2].helpText).toEqual('label.std.help.text.state');
            expect(stdDataFilters[3].key).toEqual('age_group');
            expect(stdDataFilters[3].value).toEqual('All age groups');
            expect(stdDataFilters[3].helpText).toEqual('label.std.help.text.age.group');
            expect(stdDataFilters[3].autoCompleteOptions.length).toEqual(11);
            expect(stdDataFilters[3].autoCompleteOptions[0].key).toEqual("All age groups");
            expect(stdDataFilters[3].autoCompleteOptions[1].key).toEqual("0-14");
            expect(stdDataFilters[3].autoCompleteOptions[2].key).toEqual("15-19");
            expect(stdDataFilters[3].autoCompleteOptions[3].key).toEqual("20-24");
            expect(stdDataFilters[3].autoCompleteOptions[4].key).toEqual("25-29");
            expect(stdDataFilters[3].autoCompleteOptions[5].key).toEqual("30-34");
            expect(stdDataFilters[3].autoCompleteOptions[6].key).toEqual("35-39");
            expect(stdDataFilters[3].autoCompleteOptions[7].key).toEqual("40-44");
            expect(stdDataFilters[3].autoCompleteOptions[8].key).toEqual("45-54");
            expect(stdDataFilters[3].autoCompleteOptions[9].key).toEqual("55-64");
            expect(stdDataFilters[3].autoCompleteOptions[10].key).toEqual("65+");
            expect(stdDataFilters[4].key).toEqual('race');
            expect(stdDataFilters[4].value).toEqual('All races/ethnicities');
            expect(stdDataFilters[4].helpText).toEqual('label.std.help.text.race.ethnicity');
            expect(stdDataFilters[5].key).toEqual('sex');
            expect(stdDataFilters[5].value).toEqual('Both sexes');
            expect(stdDataFilters[5].helpText).toEqual('label.std.help.text.sex');
        });

        it('When I call getAllOptionValues, I should get list of All option values', function(){
            var values = filterUtils.getAllOptionValues();
            expect(values[0]).toEqual("Both sexes");
            expect(values[1]).toEqual("All races/ethnicities");
            expect(values[2]).toEqual("All age groups");
            expect(values[3]).toEqual("National");
        });
    });

    describe('TB filters -> ', function() {
        it('when I call getTBDataFilters, I should get all TB filters', function () {
            var stdDataFilters= filterUtils.getTBDataFilters();
            expect(stdDataFilters[0].key).toEqual('current_year');
            expect(stdDataFilters[0].value).toEqual('2015');
            expect(stdDataFilters[1].key).toEqual('age_group');
            expect(stdDataFilters[2].key).toEqual('race');
            expect(stdDataFilters[2].value).toEqual('All races/ethnicities');
            expect(stdDataFilters[3].key).toEqual('sex');
            expect(stdDataFilters[3].value).toEqual('Both sexes');
            expect(stdDataFilters[4].key).toEqual('state');
            expect(stdDataFilters[4].value).toEqual('National');
            expect(stdDataFilters[5].key).toEqual('transmission');
        });
    });
});<|endoftext|>


<commit_before>var util = require('../util');
var values = require('./util').values;
var url = require('url');
var net = require('net');
var lookup = require('./dns');
var extend = require('util')._extend;
var allowDnsCache = true;
var protocols = ['host', 'req', 'rule', 'res', 'weinre', 'filter', 'disable', 'log', 'referer', 'auth', 'ua', 'urlParams', 'params', 'statusCode', 'redirect', 'method', 'cache',
                 'reqDelay', 'resDelay', 'reqSpeed', 'resSpeed', 'reqType', 'resType', 'reqCookies', 'resCookies', 'reqCors', 'resCors', 'reqHeaders', 'resHeaders',
                 'reqPrepend', 'resPrepend', 'reqBody', 'resBody', 'reqAppend', 'resAppend', 'head', 'reqReplace', 'resReplace', 'reqWrite', 'resWrite', 'css', 'html', 'js'];
var rules = {};
protocols.forEach(function(protocol) {
	rules[protocol] = [];
});

function parse(text) {
	protocols.forEach(function(protocol) {
		rules[protocol] = [];
	});
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^(?:http|ws)s?:\/\//.test(pattern))
			|| (!util.isRegExp(pattern) && util.isRegExp(line[1]))) {
		//supports: operator-uri pattern1 pattern2 ... patternN
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else {
		//supports: pattern operator-uri1 operator-uri2 ... operator-uriN
		line.slice(1).forEach(function(matcher) {
			parseRule(pattern, matcher, raw);
		});
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	var rawPattern = pattern;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var list;
	if (net.isIP(matcher)) {
		list = rules.host;
	} else {
		var protocol = matcher.match(/^([\w\-]+):\/\//);
		list = rules[protocol && protocol[1]] || rules.rule;
	}

	list.push({
		isRegExp: isRegExp,
		protocol: isRegExp ? null : util.getProtocol(pattern),
		pattern: isRegExp ? pattern : formatUrl(pattern),
		matcher: matcher,
		raw: raw,
		isDomain: !isRegExp && util.removeProtocol(rawPattern, true).indexOf('/') == -1,
		rawPattern: rawPattern
	});
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!options.hostname || (!util.isWebsocketProtocol(options.protocol)
			&& !util.isWebProtocol(options.protocol))) {

		return callback(null, null);
	}

	var host = !resolveFilter(_url).host && getRule(_url, rules.host);
	host ? callback(null, util.removeProtocol(host.matcher, true))
			: lookup(options.hostname, callback, allowDnsCache && !resolveDisable(_url).dnsCache);
}

function formatUrl(pattern) {
	var queryString = '';
	var queryIndex = pattern.indexOf('?');
	if (queryIndex != -1) {
		queryString = pattern.substring(queryIndex);
		pattern = pattern.substring(0, queryIndex);
	}
	return (pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern) + queryString;
}

exports.resolveHost = resolveHost;

function getRule(url, list) {
	var rule = _getRule(url, list);
	//支持域名匹配
	if (!rule && (rule = _getRule(url.replace(/^((?:http|ws)s?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), list))
			&& !rule.isDomain) {
		rule = null;
	}
	if (rule) {
		var matcher = rule.matcher;
		var index = matcher.indexOf('://') + 3;
		var protocol = matcher.substring(0, index);
		matcher = matcher.substring(index);
		var key = getKey(matcher);
		if (key) {
			rule.key = key;
		}
		var value = key && values.get(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(matcher)) !== false) {
			rule.value = protocol + value;
		} else if ((value = getPath(matcher)) !== false) {
			rule.path = protocol + value;
			rule.files = getFiles(rule.path);
		}
	}

	return rule;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, list) {
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}

	var _url = url.replace(/[?#].*$/, '');
	for (var i = 0, rule; rule = list[i]; i++) {
		var pattern = rule.pattern;
		var matcher = setProtocol(rule.matcher, url);
		var files = getFiles(matcher);
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				function replaceSubMatcher(url) {
					return url.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					});
				}
				matcher = replaceSubMatcher(matcher);
				var result = extend({
					url: matcher,
					files: files && files.map(function(file) {
						return replaceSubMatcher(file);
					})
				}, rule);
				result.matcher = matcher;
				return result;
			}
		} else if (url.indexOf(pattern = setProtocol(pattern, url)) === 0) {
			var len = pattern.length;
			var hasQuery = pattern.indexOf('?') != -1;
			if (hasQuery || pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
				var filePath = url.substring(len);
				if (hasQuery && filePath) {
					filePath = '?' + filePath;
				}
				return extend({
					files: files && files.map(function(file) {
						return join(file, filePath);
					}),
					url: join(matcher, filePath)
				}, rule);
			}
		}
	}
}

function getFiles(path) {

	return /^x?(file|tpl|jsonp|dust):/.test(path) ? util.removeProtocol(path, true).split('|') : null;
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\' || ch == '?';
}

/**
 * first: xxxx, xxxx?, xxx?xxxx
 * second: ?xxx, xxx?xxxx
 * @param first
 * @param second
 * @returns
 */
function join(first, second) {
	if (!first || !second) {
		return first + second;
	}

	var firstIndex = first.indexOf('?');
	var secondIndex = second.indexOf('?');
	var firstQuery = '';
	var secondQuery = '';

	if (firstIndex != -1) {
		firstQuery = first.substring(firstIndex);
		first = first.substring(0, firstIndex);
	}

	if (secondIndex != -1) {
		secondQuery = second.substring(secondIndex);
		second = second.substring(0, secondIndex);
	}

	var query = firstQuery && secondQuery ? firstQuery + secondQuery.substring(1) : (firstQuery || secondQuery);
	if (second) {
		var lastIndex = first.length - 1;
		var startWithSep = isPathSeparator(second[0]);
		if (isPathSeparator(first[lastIndex])) {
			first = startWithSep ? first.substring(0, lastIndex) + second : first + second;
		} else {
			first = first + (startWithSep ? '' : '/') + second;
		}
	}

	return first + query;
}

function resolveProperties(url, rules) {
	var rule = getRule(url, rules);
	var result = {};
	if (rule) {
		util.getMatcherValue(rule)
				.split('|').forEach(function(action) {
			result[action] = true;
		});
	}

	return result;
}

function resolveFilter(url) {
	return resolveProperties(url, rules.filter);
}

exports.resolveFilter = resolveFilter;

function resolveDisable(url) {
	return resolveProperties(url, rules.disable);
}

exports.resolveDisable = resolveDisable;

exports.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rule;
	var _rules = {};
	var filter = resolveFilter(url);
	protocols.forEach(function(protocol) {
		if ((protocol == 'filter' || !filter[protocol]) && (rule = getRule(url, rules[protocol]))) {
			_rules[protocol] = rule;
		}
	});

	return _rules;
};

exports.disableDnsCache = function() {
	allowDnsCache = false;
};
<commit_msg>reqWriteRaw, resWriteRaw<commit_after>var util = require('../util');
var values = require('./util').values;
var url = require('url');
var net = require('net');
var lookup = require('./dns');
var extend = require('util')._extend;
var allowDnsCache = true;
var protocols = ['host', 'req', 'rule', 'res', 'weinre', 'filter', 'disable', 'log', 'referer', 'auth', 'ua', 'urlParams', 'params', 'statusCode', 'redirect', 'method', 'cache',
                 'reqDelay', 'resDelay', 'reqSpeed', 'resSpeed', 'reqType', 'resType', 'reqCookies', 'resCookies', 'reqCors', 'resCors', 'reqHeaders', 'resHeaders',
                 'reqPrepend', 'resPrepend', 'reqBody', 'resBody', 'reqAppend', 'resAppend', 'head', 'reqReplace', 'resReplace', 'reqWrite', 'resWrite', 'reqWriteRaw', 'resWriteRaw',
                 'css', 'html', 'js'];
var rules = {};
protocols.forEach(function(protocol) {
	rules[protocol] = [];
});

function parse(text) {
	protocols.forEach(function(protocol) {
		rules[protocol] = [];
	});
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^(?:http|ws)s?:\/\//.test(pattern))
			|| (!util.isRegExp(pattern) && util.isRegExp(line[1]))) {
		//supports: operator-uri pattern1 pattern2 ... patternN
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else {
		//supports: pattern operator-uri1 operator-uri2 ... operator-uriN
		line.slice(1).forEach(function(matcher) {
			parseRule(pattern, matcher, raw);
		});
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	var rawPattern = pattern;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var list;
	if (net.isIP(matcher)) {
		list = rules.host;
	} else {
		var protocol = matcher.match(/^([\w\-]+):\/\//);
		list = rules[protocol && protocol[1]] || rules.rule;
	}

	list.push({
		isRegExp: isRegExp,
		protocol: isRegExp ? null : util.getProtocol(pattern),
		pattern: isRegExp ? pattern : formatUrl(pattern),
		matcher: matcher,
		raw: raw,
		isDomain: !isRegExp && util.removeProtocol(rawPattern, true).indexOf('/') == -1,
		rawPattern: rawPattern
	});
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!options.hostname || (!util.isWebsocketProtocol(options.protocol)
			&& !util.isWebProtocol(options.protocol))) {

		return callback(null, null);
	}

	var host = !resolveFilter(_url).host && getRule(_url, rules.host);
	host ? callback(null, util.removeProtocol(host.matcher, true))
			: lookup(options.hostname, callback, allowDnsCache && !resolveDisable(_url).dnsCache);
}

function formatUrl(pattern) {
	var queryString = '';
	var queryIndex = pattern.indexOf('?');
	if (queryIndex != -1) {
		queryString = pattern.substring(queryIndex);
		pattern = pattern.substring(0, queryIndex);
	}
	return (pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern) + queryString;
}

exports.resolveHost = resolveHost;

function getRule(url, list) {
	var rule = _getRule(url, list);
	//支持域名匹配
	if (!rule && (rule = _getRule(url.replace(/^((?:http|ws)s?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), list))
			&& !rule.isDomain) {
		rule = null;
	}
	if (rule) {
		var matcher = rule.matcher;
		var index = matcher.indexOf('://') + 3;
		var protocol = matcher.substring(0, index);
		matcher = matcher.substring(index);
		var key = getKey(matcher);
		if (key) {
			rule.key = key;
		}
		var value = key && values.get(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(matcher)) !== false) {
			rule.value = protocol + value;
		} else if ((value = getPath(matcher)) !== false) {
			rule.path = protocol + value;
			rule.files = getFiles(rule.path);
		}
	}

	return rule;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, list) {
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}

	var _url = url.replace(/[?#].*$/, '');
	for (var i = 0, rule; rule = list[i]; i++) {
		var pattern = rule.pattern;
		var matcher = setProtocol(rule.matcher, url);
		var files = getFiles(matcher);
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				function replaceSubMatcher(url) {
					return url.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					});
				}
				matcher = replaceSubMatcher(matcher);
				var result = extend({
					url: matcher,
					files: files && files.map(function(file) {
						return replaceSubMatcher(file);
					})
				}, rule);
				result.matcher = matcher;
				return result;
			}
		} else if (url.indexOf(pattern = setProtocol(pattern, url)) === 0) {
			var len = pattern.length;
			var hasQuery = pattern.indexOf('?') != -1;
			if (hasQuery || pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
				var filePath = url.substring(len);
				if (hasQuery && filePath) {
					filePath = '?' + filePath;
				}
				return extend({
					files: files && files.map(function(file) {
						return join(file, filePath);
					}),
					url: join(matcher, filePath)
				}, rule);
			}
		}
	}
}

function getFiles(path) {

	return /^x?(file|tpl|jsonp|dust):/.test(path) ? util.removeProtocol(path, true).split('|') : null;
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\' || ch == '?';
}

/**
 * first: xxxx, xxxx?, xxx?xxxx
 * second: ?xxx, xxx?xxxx
 * @param first
 * @param second
 * @returns
 */
function join(first, second) {
	if (!first || !second) {
		return first + second;
	}

	var firstIndex = first.indexOf('?');
	var secondIndex = second.indexOf('?');
	var firstQuery = '';
	var secondQuery = '';

	if (firstIndex != -1) {
		firstQuery = first.substring(firstIndex);
		first = first.substring(0, firstIndex);
	}

	if (secondIndex != -1) {
		secondQuery = second.substring(secondIndex);
		second = second.substring(0, secondIndex);
	}

	var query = firstQuery && secondQuery ? firstQuery + secondQuery.substring(1) : (firstQuery || secondQuery);
	if (second) {
		var lastIndex = first.length - 1;
		var startWithSep = isPathSeparator(second[0]);
		if (isPathSeparator(first[lastIndex])) {
			first = startWithSep ? first.substring(0, lastIndex) + second : first + second;
		} else {
			first = first + (startWithSep ? '' : '/') + second;
		}
	}

	return first + query;
}

function resolveProperties(url, rules) {
	var rule = getRule(url, rules);
	var result = {};
	if (rule) {
		util.getMatcherValue(rule)
				.split('|').forEach(function(action) {
			result[action] = true;
		});
	}

	return result;
}

function resolveFilter(url) {
	return resolveProperties(url, rules.filter);
}

exports.resolveFilter = resolveFilter;

function resolveDisable(url) {
	return resolveProperties(url, rules.disable);
}

exports.resolveDisable = resolveDisable;

exports.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rule;
	var _rules = {};
	var filter = resolveFilter(url);
	protocols.forEach(function(protocol) {
		if ((protocol == 'filter' || !filter[protocol]) && (rule = getRule(url, rules[protocol]))) {
			_rules[protocol] = rule;
		}
	});

	return _rules;
};

exports.disableDnsCache = function() {
	allowDnsCache = false;
};
<|endoftext|>