<commit_before>var inherits = require('util').inherits,
    EventEmitter = require('events').EventEmitter;

var consts = require('./Parser.constants');

for (var i=0,keys=Object.keys(consts),len=keys.length; i<len; ++i)
  global[keys[i]] = consts[keys[i]];

var MAX_WINDOW = Math.pow(2, 32) - 1;

function Channel(info, conn) {
  EventEmitter.call(this);

  var self = this;

  this.type = info.type;
  /*
    incoming and outgoing contain these properties:
    {
      id: undefined,
      window: undefined,
      packetSize: undefined,
      state: 'closed'
    }
  */
  this.incoming = info.incoming;
  this.outgoing = info.outgoing;

  this._conn = conn;
  this._stream = undefined;
  this._callbacks = [];
  this._queue = []; // for buffering outoing messages during key (re-)exchanges

  conn._parser.on('CHANNEL_EOF:' + this.incoming.id, function() {
    this.incoming.state = 'eof';
    self._stream.emit('end');
  });

  conn._parser.on('CHANNEL_CLOSE:' + this.incoming.id, function() {
    this.incoming.state = 'closed';
    if (self.outgoing.state !== 'closed') {
      self.close();
      self._conn.channels.splice(self._conn.channels.indexOf(self.incoming.id), 1);
      var stream = self._stream;
      self._stream = undefined;
      stream.emit('close');
    }
  });

  conn._parser.on('CHANNEL_DATA:' + this.incoming.id, function(data) {
    self._stream.emit('data', data);
  });

  conn._parser.on('CHANNEL_EXTENDED_DATA:' + this.incoming.id,
    function(type, data) {
      type = CHANNEL_EXTENDED_DATATYPE[type].toLowerCase();
      self._stream.emit('data', data, type);
    }
  );

  conn._parser.on('CHANNEL_SUCCESS:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(false);
  });

  conn._parser.on('CHANNEL_FAILURE:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(true);
  });

  conn._parser.on('CHANNEL_REQUEST:' + this.incoming.id, function(info) {
    var err;
    if (info.request === 'exit-status')
      self._stream.emit('exit', info.code);
    else if (info.request === 'exit-signal') {
      self._stream.emit('exit', null, 'SIG' + info.signal, info.coredump,
                        info.description, info.lang);
    } else
      return;
    self.close();
  });

  conn.on('_reexchg', function() {
    self._processQueue();
  });
}

Channel.prototype.eof = function(go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['eof']);
    return false;
  }
  if (this._conn._sock.writable && this.outgoing.state === 'open') {
    // Note: CHANNEL_EOF does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_EOF
      uint32    recipient channel
    */
    var buf = new Buffer(1 + 4);
    this.outgoing.state = 'EOF';
    buf[0] = MESSAGE.CHANNEL_EOF;
    buf.writeUInt32BE(this.outgoing.id, 1, true);
    this._conn._send(buf);
    return true;
  } else
    return false;
};

Channel.prototype.close = function(go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['close']);
    return false;
  }
  if (this.outgoing.state !== 'closed') {
    // Note: CHANNEL_CLOSE does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_CLOSE
      uint32    recipient channel
    */
    var buf = new Buffer(1 + 4);
    buf[0] = MESSAGE.CHANNEL_CLOSE;
    buf.writeUInt32BE(this.outgoing.id, 1, true);
    this.outgoing.state = 'closed';
    this._conn._send(buf);
    return true;
  } else
    return false;
};

Channel.prototype._sendExec = function(cmd, cb, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendExec', cmd, cb]);
    return false;
  }
  // Note: CHANNEL_REQUEST does not consume window space
  /*
    byte      SSH_MSG_CHANNEL_REQUEST
    uint32    recipient channel
    string    "exec"
    boolean   want reply
    string    command
  */
  var cmdlen = (Buffer.isBuffer(cmd) ? cmd.length : Buffer.byteLength(cmd)),
      buf = new Buffer(1 + 4 + 4 + 4 + 1 + 4 + cmdlen);
  buf[0] = MESSAGE.CHANNEL_REQUEST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(4, 5, true);
  buf.write('exec', 9, 4, 'ascii');
  buf[13] = 0;
  buf.writeUInt32BE(cmdlen, 14, true);
  if (Buffer.isBuffer(cmd))
    cmd.copy(buf, 18);
  else
    buf.write(cmd, 18, cmdlen, 'utf8');

  this._stream = new EventEmitter();
  cb(undefined, this._stream);

  this._conn._send(buf);

  return true;
};

Channel.prototype._sendTermSizeChg = function(cols, rows, width, height, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendTermSizeChg', cols, rows, width, height]);
    return false;
  }
  // Note: CHANNEL_REQUEST does not consume window space
  /*
    byte      SSH_MSG_CHANNEL_REQUEST
    uint32    recipient channel
    string    "window-change"
    boolean   FALSE
    uint32    terminal width, columns
    uint32    terminal height, rows
    uint32    terminal width, pixels
    uint32    terminal height, pixels
  */
  var buf = new Buffer(1 + 4 + 4 + 13 + 1 + 4 + 4 + 4 + 4);
  buf[0] = MESSAGE.CHANNEL_REQUEST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(13, 5, true);
  buf.write('window-change', 9, 13, 'ascii');
  buf[22] = 0;
  buf.writeUInt32BE(cols, 23, true);
  buf.writeUInt32BE(rows, 27, true);
  buf.writeUInt32BE(width, 31, true);
  buf.writeUInt32BE(height, 35, true);

  this._conn._send(buf);

  return true;
};

Channel.prototype._sendEnv = function(env, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendEnv', env]);
    return false;
  }
  var keys, buf;
  if (env && (keys = Object.keys(env)).length > 0) {
    // Note: CHANNEL_REQUEST does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_REQUEST
      uint32    recipient channel
      string    "env"
      boolean   want reply
      string    variable name
      string    variable value
    */
    for (var i = 0, klen, vlen, len = keys.length; i < len; ++i) {
      klen = Buffer.byteLength(keys[i]);
      if (Buffer.isBuffer(env[keys[i]]))
        vlen = env[keys[i]].length;
      else
        vlen = Buffer.byteLength(env[keys[i]]);
      buf = new Buffer(1 + 4 + 4 + 3 + 1 + 4 + klen + 4 + vlen);
      buf[0] = MESSAGE.CHANNEL_REQUEST;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(3, 5, true);
      buf.write('env', 9, 3, 'ascii');
      buf[13] = 0;
      buf.writeUInt32BE(klen, 14, true);
      buf.write(keys[i], 18, klen, 'ascii');
      buf.writeUInt32BE(vlen, 18 + klen, true);
      if (Buffer.isBuffer(env[keys[i]]))
        env[keys[i]].copy(buf, 18 + klen + 4);
      else
        buf.write(env[keys[i]], 18 + klen + 4, vlen, 'utf8');
      this._conn._send(buf);
    }
    return true;
  } else
    return false;
};

Channel.prototype._sendData = function(data, extendedType, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendData', data, extendedType]);
    return false;
  }
  var len = data.length, p = 0, buf, sliceLen;
  while (len - p > 0) {
    if (this.outgoing.window === 0)
      this._sendWndAdjust();
    sliceLen = (len - p < this.outgoing.window ? len - p : this.outgoing.window);
    if (extendedType === undefined) {
      /*
        byte      SSH_MSG_CHANNEL_DATA
        uint32    recipient channel
        string    data
      */
      buf = new Buffer(1 + 4 + 4 + sliceLen);
      buf[0] = MESSAGE.CHANNEL_DATA;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(sliceLen, 5, true);
      data.copy(buf, 9, p, p + sliceLen);
    } else {
      /*
        byte      SSH_MSG_CHANNEL_EXTENDED_DATA
        uint32    recipient channel
        uint32    data_type_code
        string    data
      */
      buf = new Buffer(1 + 4 + 4 + 4 + sliceLen);
      buf[0] = MESSAGE.CHANNEL_EXTENDED_DATA;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(extendedType, 5, true);
      buf.writeUInt32BE(sliceLen, 9, true);
      data.copy(buf, 13, p, p + sliceLen);
    }
    p += sliceLen;
    this.outgoing.window -= sliceLen;
    this._conn._send(buf);
  }
  return true;
};

Channel.prototype._sendWndAdjust = function(amt, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendWndAdjust', amt]);
    return false;
  }
  /*
    byte      SSH_MSG_CHANNEL_WINDOW_ADJUST
    uint32    recipient channel
    uint32    bytes to add
  */
  amt = amt || Math.min(MAX_WINDOW, this.outgoing.packetSize);
  var buf = new Buffer(1 + 4 + 4);
  buf[0] = MESSAGE.CHANNEL_WINDOW_ADJUST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(amt, 5, true);
  this._conn._send(buf);
  this.outgoing.window += amt;

  return true;
};

Channel.prototype._processQueue = function() {
  if (!this._conn._sock.writable) {
    this._queue = [];
    return;
  }
  if (this._conn._state === 'authenticated') {
    var paramLen, req;
    while (this._queue.length) {
      req = this._queue.shift();
      paramLen = req.length - 1;
      if (paramLen === 0)
        this[req[0]](true);
      else if (paramLen === 1)
        this[req[0]](req[1], true);
      else if (paramLen === 2)
        this[req[0]](req[1], req[2], true);
      else if (paramLen === 3) {
        this[req[0]](req[1], req[2], req[3], true);
      } else {
        var args = req.slice(1);
        args.push(true);
        this[req[0]].apply(this, args);
      }
    }
  }
};

Channel.MAX_WINDOW = MAX_WINDOW;

module.exports = Channel;
<commit_msg>channel: implement sending signals<commit_after>var inherits = require('util').inherits,
    EventEmitter = require('events').EventEmitter;

var consts = require('./Parser.constants');

for (var i=0,keys=Object.keys(consts),len=keys.length; i<len; ++i)
  global[keys[i]] = consts[keys[i]];

var MAX_WINDOW = Math.pow(2, 32) - 1,
    SIGNALS = ['ABRT', 'ALRM', 'FPE', 'HUP', 'ILL', 'INT', 'KILL', 'PIPE',
               'QUIT', 'SEGV', 'TERM', 'USR1', 'USR2'];

function Channel(info, conn) {
  EventEmitter.call(this);

  var self = this;

  this.type = info.type;
  /*
    incoming and outgoing contain these properties:
    {
      id: undefined,
      window: undefined,
      packetSize: undefined,
      state: 'closed'
    }
  */
  this.incoming = info.incoming;
  this.outgoing = info.outgoing;

  this._conn = conn;
  this._stream = undefined;
  this._callbacks = [];
  this._queue = []; // for buffering outoing messages during key (re-)exchanges

  conn._parser.on('CHANNEL_EOF:' + this.incoming.id, function() {
    this.incoming.state = 'eof';
    self._stream.emit('end');
  });

  conn._parser.on('CHANNEL_CLOSE:' + this.incoming.id, function() {
    this.incoming.state = 'closed';
    if (self.outgoing.state !== 'closed') {
      self.close();
      self._conn.channels.splice(self._conn.channels.indexOf(self.incoming.id), 1);
      var stream = self._stream;
      self._stream = undefined;
      stream.emit('close');
    }
  });

  conn._parser.on('CHANNEL_DATA:' + this.incoming.id, function(data) {
    self._stream.emit('data', data);
  });

  conn._parser.on('CHANNEL_EXTENDED_DATA:' + this.incoming.id,
    function(type, data) {
      type = CHANNEL_EXTENDED_DATATYPE[type].toLowerCase();
      self._stream.emit('data', data, type);
    }
  );

  conn._parser.on('CHANNEL_SUCCESS:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(false);
  });

  conn._parser.on('CHANNEL_FAILURE:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(true);
  });

  conn._parser.on('CHANNEL_REQUEST:' + this.incoming.id, function(info) {
    var err;
    if (info.request === 'exit-status')
      self._stream.emit('exit', info.code);
    else if (info.request === 'exit-signal') {
      self._stream.emit('exit', null, 'SIG' + info.signal, info.coredump,
                        info.description, info.lang);
    } else
      return;
    self.close();
  });

  conn.on('_reexchg', function() {
    self._processQueue();
  });
}

Channel.prototype.eof = function(go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['eof']);
    return false;
  }
  if (this._conn._sock.writable && this.outgoing.state === 'open') {
    // Note: CHANNEL_EOF does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_EOF
      uint32    recipient channel
    */
    var buf = new Buffer(1 + 4);
    this.outgoing.state = 'EOF';
    buf[0] = MESSAGE.CHANNEL_EOF;
    buf.writeUInt32BE(this.outgoing.id, 1, true);
    this._conn._send(buf);
    return true;
  } else
    return false;
};

Channel.prototype.close = function(go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['close']);
    return false;
  }
  if (this.outgoing.state !== 'closed') {
    // Note: CHANNEL_CLOSE does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_CLOSE
      uint32    recipient channel
    */
    var buf = new Buffer(1 + 4);
    buf[0] = MESSAGE.CHANNEL_CLOSE;
    buf.writeUInt32BE(this.outgoing.id, 1, true);
    this.outgoing.state = 'closed';
    this._conn._send(buf);
    return true;
  } else
    return false;
};

Channel.prototype._sendExec = function(cmd, cb, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendExec', cmd, cb]);
    return false;
  }
  // Note: CHANNEL_REQUEST does not consume window space
  /*
    byte      SSH_MSG_CHANNEL_REQUEST
    uint32    recipient channel
    string    "exec"
    boolean   want reply
    string    command
  */
  var cmdlen = (Buffer.isBuffer(cmd) ? cmd.length : Buffer.byteLength(cmd)),
      buf = new Buffer(1 + 4 + 4 + 4 + 1 + 4 + cmdlen);
  buf[0] = MESSAGE.CHANNEL_REQUEST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(4, 5, true);
  buf.write('exec', 9, 4, 'ascii');
  buf[13] = 0;
  buf.writeUInt32BE(cmdlen, 14, true);
  if (Buffer.isBuffer(cmd))
    cmd.copy(buf, 18);
  else
    buf.write(cmd, 18, cmdlen, 'utf8');

  this._stream = new EventEmitter();
  cb(undefined, this._stream);

  this._conn._send(buf);

  return true;
};

Channel.prototype._sendTermSizeChg = function(cols, rows, width, height, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendTermSizeChg', cols, rows, width, height]);
    return false;
  }
  // Note: CHANNEL_REQUEST does not consume window space
  /*
    byte      SSH_MSG_CHANNEL_REQUEST
    uint32    recipient channel
    string    "window-change"
    boolean   FALSE
    uint32    terminal width, columns
    uint32    terminal height, rows
    uint32    terminal width, pixels
    uint32    terminal height, pixels
  */
  var buf = new Buffer(1 + 4 + 4 + 13 + 1 + 4 + 4 + 4 + 4);
  buf[0] = MESSAGE.CHANNEL_REQUEST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(13, 5, true);
  buf.write('window-change', 9, 13, 'ascii');
  buf[22] = 0;
  buf.writeUInt32BE(cols, 23, true);
  buf.writeUInt32BE(rows, 27, true);
  buf.writeUInt32BE(width, 31, true);
  buf.writeUInt32BE(height, 35, true);

  this._conn._send(buf);

  return true;
};

Channel.prototype._sendSignal = function(signal, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendSignal', signal]);
    return false;
  }
  // Note: CHANNEL_REQUEST does not consume window space
  /*
    byte      SSH_MSG_CHANNEL_REQUEST
    uint32    recipient channel
    string    "signal"
    boolean   FALSE
    string    signal name (without the "SIG" prefix)
  */
  signal = signal.toUpperCase();
  if (signal.length > 3
      && signal[0] === 'S' && signal[1] === 'I' && signal[2] === 'G')
    signal = signal.substr(3);
  if (~SIGNALS.indexOf(signal))
    throw new Error('Invalid signal: ' + signal);
  var signalLen = signal.length,
      buf = new Buffer(1 + 4 + 4 + 6 + 1 + 4 + );
  buf[0] = MESSAGE.CHANNEL_REQUEST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(6, 5, true);
  buf.write('signal', 9, 6, 'ascii');
  buf[15] = 0;
  buf.writeUInt32BE(signalLen, 16, true);
  buf.write(signal, 20, signalLen, 'ascii');

  this._conn._send(buf);

  return true;
};

Channel.prototype._sendEnv = function(env, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendEnv', env]);
    return false;
  }
  var keys, buf;
  if (env && (keys = Object.keys(env)).length > 0) {
    // Note: CHANNEL_REQUEST does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_REQUEST
      uint32    recipient channel
      string    "env"
      boolean   want reply
      string    variable name
      string    variable value
    */
    for (var i = 0, klen, vlen, len = keys.length; i < len; ++i) {
      klen = Buffer.byteLength(keys[i]);
      if (Buffer.isBuffer(env[keys[i]]))
        vlen = env[keys[i]].length;
      else
        vlen = Buffer.byteLength(env[keys[i]]);
      buf = new Buffer(1 + 4 + 4 + 3 + 1 + 4 + klen + 4 + vlen);
      buf[0] = MESSAGE.CHANNEL_REQUEST;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(3, 5, true);
      buf.write('env', 9, 3, 'ascii');
      buf[13] = 0;
      buf.writeUInt32BE(klen, 14, true);
      buf.write(keys[i], 18, klen, 'ascii');
      buf.writeUInt32BE(vlen, 18 + klen, true);
      if (Buffer.isBuffer(env[keys[i]]))
        env[keys[i]].copy(buf, 18 + klen + 4);
      else
        buf.write(env[keys[i]], 18 + klen + 4, vlen, 'utf8');
      this._conn._send(buf);
    }
    return true;
  } else
    return false;
};

Channel.prototype._sendData = function(data, extendedType, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendData', data, extendedType]);
    return false;
  }
  var len = data.length, p = 0, buf, sliceLen;
  while (len - p > 0) {
    if (this.outgoing.window === 0)
      this._sendWndAdjust();
    sliceLen = (len - p < this.outgoing.window ? len - p : this.outgoing.window);
    if (extendedType === undefined) {
      /*
        byte      SSH_MSG_CHANNEL_DATA
        uint32    recipient channel
        string    data
      */
      buf = new Buffer(1 + 4 + 4 + sliceLen);
      buf[0] = MESSAGE.CHANNEL_DATA;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(sliceLen, 5, true);
      data.copy(buf, 9, p, p + sliceLen);
    } else {
      /*
        byte      SSH_MSG_CHANNEL_EXTENDED_DATA
        uint32    recipient channel
        uint32    data_type_code
        string    data
      */
      buf = new Buffer(1 + 4 + 4 + 4 + sliceLen);
      buf[0] = MESSAGE.CHANNEL_EXTENDED_DATA;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(extendedType, 5, true);
      buf.writeUInt32BE(sliceLen, 9, true);
      data.copy(buf, 13, p, p + sliceLen);
    }
    p += sliceLen;
    this.outgoing.window -= sliceLen;
    this._conn._send(buf);
  }
  return true;
};

Channel.prototype._sendWndAdjust = function(amt, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendWndAdjust', amt]);
    return false;
  }
  /*
    byte      SSH_MSG_CHANNEL_WINDOW_ADJUST
    uint32    recipient channel
    uint32    bytes to add
  */
  amt = amt || Math.min(MAX_WINDOW, this.outgoing.packetSize);
  var buf = new Buffer(1 + 4 + 4);
  buf[0] = MESSAGE.CHANNEL_WINDOW_ADJUST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(amt, 5, true);
  this._conn._send(buf);
  this.outgoing.window += amt;

  return true;
};

Channel.prototype._processQueue = function() {
  if (!this._conn._sock.writable) {
    this._queue = [];
    return;
  }
  if (this._conn._state === 'authenticated') {
    var paramLen, req;
    while (this._queue.length) {
      req = this._queue.shift();
      paramLen = req.length - 1;
      if (paramLen === 0)
        this[req[0]](true);
      else if (paramLen === 1)
        this[req[0]](req[1], true);
      else if (paramLen === 2)
        this[req[0]](req[1], req[2], true);
      else if (paramLen === 3) {
        this[req[0]](req[1], req[2], req[3], true);
      } else {
        var args = req.slice(1);
        args.push(true);
        this[req[0]].apply(this, args);
      }
    }
  }
};

Channel.MAX_WINDOW = MAX_WINDOW;

module.exports = Channel;
<|endoftext|>


<commit_before>var inherits = require('util').inherits,
    EventEmitter = require('events').EventEmitter;

var consts = require('./Parser.constants');

for (var i=0,keys=Object.keys(consts),len=keys.length; i<len; ++i)
  global[keys[i]] = consts[keys[i]];

var MAX_WINDOW = Math.pow(2, 32) - 1;

function Channel(info, conn) {
  EventEmitter.call(this);

  var self = this;

  this.type = info.type;
  /*
    incoming and outgoing contain these properties:
    {
      id: undefined,
      window: undefined,
      packetSize: undefined,
      state: 'closed'
    }
  */
  this.incoming = info.incoming;
  this.outgoing = info.outgoing;

  this._conn = conn;
  this._stream = undefined;
  this._callbacks = [];
  this._queue = []; // for buffering outoing messages during key (re-)exchanges

  conn._parser.on('CHANNEL_EOF:' + this.incoming.id, function() {
    self._stream.emit('end');
  });

  conn._parser.on('CHANNEL_CLOSE:' + this.incoming.id, function() {
    if (self.outgoing.state !== 'closed') {
      self.close();
      self._conn.channels.splice(self._conn.channels.indexOf(self.incoming.id), 1);
      var stream = self._stream;
      self._stream = undefined;
      stream.emit('close');
    }
  });

  conn._parser.on('CHANNEL_DATA:' + this.incoming.id, function(data) {
    self._stream.emit('data', data);
  });

  conn._parser.on('CHANNEL_EXTENDED_DATA:' + this.incoming.id,
    function(type, data) {
      type = CHANNEL_EXTENDED_DATATYPE[type].toLowerCase();
      self._stream.emit('data', data, type);
    }
  );

  conn._parser.on('CHANNEL_SUCCESS:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(false);
  });

  conn._parser.on('CHANNEL_FAILURE:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(true);
  });

  conn._parser.on('CHANNEL_REQUEST:' + this.incoming.id, function(info) {
    var err;
    if (info.request === 'exit-status')
      self._stream.emit('exit', info.code);
    else if (info.request === 'exit-signal') {
      self._stream.emit('exit', null, 'SIG' + info.signal, info.coredump,
                        info.description, info.lang);
    } else
      return;
    self.close();
  });

  conn.on('_reexchg', function() {
    self._processQueue();
  });
}

Channel.prototype.eof = function(go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['eof']);
    return false;
  }
  if (this._conn._sock.writable && this.outgoing.state === 'open') {
    // Note: CHANNEL_EOF does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_EOF
      uint32    recipient channel
    */
    var buf = new Buffer(1 + 4);
    this.outgoing.state = 'EOF';
    buf[0] = MESSAGE.CHANNEL_EOF;
    buf.writeUInt32BE(this.outgoing.id, 1, true);
    this._conn._send(buf);
    return true;
  } else
    return false;
};

Channel.prototype.close = function(go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['close']);
    return false;
  }
  if (this.outgoing.state !== 'closed') {
    // Note: CHANNEL_CLOSE does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_CLOSE
      uint32    recipient channel
    */
    var buf = new Buffer(1 + 4);
    buf[0] = MESSAGE.CHANNEL_CLOSE;
    buf.writeUInt32BE(this.outgoing.id, 1, true);
    this.outgoing.state = 'closed';
    this._conn._send(buf);
    return true;
  } else
    return false;
};

Channel.prototype._sendExec = function(cmd, cb, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendExec', cmd, cb]);
    return false;
  }
  // Note: CHANNEL_REQUEST does not consume window space
  /*
    byte      SSH_MSG_CHANNEL_REQUEST
    uint32    recipient channel
    string    "exec"
    boolean   want reply
    string    command
  */
  var cmdlen = (Buffer.isBuffer(cmd) ? cmd.length : Buffer.byteLength(cmd)),
      buf = new Buffer(1 + 4 + 4 + 4 + 1 + 4 + cmdlen);
  buf[0] = MESSAGE.CHANNEL_REQUEST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(4, 5, true);
  buf.write('exec', 9, 4, 'ascii');
  buf[13] = 0;
  buf.writeUInt32BE(cmdlen, 14, true);
  if (Buffer.isBuffer(cmd))
    cmd.copy(buf, 18);
  else
    buf.write(cmd, 18, cmdlen, 'utf8');

  this._stream = new EventEmitter();
  cb(undefined, this._stream);

  this._conn._send(buf);

  return true;
};

Channel.prototype._sendEnv = function(env, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendEnv', env]);
    return false;
  }
  var keys, buf;
  if (env && (keys = Object.keys(env)).length > 0) {
    // Note: CHANNEL_REQUEST does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_REQUEST
      uint32    recipient channel
      string    "env"
      boolean   want reply
      string    variable name
      string    variable value
    */
    for (var i = 0, klen, vlen, len = keys.length; i < len; ++i) {
      klen = Buffer.byteLength(keys[i]);
      if (Buffer.isBuffer(env[keys[i]]))
        vlen = env[keys[i]].length;
      else
        vlen = Buffer.byteLength(env[keys[i]]);
      buf = new Buffer(1 + 4 + 4 + 3 + 1 + 4 + klen + 4 + vlen);
      buf[0] = MESSAGE.CHANNEL_REQUEST;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(3, 5, true);
      buf.write('env', 9, 3, 'ascii');
      buf[13] = 0;
      buf.writeUInt32BE(klen, 14, true);
      buf.write(keys[i], 18, klen, 'ascii');
      buf.writeUInt32BE(vlen, 18 + klen, true);
      if (Buffer.isBuffer(env[keys[i]]))
        env[keys[i]].copy(buf, 18 + klen + 4);
      else
        buf.write(env[keys[i]], 18 + klen + 4, vlen, 'utf8');
      this._conn._send(buf);
    }
    return true;
  } else
    return false;
};

Channel.prototype._sendData = function(data, extendedType, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendData', data, extendedType]);
    return false;
  }
  var len = data.length, p = 0, buf, sliceLen;
  while (len - p > 0) {
    if (this.outgoing.window === 0)
      this._sendWndAdjust();
    sliceLen = (len - p < this.outgoing.window ? len - p : this.outgoing.window);
    if (extendedType === undefined) {
      /*
        byte      SSH_MSG_CHANNEL_DATA
        uint32    recipient channel
        string    data
      */
      buf = new Buffer(1 + 4 + 4 + sliceLen);
      buf[0] = MESSAGE.CHANNEL_DATA;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(sliceLen, 5, true);
      data.copy(buf, 9, p, p + sliceLen);
    } else {
      /*
        byte      SSH_MSG_CHANNEL_EXTENDED_DATA
        uint32    recipient channel
        uint32    data_type_code
        string    data
      */
      buf = new Buffer(1 + 4 + 4 + 4 + sliceLen);
      buf[0] = MESSAGE.CHANNEL_EXTENDED_DATA;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(extendedType, 5, true);
      buf.writeUInt32BE(sliceLen, 9, true);
      data.copy(buf, 13, p, p + sliceLen);
    }
    p += sliceLen;
    this.outgoing.window -= sliceLen;
    this._conn._send(buf);
  }
  return true;
};

Channel.prototype._sendWndAdjust = function(amt, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendWndAdjust', amt]);
    return false;
  }
  /*
    byte      SSH_MSG_CHANNEL_WINDOW_ADJUST
    uint32    recipient channel
    uint32    bytes to add
  */
  amt = amt || Math.min(MAX_WINDOW, this.outgoing.packetSize);
  var buf = new Buffer(1 + 4 + 4);
  buf[0] = MESSAGE.CHANNEL_WINDOW_ADJUST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(amt, 5, true);
  this._conn._send(buf);
  this.outgoing.window += amt;

  return true;
};

Channel.prototype._processQueue = function() {
  if (!this._conn._sock.writable) {
    this._queue = [];
    return;
  }
  if (this._conn._state === 'authenticated') {
    var paramLen, req;
    while (this._queue.length) {
      req = this._queue.shift();
      paramLen = req.length - 1;
      if (paramLen === 0)
        this[req[0]](true);
      else if (paramLen === 1)
        this[req[0]](req[1], true);
      else if (paramLen === 2)
        this[req[0]](req[1], req[2], true);
      else if (paramLen === 3) {
        this[req[0]](req[1], req[2], req[3], true);
      } else {
        var args = req.slice(1);
        args.push(true);
        this[req[0]].apply(this, args);
      }
    }
  }
};

Channel.MAX_WINDOW = MAX_WINDOW;

module.exports = Channel;
<commit_msg>channel: set incoming states appropriately<commit_after>var inherits = require('util').inherits,
    EventEmitter = require('events').EventEmitter;

var consts = require('./Parser.constants');

for (var i=0,keys=Object.keys(consts),len=keys.length; i<len; ++i)
  global[keys[i]] = consts[keys[i]];

var MAX_WINDOW = Math.pow(2, 32) - 1;

function Channel(info, conn) {
  EventEmitter.call(this);

  var self = this;

  this.type = info.type;
  /*
    incoming and outgoing contain these properties:
    {
      id: undefined,
      window: undefined,
      packetSize: undefined,
      state: 'closed'
    }
  */
  this.incoming = info.incoming;
  this.outgoing = info.outgoing;

  this._conn = conn;
  this._stream = undefined;
  this._callbacks = [];
  this._queue = []; // for buffering outoing messages during key (re-)exchanges

  conn._parser.on('CHANNEL_EOF:' + this.incoming.id, function() {
    this.incoming.state = 'eof';
    self._stream.emit('end');
  });

  conn._parser.on('CHANNEL_CLOSE:' + this.incoming.id, function() {
    this.incoming.state = 'closed';
    if (self.outgoing.state !== 'closed') {
      self.close();
      self._conn.channels.splice(self._conn.channels.indexOf(self.incoming.id), 1);
      var stream = self._stream;
      self._stream = undefined;
      stream.emit('close');
    }
  });

  conn._parser.on('CHANNEL_DATA:' + this.incoming.id, function(data) {
    self._stream.emit('data', data);
  });

  conn._parser.on('CHANNEL_EXTENDED_DATA:' + this.incoming.id,
    function(type, data) {
      type = CHANNEL_EXTENDED_DATATYPE[type].toLowerCase();
      self._stream.emit('data', data, type);
    }
  );

  conn._parser.on('CHANNEL_SUCCESS:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(false);
  });

  conn._parser.on('CHANNEL_FAILURE:' + this.incoming.id, function() {
    if (self._callbacks.length)
      self._callbacks.shift()(true);
  });

  conn._parser.on('CHANNEL_REQUEST:' + this.incoming.id, function(info) {
    var err;
    if (info.request === 'exit-status')
      self._stream.emit('exit', info.code);
    else if (info.request === 'exit-signal') {
      self._stream.emit('exit', null, 'SIG' + info.signal, info.coredump,
                        info.description, info.lang);
    } else
      return;
    self.close();
  });

  conn.on('_reexchg', function() {
    self._processQueue();
  });
}

Channel.prototype.eof = function(go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['eof']);
    return false;
  }
  if (this._conn._sock.writable && this.outgoing.state === 'open') {
    // Note: CHANNEL_EOF does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_EOF
      uint32    recipient channel
    */
    var buf = new Buffer(1 + 4);
    this.outgoing.state = 'EOF';
    buf[0] = MESSAGE.CHANNEL_EOF;
    buf.writeUInt32BE(this.outgoing.id, 1, true);
    this._conn._send(buf);
    return true;
  } else
    return false;
};

Channel.prototype.close = function(go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['close']);
    return false;
  }
  if (this.outgoing.state !== 'closed') {
    // Note: CHANNEL_CLOSE does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_CLOSE
      uint32    recipient channel
    */
    var buf = new Buffer(1 + 4);
    buf[0] = MESSAGE.CHANNEL_CLOSE;
    buf.writeUInt32BE(this.outgoing.id, 1, true);
    this.outgoing.state = 'closed';
    this._conn._send(buf);
    return true;
  } else
    return false;
};

Channel.prototype._sendExec = function(cmd, cb, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendExec', cmd, cb]);
    return false;
  }
  // Note: CHANNEL_REQUEST does not consume window space
  /*
    byte      SSH_MSG_CHANNEL_REQUEST
    uint32    recipient channel
    string    "exec"
    boolean   want reply
    string    command
  */
  var cmdlen = (Buffer.isBuffer(cmd) ? cmd.length : Buffer.byteLength(cmd)),
      buf = new Buffer(1 + 4 + 4 + 4 + 1 + 4 + cmdlen);
  buf[0] = MESSAGE.CHANNEL_REQUEST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(4, 5, true);
  buf.write('exec', 9, 4, 'ascii');
  buf[13] = 0;
  buf.writeUInt32BE(cmdlen, 14, true);
  if (Buffer.isBuffer(cmd))
    cmd.copy(buf, 18);
  else
    buf.write(cmd, 18, cmdlen, 'utf8');

  this._stream = new EventEmitter();
  cb(undefined, this._stream);

  this._conn._send(buf);

  return true;
};

Channel.prototype._sendEnv = function(env, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendEnv', env]);
    return false;
  }
  var keys, buf;
  if (env && (keys = Object.keys(env)).length > 0) {
    // Note: CHANNEL_REQUEST does not consume window space
    /*
      byte      SSH_MSG_CHANNEL_REQUEST
      uint32    recipient channel
      string    "env"
      boolean   want reply
      string    variable name
      string    variable value
    */
    for (var i = 0, klen, vlen, len = keys.length; i < len; ++i) {
      klen = Buffer.byteLength(keys[i]);
      if (Buffer.isBuffer(env[keys[i]]))
        vlen = env[keys[i]].length;
      else
        vlen = Buffer.byteLength(env[keys[i]]);
      buf = new Buffer(1 + 4 + 4 + 3 + 1 + 4 + klen + 4 + vlen);
      buf[0] = MESSAGE.CHANNEL_REQUEST;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(3, 5, true);
      buf.write('env', 9, 3, 'ascii');
      buf[13] = 0;
      buf.writeUInt32BE(klen, 14, true);
      buf.write(keys[i], 18, klen, 'ascii');
      buf.writeUInt32BE(vlen, 18 + klen, true);
      if (Buffer.isBuffer(env[keys[i]]))
        env[keys[i]].copy(buf, 18 + klen + 4);
      else
        buf.write(env[keys[i]], 18 + klen + 4, vlen, 'utf8');
      this._conn._send(buf);
    }
    return true;
  } else
    return false;
};

Channel.prototype._sendData = function(data, extendedType, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendData', data, extendedType]);
    return false;
  }
  var len = data.length, p = 0, buf, sliceLen;
  while (len - p > 0) {
    if (this.outgoing.window === 0)
      this._sendWndAdjust();
    sliceLen = (len - p < this.outgoing.window ? len - p : this.outgoing.window);
    if (extendedType === undefined) {
      /*
        byte      SSH_MSG_CHANNEL_DATA
        uint32    recipient channel
        string    data
      */
      buf = new Buffer(1 + 4 + 4 + sliceLen);
      buf[0] = MESSAGE.CHANNEL_DATA;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(sliceLen, 5, true);
      data.copy(buf, 9, p, p + sliceLen);
    } else {
      /*
        byte      SSH_MSG_CHANNEL_EXTENDED_DATA
        uint32    recipient channel
        uint32    data_type_code
        string    data
      */
      buf = new Buffer(1 + 4 + 4 + 4 + sliceLen);
      buf[0] = MESSAGE.CHANNEL_EXTENDED_DATA;
      buf.writeUInt32BE(this.outgoing.id, 1, true);
      buf.writeUInt32BE(extendedType, 5, true);
      buf.writeUInt32BE(sliceLen, 9, true);
      data.copy(buf, 13, p, p + sliceLen);
    }
    p += sliceLen;
    this.outgoing.window -= sliceLen;
    this._conn._send(buf);
  }
  return true;
};

Channel.prototype._sendWndAdjust = function(amt, go) {
  if (!this._conn._sock.writable)
    return false;
  else if (!go && this._conn._state !== 'authenticated') {
    this._enqueue(['_sendWndAdjust', amt]);
    return false;
  }
  /*
    byte      SSH_MSG_CHANNEL_WINDOW_ADJUST
    uint32    recipient channel
    uint32    bytes to add
  */
  amt = amt || Math.min(MAX_WINDOW, this.outgoing.packetSize);
  var buf = new Buffer(1 + 4 + 4);
  buf[0] = MESSAGE.CHANNEL_WINDOW_ADJUST;
  buf.writeUInt32BE(this.outgoing.id, 1, true);
  buf.writeUInt32BE(amt, 5, true);
  this._conn._send(buf);
  this.outgoing.window += amt;

  return true;
};

Channel.prototype._processQueue = function() {
  if (!this._conn._sock.writable) {
    this._queue = [];
    return;
  }
  if (this._conn._state === 'authenticated') {
    var paramLen, req;
    while (this._queue.length) {
      req = this._queue.shift();
      paramLen = req.length - 1;
      if (paramLen === 0)
        this[req[0]](true);
      else if (paramLen === 1)
        this[req[0]](req[1], true);
      else if (paramLen === 2)
        this[req[0]](req[1], req[2], true);
      else if (paramLen === 3) {
        this[req[0]](req[1], req[2], req[3], true);
      } else {
        var args = req.slice(1);
        args.push(true);
        this[req[0]].apply(this, args);
      }
    }
  }
};

Channel.MAX_WINDOW = MAX_WINDOW;

module.exports = Channel;
<|endoftext|>


<commit_before>
var terraformer = require('terraformer');
var terraformerParser = require('terraformer-arcgis-parser');

exports.fromCSV = function( csv, callback ){
  var geojson = {type: 'FeatureCollection', features: []};
  var feature, headers, cols;
  var lat = null,
    lon = null;
  csv.forEach(function(row,i){

    if (i === 0){
      headers = row;
      // search a whitelist of lat longs to try to build a geom
      headers.forEach(function(h,i){
        switch (h.toLowerCase()){
          case 'lat':
          case 'latitude':
          case 'latitude_deg':
          case 'y':
            lat = i;
            break;
          case 'lon':
          case 'longitude':
          case 'longitude_deg':
          case 'long':
          case 'x':
            lon = i+'';
            break;
        }
      });

    } else {
      feature = {type: 'Feature', id: i, properties: {}, geometry:null };
      row.forEach(function(col, j){
        feature.properties[headers[j]] = ( !isNaN(col) ) ? parseFloat(col) : col;
      });
      if ( lat && lon ){
        feature.geometry = {
          type: 'Point',
          coordinates: [parseFloat(row[parseInt(lon)]), parseFloat(row[parseInt(lat)])]
        };
      }
      geojson.features.push(feature);
    }
  });
  callback(null, geojson);
};

exports.fromEsri = function( fields, json, callback ){

    var dateFields = [];

    fields.forEach(function(f,i){
      if ( f.type == 'esriFieldTypeDate'){
        dateFields.push(f.name);
      }
    });

    // use terraformer to convert esri json to geojson
    var geojson = {type: 'FeatureCollection', features: []};
    var feature, newFeature;

    json.features.forEach(function(f, i){
      try {
        // if we have attributes we need to remove special chars for shp limitations
        if ( f.attributes ){
          var attr, attrNew, match, regex = new RegExp(/\.|\(|\)/g);
          for ( attr in f.attributes ){
            // if we have a special char in the field name remove it
            match = attr.match( regex );
            if ( match && match.length ){
              attrNew = attr.replace(regex, '');
              f.attributes[attrNew] = f.attributes[attr];
              delete f.attributes[attr];
            }
          }
        }
        if ( f.geometry ){
          feature = terraformerParser.parse( f );

          // build a new feature
          // 'feature' has bboxes we dont want and 'delete' is slow
          newFeature = {
            type: 'Feature',
            id: feature.id,
            properties: feature.properties,
            geometry: {
              type: feature.geometry.type,
              coordinates: feature.geometry.coordinates
            }
          };

          // this sort of sucks
          // but we need to enforce geometry types in postgis
          // we cant turn multi geoms in to signular geoms so we have to force multi
          if (newFeature.geometry && newFeature.geometry.type == 'LineString'){
            newFeature.geometry.type = 'MultiLineString';
            newFeature.geometry.coordinates = [ newFeature.geometry.coordinates ];
          } else if (newFeature.geometry && newFeature.geometry.type == 'Polygon'){
            newFeature.geometry.type = 'MultiPolygon';
            newFeature.geometry.coordinates = [ newFeature.geometry.coordinates ];
          }

          if (!newFeature.id) {
            newFeature.id = i+1;
          }
        } else {
          newFeature = {
            id: i+1,
            properties: f.attributes,
            type: 'Feature',
            geometry: null
          };
        }

        if ( dateFields.length ){
          dateFields.forEach(function(d,i){
            if ( newFeature.properties[d] ){
              newFeature.properties[d] = new Date(newFeature.properties[d]).toISOString();
            }
          });
        }
        geojson.features.push( newFeature );
      } catch (e){
        if ( dateFields.length ){
          dateFields.forEach(function(d,i){
            if ( newFeature.attributes[d] ){
              feature.attributes[d] = new Date(feature.attributes[d]).toISOString();
            }
          });
        }
        newFeature = {
            type: 'Feature',
            id: i+1,
            properties: f.attributes,
            geometry: null
        };
        geojson.features.push( newFeature );
        console.log('error parsing feature', e, f);
      }
    });
    callback(null, geojson);
  };
<commit_msg>fixing csv geoms as points<commit_after>
var terraformer = require('terraformer');
var terraformerParser = require('terraformer-arcgis-parser');

exports.fromCSV = function( csv, callback ){
  var geojson = {type: 'FeatureCollection', features: []};
  var feature, headers, cols;
  var lat = null,
    lon = null;
  csv.forEach(function(row,i){

    if (i === 0){
      headers = row;
      // search a whitelist of lat longs to try to build a geom
      headers.forEach(function(h,i){
        switch (h.toLowerCase()){
          case 'lat':
          case 'latitude':
          case 'latitude_deg':
          case 'y':
            lat = i;
            break;
          case 'lon':
          case 'longitude':
          case 'longitude_deg':
          case 'long':
          case 'x':
            lon = i+'';
            break;
        }
      });

    } else {
      feature = {type: 'Feature', id: i, properties: {}, geometry:null };
      row.forEach(function(col, j){
        feature.properties[headers[j]] = ( !isNaN(col) ) ? parseFloat(col) : col;
      });
      if ( lat && lon ){
        feature.geometry = {
          type: 'MultiPoint',
          coordinates: [[parseFloat(row[parseInt(lon)]), parseFloat(row[parseInt(lat)])]]
        };
      }
      geojson.features.push(feature);
    }
  });
  callback(null, geojson);
};

exports.fromEsri = function( fields, json, callback ){

    var dateFields = [];

    fields.forEach(function(f,i){
      if ( f.type == 'esriFieldTypeDate'){
        dateFields.push(f.name);
      }
    });

    // use terraformer to convert esri json to geojson
    var geojson = {type: 'FeatureCollection', features: []};
    var feature, newFeature;

    json.features.forEach(function(f, i){
      try {
        // if we have attributes we need to remove special chars for shp limitations
        if ( f.attributes ){
          var attr, attrNew, match, regex = new RegExp(/\.|\(|\)/g);
          for ( attr in f.attributes ){
            // if we have a special char in the field name remove it
            match = attr.match( regex );
            if ( match && match.length ){
              attrNew = attr.replace(regex, '');
              f.attributes[attrNew] = f.attributes[attr];
              delete f.attributes[attr];
            }
          }
        }
        if ( f.geometry ){
          feature = terraformerParser.parse( f );

          // build a new feature
          // 'feature' has bboxes we dont want and 'delete' is slow
          newFeature = {
            type: 'Feature',
            id: feature.id,
            properties: feature.properties,
            geometry: {
              type: feature.geometry.type,
              coordinates: feature.geometry.coordinates
            }
          };

          // this sort of sucks
          // but we need to enforce geometry types in postgis
          // we cant turn multi geoms in to signular geoms so we have to force multi
          if (newFeature.geometry && newFeature.geometry.type == 'LineString'){
            newFeature.geometry.type = 'MultiLineString';
            newFeature.geometry.coordinates = [ newFeature.geometry.coordinates ];
          } else if (newFeature.geometry && newFeature.geometry.type == 'Polygon'){
            newFeature.geometry.type = 'MultiPolygon';
            newFeature.geometry.coordinates = [ newFeature.geometry.coordinates ];
          } else if ( newFeature.geometry && newFeature.geometry.type == 'Point' ){
            newFeature.geometry.type = 'MultiPoint';
            newFeature.geometry.coordinates = [[ newFeature.geometry.coordinates ]];
          }

          if (!newFeature.id) {
            newFeature.id = i+1;
          }
        } else {
          newFeature = {
            id: i+1,
            properties: f.attributes,
            type: 'Feature',
            geometry: null
          };
        }

        if ( dateFields.length ){
          dateFields.forEach(function(d,i){
            if ( newFeature.properties[d] ){
              newFeature.properties[d] = new Date(newFeature.properties[d]).toISOString();
            }
          });
        }
        geojson.features.push( newFeature );
      } catch (e){
        if ( dateFields.length ){
          dateFields.forEach(function(d,i){
            if ( newFeature.attributes[d] ){
              feature.attributes[d] = new Date(feature.attributes[d]).toISOString();
            }
          });
        }
        newFeature = {
            type: 'Feature',
            id: i+1,
            properties: f.attributes,
            geometry: null
        };
        geojson.features.push( newFeature );
        console.log('error parsing feature', e, f);
      }
    });
    callback(null, geojson);
  };
<|endoftext|>


<commit_before>// **N3Store** objects store N3 triples by graph in memory.

var expandPrefixedName = require('./N3Util').expandPrefixedName;

// ## Constructor
function N3Store(triples, options) {
  if (!(this instanceof N3Store))
    return new N3Store(triples, options);

  // The number of triples is initially zero.
  this._size = 0;
  // `_graphs` contains subject, predicate, and object indexes per graph.
  this._graphs = Object.create(null);
  // `_entities` maps entities such as `http://xmlns.com/foaf/0.1/name` to numbers.
  // This saves memory, since only the numbers have to be stored in `_graphs`.
  this._entities = Object.create(null);
  this._entities['><'] = 0; // Dummy entry, so the first actual key is non-zero
  this._entityCount = 0;
  // `_blankNodeIndex` is the index of the last created blank node that was automatically named
  this._blankNodeIndex = 0;

  // Shift parameters if `triples` is not given
  if (!options && triples && !triples[0])
    options = triples, triples = null;
  options = options || {};

  // Add triples and prefixes if passed
  this._prefixes = Object.create(null);
  if (options.prefixes)
    this.addPrefixes(options.prefixes);
  this.defaultGraph = options.defaultGraph || 'http://example.org/#defaultGraph';
  if (triples)
    this.addTriples(triples);
}

N3Store.prototype = {
  // ## Public properties

  // ### `size` returns the number of triples in the store.
  get size() {
    // Return the triple count if if was cached.
    var size = this._size;
    if (size !== null)
      return size;

    // Calculate the number of triples by counting to the deepest level.
    var graphs = this._graphs, subjects, subject;
    for (var graphKey in graphs)
      for (var subjectKey in (subjects = graphs[graphKey].subjects))
        for (var predicateKey in (subject = subjects[subjectKey]))
          size += Object.keys(subject[predicateKey]).length;
    return this._size = size;
  },

  // ## Private methods

  // ### `_addToIndex` adds a triple to a three-layered index.
  _addToIndex: function (index0, key0, key1, key2) {
    // Create layers as necessary.
    var index1 = index0[key0] || (index0[key0] = {});
    var index2 = index1[key1] || (index1[key1] = {});
    // Setting the key to _any_ value signalizes the presence of the triple.
    index2[key2] = null;
  },

  // ### `_removeFromIndex` removes a triple from a three-layered index.
  _removeFromIndex: function (index0, key0, key1, key2) {
    // Remove the triple from the index.
    var index1 = index0[key0], index2 = index1[key1], key;
    delete index2[key2];

    // Remove intermediary index layers if they are empty.
    for (key in index2) return;
    delete index1[key1];
    for (key in index1) return;
    delete index0[key0];
  },

  // ### `_findInIndex` finds a set of triples in a three-layered index.
  // The index base is `index0` and the keys at each level are `key0`, `key1`, and `key2`.
  // Any of these keys can be `null`, which is interpreted as a wildcard.
  // `name0`, `name1`, and `name2` are the names of the keys at each level,
  // used when reconstructing the resulting triple
  // (for instance: _subject_, _predicate_, and _object_).
  // Finally, `graph` will be the graph of the created triples.
  _findInIndex: function (index0, key0, key1, key2, name0, name1, name2, graph) {
    var results = [], entityKeys = Object.keys(this._entities), tmp, index1, index2;

    // If a key is specified, use only that part of index 0.
    if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0];
    for (var value0 in index0) {
      var entity0 = entityKeys[value0];

      if (index1 = index0[value0]) {
        // If a key is specified, use only that part of index 1.
        if (key1) (tmp = index1, index1 = {})[key1] = tmp[key1];
        for (var value1 in index1) {
          var entity1 = entityKeys[value1];

          if (index2 = index1[value1]) {
            // If a key is specified, use only that part of index 2, if it exists.
            var values = key2 ? (key2 in index2 ? [key2] : []) : Object.keys(index2);
            // Create triples for all items found in index 2.
            for (var l = values.length - 1; l >= 0; l--) {
              var result = { subject: '', predicate: '', object: '', graph: graph };
              result[name0] = entity0;
              result[name1] = entity1;
              result[name2] = entityKeys[values[l]];
              results.push(result);
            }
          }
        }
      }
    }
    return results;
  },

  // ### `_countInIndex` counts matching triples in a three-layered index.
  // The index base is `index0` and the keys at each level are `key0`, `key1`, and `key2`.
  // Any of these keys can be `null`, which is interpreted as a wildcard.
  _countInIndex: function (index0, key0, key1, key2) {
    var count = 0, tmp, index1, index2;

    // If a key is specified, count only that part of index 0.
    if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0];
    for (var value0 in index0) {
      if (index1 = index0[value0]) {
        // If a key is specified, count only that part of index 1.
        if (key1) (tmp = index1, index1 = {})[key1] = tmp[key1];
        for (var value1 in index1) {
          if (index2 = index1[value1]) {
            // If a key is specified, count the triple if it exists.
            if (key2) (key2 in index2) && count++;
            // Otherwise, count all triples.
            else count += Object.keys(index2).length;
          }
        }
      }
    }
    return count;
  },

  // ## Public methods

  // ### `addTriple` adds a new N3 triple to the store.
  addTriple: function (subject, predicate, object, graph) {
    // Shift arguments if a triple object is given instead of components
    if (!predicate)
      graph = subject.graph, object = subject.object,
        predicate = subject.predicate, subject = subject.subject;

    // Find the graph that will contain the triple.
    graph = graph || this.defaultGraph;
    var graphItem = this._graphs[graph];
    // Create the graph if it doesn't exist yet.
    if (!graphItem) {
      graphItem = this._graphs[graph] = { subjects: {}, predicates: {}, objects: {} };
      // Freezing a graph helps subsequent `add` performance,
      // and properties will never be modified anyway.
      Object.freeze(graphItem);
    }

    // Since entities can often be long IRIs, we avoid storing them in every index.
    // Instead, we have a separate index that maps entities to numbers,
    // which are then used as keys in the other indexes.
    var entities = this._entities;
    subject   = entities[subject]   || (entities[subject]   = ++this._entityCount);
    predicate = entities[predicate] || (entities[predicate] = ++this._entityCount);
    object    = entities[object]    || (entities[object]    = ++this._entityCount);

    this._addToIndex(graphItem.subjects,   subject,   predicate, object);
    this._addToIndex(graphItem.predicates, predicate, object,    subject);
    this._addToIndex(graphItem.objects,    object,    subject,   predicate);

    // The cached triple count is now invalid.
    this._size = null;
  },

  // ### `addTriples` adds multiple N3 triples to the store.
  addTriples: function (triples) {
    for (var i = triples.length - 1; i >= 0; i--)
      this.addTriple(triples[i]);
  },

  // ### `addPrefix` adds support for querying with the given prefix
  addPrefix: function (prefix, iri) {
    this._prefixes[prefix] = iri;
  },

  // ### `addPrefixes` adds support for querying with the given prefixes
  addPrefixes: function (prefixes) {
    for (var prefix in prefixes)
      this.addPrefix(prefix, prefixes[prefix]);
  },

  // ### `removeTriple` removes an N3 triple from the store if it exists.
  removeTriple: function (subject, predicate, object, graph) {
    // Shift arguments if a triple object is given instead of components.
    if (!predicate)
      graph = subject.graph, object = subject.object,
        predicate = subject.predicate, subject = subject.subject;
    graph = graph || this.defaultGraph;

    // Find internal identifiers for all components.
    var graphItem, entities = this._entities, graphs = this._graphs;
    if (!(subject     = entities[subject]))   return;
    if (!(predicate   = entities[predicate])) return;
    if (!(object      = entities[object]))    return;
    if (!(graphItem   = graphs[graph]))       return;

    // Verify that the triple exists.
    var subjects, predicates;
    if (!(subjects   = graphItem.subjects[subject])) return;
    if (!(predicates = subjects[predicate])) return;
    if (!(object in predicates)) return;

    // Remove it from all indexes.
    this._removeFromIndex(graphItem.subjects,   subject,   predicate, object);
    this._removeFromIndex(graphItem.predicates, predicate, object,    subject);
    this._removeFromIndex(graphItem.objects,    object,    subject,   predicate);
    if (this._size !== null) this._size--;

    // Remove the graph if it is empty.
    for (subject in graphItem.subjects) return;
    delete graphs[graph];
  },

  // ### `removeTriples` removes multiple N3 triples from the store.
  removeTriples: function (triples) {
    for (var i = triples.length - 1; i >= 0; i--)
      this.removeTriple(triples[i]);
  },

  // ### `find` finds a set of triples matching a pattern, expanding prefixes as necessary.
  // Setting `subject`, `predicate`, `object` or `graph` to a falsy value means an _anything_ wildcard.
  find: function (subject, predicate, object, graph) {
    var prefixes = this._prefixes;
    return this.findByIRI(
      expandPrefixedName(subject,   prefixes),
      expandPrefixedName(predicate, prefixes),
      expandPrefixedName(object,    prefixes),
      expandPrefixedName(graph,     prefixes)
    );
  },

  // ### `findByIRI` finds a set of triples matching a pattern.
  // Setting `subject`, `predicate`, `object` or `graph` to a falsy value means an _anything_ wildcard.
  findByIRI: function (subject, predicate, object, graph) {
    var quads = [], graphs = {}, graphContents,
        entities = this._entities, subjectId, predicateId, objectId;
    // Either loop over all graphs, or over just one selected graph.
    if (!graph)
      graphs = this._graphs;
    else
      graphs[graph] = this._graphs[graph];

    for (var graphId in graphs) {
      // Only if the specified graph contains triples, there can be results
      if (graphContents = graphs[graphId]) {
        // Translate IRIs to internal index keys.
        // Optimization: if the entity doesn't exist, no triples with it exist.
        if (subject   && !(subjectId   = entities[subject]))   return quads;
        if (predicate && !(predicateId = entities[predicate])) return quads;
        if (object    && !(objectId    = entities[object]))    return quads;

        // Choose the optimal index, based on what fields are present
        if (subjectId) {
          if (objectId)
          // If subject and object are given, the object index will be the fastest.
            quads.push(this._findInIndex(graphContents.objects, objectId, subjectId, predicateId,
                                         'object', 'subject', 'predicate', graphId));
          else
          // If only subject and possibly predicate are given, the subject index will be the fastest.
            quads.push(this._findInIndex(graphContents.subjects, subjectId, predicateId, null,
                                         'subject', 'predicate', 'object', graphId));
        }
        else if (predicateId)
        // If only predicate and possibly object are given, the predicate index will be the fastest.
          quads.push(this._findInIndex(graphContents.predicates, predicateId, objectId, null,
                                       'predicate', 'object', 'subject', graphId));
        else if (objectId)
        // If only object is given, the object index will be the fastest.
          quads.push(this._findInIndex(graphContents.objects, objectId, null, null,
                                       'object', 'subject', 'predicate', graphId));
        else
        // If nothing is given, iterate subjects and predicates first
          quads.push(this._findInIndex(graphContents.subjects, null, null, null,
                                       'subject', 'predicate', 'object', graphId));
      }
    }
    return quads.length === 1 ? quads[0] : quads.concat.apply([], quads);
  },

  // ### `count` returns the number of triples matching a pattern, expanding prefixes as necessary.
  // Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
  // Setting `graph` to `null` means the default graph.
  count: function (subject, predicate, object, graph) {
    var prefixes = this._prefixes;
    return this.countByIRI(
      expandPrefixedName(subject,   prefixes),
      expandPrefixedName(predicate, prefixes),
      expandPrefixedName(object,    prefixes),
      expandPrefixedName(graph,     prefixes)
    );
  },

  // ### `countByIRI` returns the number of triples matching a pattern.
  // Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
  // Setting `graph` to `null` means the default graph.
  countByIRI: function (subject, predicate, object, graph) {
    graph = graph || this.defaultGraph;
    var graphItem = this._graphs[graph], entities = this._entities;

    // If the specified graph contain no triples, there are no results.
    if (!graphItem) return 0;

    // Translate IRIs to internal index keys.
    // Optimization: if the entity doesn't exist, no triples with it exist.
    if (subject   && !(subject   = entities[subject]))   return 0;
    if (predicate && !(predicate = entities[predicate])) return 0;
    if (object    && !(object    = entities[object]))    return 0;

    // Choose the optimal index, based on what fields are present
    if (subject) {
      if (object)
        // If subject and object are given, the object index will be the fastest.
        return this._countInIndex(graphItem.objects, object, subject, predicate);
      else
        // If only subject and possibly predicate are given, the subject index will be the fastest.
        return this._countInIndex(graphItem.subjects, subject, predicate, object);
    }
    else if (predicate) {
      // If only predicate and possibly object are given, the predicate index will be the fastest.
      return this._countInIndex(graphItem.predicates, predicate, object, subject);
    }
    else {
      // If only object is possibly given, the object index will be the fastest.
      return this._countInIndex(graphItem.objects, object, subject, predicate);
    }
  },

  // ### `createBlankNode` creates a new blank node, returning its name.
  createBlankNode: function (suggestedName) {
    var name, index;
    // Generate a name based on the suggested name
    if (suggestedName) {
      name = suggestedName = '_:' + suggestedName, index = 1;
      while (this._entities[name])
        name = suggestedName + index++;
    }
    // Generate a generic blank node name
    else {
      do { name = '_:b' + this._blankNodeIndex++; }
      while (this._entities[name]);
    }
    // Add the blank node to the entities, avoiding the generation of duplicates
    this._entities[name] = ++this._entityCount;
    return name;
  },
};

// ## Exports

// Export the `N3Store` class as a whole.
module.exports = N3Store;
<commit_msg>`addTriple` returns if the index was changed.<commit_after>// **N3Store** objects store N3 triples by graph in memory.

var expandPrefixedName = require('./N3Util').expandPrefixedName;

// ## Constructor
function N3Store(triples, options) {
  if (!(this instanceof N3Store))
    return new N3Store(triples, options);

  // The number of triples is initially zero.
  this._size = 0;
  // `_graphs` contains subject, predicate, and object indexes per graph.
  this._graphs = Object.create(null);
  // `_entities` maps entities such as `http://xmlns.com/foaf/0.1/name` to numbers.
  // This saves memory, since only the numbers have to be stored in `_graphs`.
  this._entities = Object.create(null);
  this._entities['><'] = 0; // Dummy entry, so the first actual key is non-zero
  this._entityCount = 0;
  // `_blankNodeIndex` is the index of the last created blank node that was automatically named
  this._blankNodeIndex = 0;

  // Shift parameters if `triples` is not given
  if (!options && triples && !triples[0])
    options = triples, triples = null;
  options = options || {};

  // Add triples and prefixes if passed
  this._prefixes = Object.create(null);
  if (options.prefixes)
    this.addPrefixes(options.prefixes);
  this.defaultGraph = options.defaultGraph || 'http://example.org/#defaultGraph';
  if (triples)
    this.addTriples(triples);
}

N3Store.prototype = {
  // ## Public properties

  // ### `size` returns the number of triples in the store.
  get size() {
    // Return the triple count if if was cached.
    var size = this._size;
    if (size !== null)
      return size;

    // Calculate the number of triples by counting to the deepest level.
    var graphs = this._graphs, subjects, subject;
    for (var graphKey in graphs)
      for (var subjectKey in (subjects = graphs[graphKey].subjects))
        for (var predicateKey in (subject = subjects[subjectKey]))
          size += Object.keys(subject[predicateKey]).length;
    return this._size = size;
  },

  // ## Private methods

  // ### `_addToIndex` adds a triple to a three-layered index.
  // Returns if the index has changed, if the entry did not already exist.
  _addToIndex: function (index0, key0, key1, key2) {
    // Create layers as necessary.
    var index1 = index0[key0] || (index0[key0] = {});
    var index2 = index1[key1] || (index1[key1] = {});
    // Setting the key to _any_ value signals the presence of the triple.
    var existed = key2 in index2;
    if (!existed)
      index2[key2] = null;
    return !existed;
  },

  // ### `_removeFromIndex` removes a triple from a three-layered index.
  _removeFromIndex: function (index0, key0, key1, key2) {
    // Remove the triple from the index.
    var index1 = index0[key0], index2 = index1[key1], key;
    delete index2[key2];

    // Remove intermediary index layers if they are empty.
    for (key in index2) return;
    delete index1[key1];
    for (key in index1) return;
    delete index0[key0];
  },

  // ### `_findInIndex` finds a set of triples in a three-layered index.
  // The index base is `index0` and the keys at each level are `key0`, `key1`, and `key2`.
  // Any of these keys can be `null`, which is interpreted as a wildcard.
  // `name0`, `name1`, and `name2` are the names of the keys at each level,
  // used when reconstructing the resulting triple
  // (for instance: _subject_, _predicate_, and _object_).
  // Finally, `graph` will be the graph of the created triples.
  _findInIndex: function (index0, key0, key1, key2, name0, name1, name2, graph) {
    var results = [], entityKeys = Object.keys(this._entities), tmp, index1, index2;

    // If a key is specified, use only that part of index 0.
    if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0];
    for (var value0 in index0) {
      var entity0 = entityKeys[value0];

      if (index1 = index0[value0]) {
        // If a key is specified, use only that part of index 1.
        if (key1) (tmp = index1, index1 = {})[key1] = tmp[key1];
        for (var value1 in index1) {
          var entity1 = entityKeys[value1];

          if (index2 = index1[value1]) {
            // If a key is specified, use only that part of index 2, if it exists.
            var values = key2 ? (key2 in index2 ? [key2] : []) : Object.keys(index2);
            // Create triples for all items found in index 2.
            for (var l = values.length - 1; l >= 0; l--) {
              var result = { subject: '', predicate: '', object: '', graph: graph };
              result[name0] = entity0;
              result[name1] = entity1;
              result[name2] = entityKeys[values[l]];
              results.push(result);
            }
          }
        }
      }
    }
    return results;
  },

  // ### `_countInIndex` counts matching triples in a three-layered index.
  // The index base is `index0` and the keys at each level are `key0`, `key1`, and `key2`.
  // Any of these keys can be `null`, which is interpreted as a wildcard.
  _countInIndex: function (index0, key0, key1, key2) {
    var count = 0, tmp, index1, index2;

    // If a key is specified, count only that part of index 0.
    if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0];
    for (var value0 in index0) {
      if (index1 = index0[value0]) {
        // If a key is specified, count only that part of index 1.
        if (key1) (tmp = index1, index1 = {})[key1] = tmp[key1];
        for (var value1 in index1) {
          if (index2 = index1[value1]) {
            // If a key is specified, count the triple if it exists.
            if (key2) (key2 in index2) && count++;
            // Otherwise, count all triples.
            else count += Object.keys(index2).length;
          }
        }
      }
    }
    return count;
  },

  // ## Public methods

  // ### `addTriple` adds a new N3 triple to the store.
  // Returns if the triple index has changed, if the triple did not already exist.
  addTriple: function (subject, predicate, object, graph) {
    // Shift arguments if a triple object is given instead of components
    if (!predicate)
      graph = subject.graph, object = subject.object,
        predicate = subject.predicate, subject = subject.subject;

    // Find the graph that will contain the triple.
    graph = graph || this.defaultGraph;
    var graphItem = this._graphs[graph];
    // Create the graph if it doesn't exist yet.
    if (!graphItem) {
      graphItem = this._graphs[graph] = { subjects: {}, predicates: {}, objects: {} };
      // Freezing a graph helps subsequent `add` performance,
      // and properties will never be modified anyway.
      Object.freeze(graphItem);
    }

    // Since entities can often be long IRIs, we avoid storing them in every index.
    // Instead, we have a separate index that maps entities to numbers,
    // which are then used as keys in the other indexes.
    var entities = this._entities;
    subject   = entities[subject]   || (entities[subject]   = ++this._entityCount);
    predicate = entities[predicate] || (entities[predicate] = ++this._entityCount);
    object    = entities[object]    || (entities[object]    = ++this._entityCount);

    var changed = this._addToIndex(graphItem.subjects,   subject,   predicate, object);
    this._addToIndex(graphItem.predicates, predicate, object,    subject);
    this._addToIndex(graphItem.objects,    object,    subject,   predicate);

    // The cached triple count is now invalid.
    this._size = null;
    return changed;
  },

  // ### `addTriples` adds multiple N3 triples to the store.
  addTriples: function (triples) {
    for (var i = triples.length - 1; i >= 0; i--)
      this.addTriple(triples[i]);
  },

  // ### `addPrefix` adds support for querying with the given prefix
  addPrefix: function (prefix, iri) {
    this._prefixes[prefix] = iri;
  },

  // ### `addPrefixes` adds support for querying with the given prefixes
  addPrefixes: function (prefixes) {
    for (var prefix in prefixes)
      this.addPrefix(prefix, prefixes[prefix]);
  },

  // ### `removeTriple` removes an N3 triple from the store if it exists.
  removeTriple: function (subject, predicate, object, graph) {
    // Shift arguments if a triple object is given instead of components.
    if (!predicate)
      graph = subject.graph, object = subject.object,
        predicate = subject.predicate, subject = subject.subject;
    graph = graph || this.defaultGraph;

    // Find internal identifiers for all components.
    var graphItem, entities = this._entities, graphs = this._graphs;
    if (!(subject     = entities[subject]))   return;
    if (!(predicate   = entities[predicate])) return;
    if (!(object      = entities[object]))    return;
    if (!(graphItem   = graphs[graph]))       return;

    // Verify that the triple exists.
    var subjects, predicates;
    if (!(subjects   = graphItem.subjects[subject])) return;
    if (!(predicates = subjects[predicate])) return;
    if (!(object in predicates)) return;

    // Remove it from all indexes.
    this._removeFromIndex(graphItem.subjects,   subject,   predicate, object);
    this._removeFromIndex(graphItem.predicates, predicate, object,    subject);
    this._removeFromIndex(graphItem.objects,    object,    subject,   predicate);
    if (this._size !== null) this._size--;

    // Remove the graph if it is empty.
    for (subject in graphItem.subjects) return;
    delete graphs[graph];
  },

  // ### `removeTriples` removes multiple N3 triples from the store.
  removeTriples: function (triples) {
    for (var i = triples.length - 1; i >= 0; i--)
      this.removeTriple(triples[i]);
  },

  // ### `find` finds a set of triples matching a pattern, expanding prefixes as necessary.
  // Setting `subject`, `predicate`, `object` or `graph` to a falsy value means an _anything_ wildcard.
  find: function (subject, predicate, object, graph) {
    var prefixes = this._prefixes;
    return this.findByIRI(
      expandPrefixedName(subject,   prefixes),
      expandPrefixedName(predicate, prefixes),
      expandPrefixedName(object,    prefixes),
      expandPrefixedName(graph,     prefixes)
    );
  },

  // ### `findByIRI` finds a set of triples matching a pattern.
  // Setting `subject`, `predicate`, `object` or `graph` to a falsy value means an _anything_ wildcard.
  findByIRI: function (subject, predicate, object, graph) {
    var quads = [], graphs = {}, graphContents,
        entities = this._entities, subjectId, predicateId, objectId;
    // Either loop over all graphs, or over just one selected graph.
    if (!graph)
      graphs = this._graphs;
    else
      graphs[graph] = this._graphs[graph];

    for (var graphId in graphs) {
      // Only if the specified graph contains triples, there can be results
      if (graphContents = graphs[graphId]) {
        // Translate IRIs to internal index keys.
        // Optimization: if the entity doesn't exist, no triples with it exist.
        if (subject   && !(subjectId   = entities[subject]))   return quads;
        if (predicate && !(predicateId = entities[predicate])) return quads;
        if (object    && !(objectId    = entities[object]))    return quads;

        // Choose the optimal index, based on what fields are present
        if (subjectId) {
          if (objectId)
          // If subject and object are given, the object index will be the fastest.
            quads.push(this._findInIndex(graphContents.objects, objectId, subjectId, predicateId,
                                         'object', 'subject', 'predicate', graphId));
          else
          // If only subject and possibly predicate are given, the subject index will be the fastest.
            quads.push(this._findInIndex(graphContents.subjects, subjectId, predicateId, null,
                                         'subject', 'predicate', 'object', graphId));
        }
        else if (predicateId)
        // If only predicate and possibly object are given, the predicate index will be the fastest.
          quads.push(this._findInIndex(graphContents.predicates, predicateId, objectId, null,
                                       'predicate', 'object', 'subject', graphId));
        else if (objectId)
        // If only object is given, the object index will be the fastest.
          quads.push(this._findInIndex(graphContents.objects, objectId, null, null,
                                       'object', 'subject', 'predicate', graphId));
        else
        // If nothing is given, iterate subjects and predicates first
          quads.push(this._findInIndex(graphContents.subjects, null, null, null,
                                       'subject', 'predicate', 'object', graphId));
      }
    }
    return quads.length === 1 ? quads[0] : quads.concat.apply([], quads);
  },

  // ### `count` returns the number of triples matching a pattern, expanding prefixes as necessary.
  // Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
  // Setting `graph` to `null` means the default graph.
  count: function (subject, predicate, object, graph) {
    var prefixes = this._prefixes;
    return this.countByIRI(
      expandPrefixedName(subject,   prefixes),
      expandPrefixedName(predicate, prefixes),
      expandPrefixedName(object,    prefixes),
      expandPrefixedName(graph,     prefixes)
    );
  },

  // ### `countByIRI` returns the number of triples matching a pattern.
  // Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
  // Setting `graph` to `null` means the default graph.
  countByIRI: function (subject, predicate, object, graph) {
    graph = graph || this.defaultGraph;
    var graphItem = this._graphs[graph], entities = this._entities;

    // If the specified graph contain no triples, there are no results.
    if (!graphItem) return 0;

    // Translate IRIs to internal index keys.
    // Optimization: if the entity doesn't exist, no triples with it exist.
    if (subject   && !(subject   = entities[subject]))   return 0;
    if (predicate && !(predicate = entities[predicate])) return 0;
    if (object    && !(object    = entities[object]))    return 0;

    // Choose the optimal index, based on what fields are present
    if (subject) {
      if (object)
        // If subject and object are given, the object index will be the fastest.
        return this._countInIndex(graphItem.objects, object, subject, predicate);
      else
        // If only subject and possibly predicate are given, the subject index will be the fastest.
        return this._countInIndex(graphItem.subjects, subject, predicate, object);
    }
    else if (predicate) {
      // If only predicate and possibly object are given, the predicate index will be the fastest.
      return this._countInIndex(graphItem.predicates, predicate, object, subject);
    }
    else {
      // If only object is possibly given, the object index will be the fastest.
      return this._countInIndex(graphItem.objects, object, subject, predicate);
    }
  },

  // ### `createBlankNode` creates a new blank node, returning its name.
  createBlankNode: function (suggestedName) {
    var name, index;
    // Generate a name based on the suggested name
    if (suggestedName) {
      name = suggestedName = '_:' + suggestedName, index = 1;
      while (this._entities[name])
        name = suggestedName + index++;
    }
    // Generate a generic blank node name
    else {
      do { name = '_:b' + this._blankNodeIndex++; }
      while (this._entities[name]);
    }
    // Add the blank node to the entities, avoiding the generation of duplicates
    this._entities[name] = ++this._entityCount;
    return name;
  },
};

// ## Exports

// Export the `N3Store` class as a whole.
module.exports = N3Store;
<|endoftext|>


<commit_before>(function(µ,SMOD,GMOD,HMOD,SC){

	var WORKER=GMOD("nodeWorker");

	SC=SC({
		File:"File",
		Promise:"Promise",
		fillResponse:require.bind(null,"./lib/fillResponse")
	});

	var LOG=require("../logger");

	var URL=require("url");
	var querystring=require("querystring");

	var NIWAapp=µ.Class(WORKER,{
		init:function(name,path)
		{
			this.logger=LOG("main").child({app:name});
			this.name=name;
			this.folder=new SC.File(path);
			this.mega(new SC.File(__dirname).changePath("NIWAappWorker").filePath,{name:name},path);
			this.eventSources=new Map();

			this.stateGuard=SC.Promise.open(this);

			this.addListener(".readyState",this,function(event)
			{
				switch(event.value.state)
				{
					case "running":
						this.logger.info({initData:event.value.data},"started");
						this.stateGuard.resolve(event.value.data);
						break;
					case "error":
						this.logger.error({error:event.value.error},"error starting");
						this.stateGuard.reject(event.value.error);
						this.stateGuard=SC.Promise.reject({message:this.name+" is not running",reason:event.value.error});
						break;
				}
			});
			setInterval(()=>
			{
				for(var context of this.eventSources.keys())
				{
					this.sendEvent(context,"ping",process.uptime())
				}
			},60000).unref();
			this.addListener("message",this,function(message)
			{
				if(message.event)
				{
					this.sendEvent(message.context,message.event,message.data);
				}
			});
		},
		rest:function(request,path)
		{
			return new SC.Promise([function(signal)
			{
				var data="";
				request.on("data",d=>data+=d);
				request.on("end",()=>signal.resolve(data==""?null:JSON.parse(data)));
				request.on("error",(e)=>signal.reject(e));
			},this.stateGuard])
			.then((data)=>
				this.request("restCall",{
					method:request.method,
					headers:request.headers,
					path:path,
					query:querystring.parse(URL.parse(request.url).query),
					data:data
				})
			,
			error=>Promise.reject({error:error,status:500}));
		},
		sendEvent:function(context, event, data)
		{
			var eventSource=this.eventSources.get(context);
			if(eventSource)
			{
				data=JSON.stringify(data);
				for(var response of eventSource)
				{
					response.write(String.raw
`event: ${event}
data: ${data}

`					);
				}
			}
		},
		eventSource:function(request,context,response)
		{
			this.request("getEventData",context).then(function(initData)
			{
				if(initData==null) return Promise.reject("no such context "+context);
				else if(request.headers.accept==="text/event-stream")
				{
					response.writeHead(200, {"Content-Type":"text/event-stream", "Cache-Control":"no-cache", "Connection":"keep-alive"});
					response.write(String.raw
`retry: 5000
event: init
data: ${JSON.stringify(initData)}

`					);

					var eventSources=this.eventSources.get(context)||[];
					this.eventSources.set(context,eventSources);
					eventSources.push(response);
					request.connection.addListener("close", function ()
					{
						var index=eventSources.indexOf(response);
						if(index===-1)this.logger.error("could not find response in eventSources");
						else eventSources.splice(index,1);
					});
				}
				else
				{//get request
					SC.fillResponse(response,initData);
				}
			})
			.catch(function(e)
			{
				SC.fillResponse(response,e,null,404);
			})
		}
	});
	module.exports=SMOD("NIWAapp",NIWAapp);

})(Morgas,Morgas.setModule,Morgas.getModule,Morgas.hasModule,Morgas.shortcut);
<commit_msg>requires<commit_after>var µ=require("Morgas");
(function(µ,SMOD,GMOD,HMOD,SC){

	var WORKER=GMOD("nodeWorker");

	SC=SC({
		File:"File",
		Promise:"Promise",
		fillResponse:require.bind(null,"./lib/fillResponse")
	});

	var LOG=require("../logger");

	var URL=require("url");
	var querystring=require("querystring");

	var NIWAapp=µ.Class(WORKER,{
		init:function(name,path)
		{
			this.logger=LOG("main").child({app:name});
			this.name=name;
			this.folder=new SC.File(path);
			this.mega(new SC.File(__dirname).changePath("NIWAappWorker").filePath,{name:name},path);
			this.eventSources=new Map();

			this.stateGuard=SC.Promise.open(this);

			this.addListener(".readyState",this,function(event)
			{
				switch(event.value.state)
				{
					case "running":
						this.logger.info({initData:event.value.data},"started");
						this.stateGuard.resolve(event.value.data);
						break;
					case "error":
						this.logger.error({error:event.value.error},"error starting");
						this.stateGuard.reject(event.value.error);
						this.stateGuard=SC.Promise.reject({message:this.name+" is not running",reason:event.value.error});
						break;
				}
			});
			setInterval(()=>
			{
				for(var context of this.eventSources.keys())
				{
					this.sendEvent(context,"ping",process.uptime())
				}
			},60000).unref();
			this.addListener("message",this,function(message)
			{
				if(message.event)
				{
					this.sendEvent(message.context,message.event,message.data);
				}
			});
		},
		rest:function(request,path)
		{
			return new SC.Promise([function(signal)
			{
				var data="";
				request.on("data",d=>data+=d);
				request.on("end",()=>signal.resolve(data==""?null:JSON.parse(data)));
				request.on("error",(e)=>signal.reject(e));
			},this.stateGuard])
			.then((data)=>
				this.request("restCall",{
					method:request.method,
					headers:request.headers,
					path:path,
					query:querystring.parse(URL.parse(request.url).query),
					data:data
				})
			,
			error=>Promise.reject({error:error,status:500}));
		},
		sendEvent:function(context, event, data)
		{
			var eventSource=this.eventSources.get(context);
			if(eventSource)
			{
				data=JSON.stringify(data);
				for(var response of eventSource)
				{
					response.write(String.raw
`event: ${event}
data: ${data}

`					);
				}
			}
		},
		eventSource:function(request,context,response)
		{
			this.request("getEventData",context).then(function(initData)
			{
				if(initData==null) return Promise.reject("no such context "+context);
				else if(request.headers.accept==="text/event-stream")
				{
					response.writeHead(200, {"Content-Type":"text/event-stream", "Cache-Control":"no-cache", "Connection":"keep-alive"});
					response.write(String.raw
`retry: 5000
event: init
data: ${JSON.stringify(initData)}

`					);

					var eventSources=this.eventSources.get(context)||[];
					this.eventSources.set(context,eventSources);
					eventSources.push(response);
					request.connection.addListener("close", function ()
					{
						var index=eventSources.indexOf(response);
						if(index===-1)this.logger.error("could not find response in eventSources");
						else eventSources.splice(index,1);
					});
				}
				else
				{//get request
					SC.fillResponse(response,initData);
				}
			})
			.catch(function(e)
			{
				SC.fillResponse(response,e,null,404);
			})
		}
	});
	module.exports=SMOD("NIWAapp",NIWAapp);

})(Morgas,Morgas.setModule,Morgas.getModule,Morgas.hasModule,Morgas.shortcut);
<|endoftext|>


<commit_before>var EventEmitter = require('eventemitter2').EventEmitter2,
    util = require('util'),
    Discovery = require('./Discovery'),
    axon = require('axon'),
    Subscriber = require('./Subscriber'),
    Requester = require('./Requester');

var Sockend = function(io, advertisement, discoveryOptions) {
    EventEmitter.call(this, {
        wildcard: true, // should the event emitter use wildcards.
        delimiter: '::', // the delimiter used to segment namespaces, defaults to `.`.
        newListener: false, // if you want to emit the newListener event set to true.
        maxListeners: 2000 // the max number of listeners that can be assigned to an event, defaults to 10.
    });

    advertisement = advertisement || {};
    advertisement.axon_type = 'sockend';
    this.advertisement = advertisement;
    this.requesterTransformators = [];

    var that = this,
        d = Discovery(advertisement, discoveryOptions);

    var namespaces = {};

    d.on('added', function(obj) {
        if (obj.advertisement.axon_type != 'rep') return;
        if (obj.advertisement.key != advertisement.key) return;

        var namespace = obj.advertisement.namespace,
            normalizedNamespace =  namespace || '';

        if (namespaces['/' + normalizedNamespace]) return;

        namespaces['/' + normalizedNamespace] = true;
        obj.namespace = namespace;

        var requester = new Requester({
            name: 'sockendReq',
            namespace: namespace,
            key: that.advertisement.key
        }, discoveryOptions);
        obj.requester = requester;

        obj.requesterSocketHandler = function(socket) {
            obj.advertisement.respondsTo.forEach(function(topic) {
                socket.on(topic, function(data, cb) {
                    if (arguments.length == 1 && typeof data == 'function') {
                        cb = data;
                        data = {};
                    }

                    var req = {
                        type: topic,
                        data: data
                    };

                    that.requesterTransformators.forEach(function(transFn) {
                        transFn(req, socket);
                    });

                    requester.send(req, cb);
                });
            });
        };

        requester.on('ready', function() {
            var server = io.sockets;
            if (namespace) server = io.of('/' + namespace);
            server.on('connection', obj.requesterSocketHandler);
        });
    });

    d.on('added', function(obj) {
        if (obj.advertisement.axon_type != 'pub-emitter') return;
        if (obj.advertisement.key != advertisement.key) return;

        var subscriber = new Subscriber({
            name: 'sockendSub',
            namespace: obj.advertisement.namespace,
            key: that.advertisement.key
        }, discoveryOptions);

        obj.subscriber = subscriber;

        subscriber.on('**', function(data) {
            if (this.event == 'added' || this.event == 'removed') return;

            var topic = this.event.split('::'),
                namespace = '';

            if (topic.length > 1) {
                namespace += '/' + topic[0];
                topic = topic.slice(1)
            }

            topic = topic.join('');

            io.of(namespace).emit(topic, data);
        });

        subscriber.on('removed', function() {
            subscriber.close();
        });
    });
};
util.inherits(Sockend, EventEmitter);

module.exports = Sockend;
<commit_msg>[Sockend] Initialize socket handler for already connected sockets, too, when the responder is ready.<commit_after>var EventEmitter = require('eventemitter2').EventEmitter2,
    util = require('util'),
    Discovery = require('./Discovery'),
    axon = require('axon'),
    Subscriber = require('./Subscriber'),
    Requester = require('./Requester');

var Sockend = function(io, advertisement, discoveryOptions) {
    EventEmitter.call(this, {
        wildcard: true, // should the event emitter use wildcards.
        delimiter: '::', // the delimiter used to segment namespaces, defaults to `.`.
        newListener: false, // if you want to emit the newListener event set to true.
        maxListeners: 2000 // the max number of listeners that can be assigned to an event, defaults to 10.
    });

    advertisement = advertisement || {};
    advertisement.axon_type = 'sockend';
    this.advertisement = advertisement;
    this.requesterTransformators = [];

    var that = this,
        d = Discovery(advertisement, discoveryOptions);

    var namespaces = {};

    d.on('added', function(obj) {
        if (obj.advertisement.axon_type != 'rep') return;
        if (obj.advertisement.key != advertisement.key) return;

        var namespace = obj.advertisement.namespace,
            normalizedNamespace =  namespace || '';

        if (namespaces['/' + normalizedNamespace]) return;

        namespaces['/' + normalizedNamespace] = true;
        obj.namespace = namespace;

        var requester = new Requester({
            name: 'sockendReq',
            namespace: namespace,
            key: that.advertisement.key
        }, discoveryOptions);
        obj.requester = requester;

        obj.requesterSocketHandler = function(socket) {
            obj.advertisement.respondsTo.forEach(function(topic) {
                socket.on(topic, function(data, cb) {
                    if (arguments.length == 1 && typeof data == 'function') {
                        cb = data;
                        data = {};
                    }

                    var req = {
                        type: topic,
                        data: data
                    };

                    that.requesterTransformators.forEach(function(transFn) {
                        transFn(req, socket);
                    });

                    requester.send(req, cb);
                });
            });
        };

        requester.on('ready', function() {
            var server = io.sockets;
            if (namespace) server = io.of('/' + namespace);
            server.on('connection', obj.requesterSocketHandler);

            for (var sId in server.sockets)
                obj.requesterSocketHandler(server.sockets[sId]);
        });
    });

    d.on('added', function(obj) {
        if (obj.advertisement.axon_type != 'pub-emitter') return;
        if (obj.advertisement.key != advertisement.key) return;

        var subscriber = new Subscriber({
            name: 'sockendSub',
            namespace: obj.advertisement.namespace,
            key: that.advertisement.key
        }, discoveryOptions);

        obj.subscriber = subscriber;

        subscriber.on('**', function(data) {
            if (this.event == 'added' || this.event == 'removed') return;

            var topic = this.event.split('::'),
                namespace = '';

            if (topic.length > 1) {
                namespace += '/' + topic[0];
                topic = topic.slice(1)
            }

            topic = topic.join('');

            io.of(namespace).emit(topic, data);
        });

        subscriber.on('removed', function() {
            subscriber.close();
        });
    });
};
util.inherits(Sockend, EventEmitter);

module.exports = Sockend;
<|endoftext|>