<commit_before>'use babel';

var ftpClient = require('@icetee/ftp');
const EventEmitter = require('events');
const FileSystem = require('fs');

export default class Ftp extends EventEmitter {

  constructor(connection) {
    super();
    const self = this;

    self.setMaxListeners(100);
  }

  connect(connection) {
    const self = this;
    self.emit('debug', 'ftp:connect');

    self.client = new ftpClient();
    let promise = new Promise((resolve, reject) => {
      self.client.on('ready', function () {
        self.emit('debug', 'ftp:connect:ready');
        self.connected = self.client.connected;
        this.emit('connected');
        resolve(self);
      });

      self.client.on('error', (err) => {
        self.emit('debug', 'ftp:connect:error', err);
        self.connected = self.client.connected;
        self.emit('error', err);
        reject(err);
      });

      self.client.on('end', () => {
        self.emit('debug', 'ftp:connect:end');
        self.connected = self.client.connected;
        self.emit('log', '> Connection end');
        self.emit('ended', 'Connection end');
        reject({ message: 'Connection end' });
      });

      self.client.on('close', (hadErr) => {
        self.emit('debug', 'ftp:connect:close');
        self.connected = self.client.connected;
        self.emit('log', '> Connection closed');
        self.emit('closed', 'Connection closed');
        reject({ message: 'Connection closed' });
      });
    });

    connection.debug = function (msg) {
      let data = msg.split(/\[(.*)\] (>|<)(.*)/g);
      if (data[1] == "connection") {
        let direction = data[2];
        let cmd = data[3].replace(/\'+/g, "")
          .replace(/\\r|\\n/g, " ");

        // mask password
        if (direction.trim() == ">") {
          let cmdparts = cmd.split(" ");
          if (cmdparts[1] == "PASS") {
            cmd = cmdparts[1] + " " + '*'.repeat(cmdparts[2].length);
          }
        }

        self.emit('log', direction + ' ' + cmd);
      }
    }

    self.client.connect(connection);

    return promise;
  }

  isConnected() {
    const self = this;

    if (!self.client) return false;
    return self.connected;
  }

  list(remotePath) {
    const self = this;
    self.emit('debug', 'ftp:list', remotePath);

    let promise = new Promise((resolve, reject) => {
      try {
        self.client.list(remotePath.trim(), function (err, list) {
          if (err) {
            reject(err);
          } else {
            resolve(list);
          }
        });
      } catch (err) {
        reject(err);
      }
    });

    return promise;
  }

  mkdir(remotePath) {
    const self = this;
    self.emit('debug', 'ftp:mkdir', remotePath);

    let promise = new Promise((resolve, reject) => {
      self.client.mkdir(remotePath.trim(), function (err) {
        if (err) {
          reject(err);
        } else {
          resolve(remotePath.trim());
        }
      });
    });

    return promise;
  }

  rmdir(remotePath, recursive) {
    const self = this;
    self.emit('debug', 'ftp:rmdir', remotePath);

    let promise = new Promise((resolve, reject) => {
      self.client.rmdir(remotePath.trim(), recursive, (err) => {
        if (err) {
          reject(err);
        } else {
          resolve(remotePath.trim());
        }
      });
    });

    return promise;
  }

  put(content, queueItem) {
    const self = this;

    let remotePath = queueItem.info.remotePath;
    let localPath = queueItem.info.localPath;
    let written = 0;

    self.emit('debug', 'ftp:put', remotePath);

    const timer = setInterval(() => {
      if (!self.client || !self.client._pasvSocket) return;
      queueItem.changeStatus('Transferring');
      written = self.client._pasvSocket.bytesWritten;
      writtena = self.client._socket.bytesWritten;
      queueItem.changeProgress(written);
      self.emit('data', written);
    }, 250);

    let promise = new Promise((resolve, reject) => {
      self.client.put(content, remotePath, function (err) {
        if (err) {
          if (timer) clearInterval(timer);
          reject(err);
        } else {
          if (timer) clearInterval(timer);
          queueItem.changeProgress(queueItem.info.size);
          resolve(remotePath.trim());
        }
      });
    });

    return promise;
  }

  get(queueItem) {
    const self = this;

    let remotePath = queueItem.info.remotePath;
    let localPath = queueItem.info.localPath;

    self.emit('debug', 'ftp:get', remotePath, localPath);

    let promise = new Promise((resolve, reject) => {
      self.client.get(remotePath, function (err, stream) {
        if (err) {
          reject(err);
        } else {
          let file = FileSystem.createWriteStream(localPath, { autoClose: true });

          file.on('open', (err) => {
            queueItem.addStream(file);
            queueItem.changeStatus('Transferring');
          });
          file.on('error', (err) => {
            queueItem.changeStatus('Error');
            reject(err);
          });

          self.on('closed', function (status) {
            queueItem.changeStatus('Error');
            reject(status);
          });
          self.on('error', function (err) {
            queueItem.changeStatus('Error');
            reject(status);
          });

          let read = 0;
          stream.on('data', (data) => {
            read += data.length;
            queueItem.changeProgress(read);
            self.emit('data', read);
          });

          stream.once('end', () => {
            resolve(localPath.trim());
          });
          stream.pipe(file);
        }
      });
    });

    return promise;
  }

  delete(remotePath) {
    const self = this;
    self.emit('debug', 'ftp:delete', remotePath);

    let promise = new Promise((resolve, reject) => {
      self.client.delete(remotePath, function (err) {
        if (err) {
          reject(err);
        } else {
          resolve(remotePath.trim());
        }
      });
    });

    return promise;
  }

  rename(oldRemotePath, newRemotePath) {
    const self = this;
    self.emit('debug', 'ftp:rename', oldRemotePath, newRemotePath);

    let promise = new Promise((resolve, reject) => {
      self.client.rename(oldRemotePath.trim(), newRemotePath.trim(), function (err) {
        if (err) {
          reject(err);
        } else {
          resolve(newRemotePath.trim());
        }
      });
    });

    return promise;
  }

  end() {
    const self = this;
    self.emit('debug', 'ftp:end');

    self.connected = false;
    let promise = new Promise((resolve, reject) => {
      self.client.on('end', () => {
        self.emit('debug', 'ftp:end:end');
        self.connected = false;
        resolve(true);
      });

      self.client.on('close', (hadErr) => {
        self.emit('debug', 'ftp:end:close');
        self.connected = false;
        resolve(true);
      });

      self.client.end();
    });

    return promise;
  }
}
<commit_msg>Add option "showHiddenFiles" to settings.<commit_after>'use babel';

var ftpClient = require('@icetee/ftp');
const EventEmitter = require('events');
const FileSystem = require('fs');

export default class Ftp extends EventEmitter {

  constructor(connection) {
    super();
    const self = this;

    self.setMaxListeners(100);
  }

  connect(connection) {
    const self = this;
    self.emit('debug', 'ftp:connect');

    self.client = new ftpClient();
    let promise = new Promise((resolve, reject) => {
      self.client.on('ready', function () {
        self.emit('debug', 'ftp:connect:ready');
        self.connected = self.client.connected;
        this.emit('connected');
        resolve(self);
      });

      self.client.on('error', (err) => {
        self.emit('debug', 'ftp:connect:error', err);
        self.connected = self.client.connected;
        self.emit('error', err);
        reject(err);
      });

      self.client.on('end', () => {
        self.emit('debug', 'ftp:connect:end');
        self.connected = self.client.connected;
        self.emit('log', '> Connection end');
        self.emit('ended', 'Connection end');
        reject({ message: 'Connection end' });
      });

      self.client.on('close', (hadErr) => {
        self.emit('debug', 'ftp:connect:close');
        self.connected = self.client.connected;
        self.emit('log', '> Connection closed');
        self.emit('closed', 'Connection closed');
        reject({ message: 'Connection closed' });
      });
    });

    connection.debug = function (msg) {
      let data = msg.split(/\[(.*)\] (>|<)(.*)/g);
      if (data[1] == "connection") {
        let direction = data[2];
        let cmd = data[3].replace(/\'+/g, "")
          .replace(/\\r|\\n/g, " ");

        // mask password
        if (direction.trim() == ">") {
          let cmdparts = cmd.split(" ");
          if (cmdparts[1] == "PASS") {
            cmd = cmdparts[1] + " " + '*'.repeat(cmdparts[2].length);
          }
        }

        self.emit('log', direction + ' ' + cmd);
      }
    }

    self.client.connect(connection);

    return promise;
  }

  isConnected() {
    const self = this;

    if (!self.client) return false;
    return self.connected;
  }

  list(remotePath) {
    const self = this;
    const showHiddenFiles = atom.config.get('ftp-remote-edit.tree.showHiddenFiles');

    self.emit('debug', 'ftp:list', remotePath);

    let promise = new Promise((resolve, reject) => {
      try {
        let path = (showHiddenFiles ? '-al ' + remotePath.trim() : remotePath.trim());
        self.client.list(path, function (err, list) {
          if (err) {
            reject(err);
          } else {
            resolve(list);
          }
        });
      } catch (err) {
        reject(err);
      }
    });

    return promise;
  }

  mkdir(remotePath) {
    const self = this;
    self.emit('debug', 'ftp:mkdir', remotePath);

    let promise = new Promise((resolve, reject) => {
      self.client.mkdir(remotePath.trim(), function (err) {
        if (err) {
          reject(err);
        } else {
          resolve(remotePath.trim());
        }
      });
    });

    return promise;
  }

  rmdir(remotePath, recursive) {
    const self = this;
    self.emit('debug', 'ftp:rmdir', remotePath);

    let promise = new Promise((resolve, reject) => {
      self.client.rmdir(remotePath.trim(), recursive, (err) => {
        if (err) {
          reject(err);
        } else {
          resolve(remotePath.trim());
        }
      });
    });

    return promise;
  }

  put(content, queueItem) {
    const self = this;

    let remotePath = queueItem.info.remotePath;
    let localPath = queueItem.info.localPath;
    let written = 0;

    self.emit('debug', 'ftp:put', remotePath);

    const timer = setInterval(() => {
      if (!self.client || !self.client._pasvSocket) return;
      queueItem.changeStatus('Transferring');
      written = self.client._pasvSocket.bytesWritten;
      writtena = self.client._socket.bytesWritten;
      queueItem.changeProgress(written);
      self.emit('data', written);
    }, 250);

    let promise = new Promise((resolve, reject) => {
      self.client.put(content, remotePath, function (err) {
        if (err) {
          if (timer) clearInterval(timer);
          reject(err);
        } else {
          if (timer) clearInterval(timer);
          queueItem.changeProgress(queueItem.info.size);
          resolve(remotePath.trim());
        }
      });
    });

    return promise;
  }

  get(queueItem) {
    const self = this;

    let remotePath = queueItem.info.remotePath;
    let localPath = queueItem.info.localPath;

    self.emit('debug', 'ftp:get', remotePath, localPath);

    let promise = new Promise((resolve, reject) => {
      self.client.get(remotePath, function (err, stream) {
        if (err) {
          reject(err);
        } else {
          let file = FileSystem.createWriteStream(localPath, { autoClose: true });

          file.on('open', (err) => {
            queueItem.addStream(file);
            queueItem.changeStatus('Transferring');
          });
          file.on('error', (err) => {
            queueItem.changeStatus('Error');
            reject(err);
          });

          self.on('closed', function (status) {
            queueItem.changeStatus('Error');
            reject(status);
          });
          self.on('error', function (err) {
            queueItem.changeStatus('Error');
            reject(status);
          });

          let read = 0;
          stream.on('data', (data) => {
            read += data.length;
            queueItem.changeProgress(read);
            self.emit('data', read);
          });

          stream.once('end', () => {
            resolve(localPath.trim());
          });
          stream.pipe(file);
        }
      });
    });

    return promise;
  }

  delete(remotePath) {
    const self = this;
    self.emit('debug', 'ftp:delete', remotePath);

    let promise = new Promise((resolve, reject) => {
      self.client.delete(remotePath, function (err) {
        if (err) {
          reject(err);
        } else {
          resolve(remotePath.trim());
        }
      });
    });

    return promise;
  }

  rename(oldRemotePath, newRemotePath) {
    const self = this;
    self.emit('debug', 'ftp:rename', oldRemotePath, newRemotePath);

    let promise = new Promise((resolve, reject) => {
      self.client.rename(oldRemotePath.trim(), newRemotePath.trim(), function (err) {
        if (err) {
          reject(err);
        } else {
          resolve(newRemotePath.trim());
        }
      });
    });

    return promise;
  }

  end() {
    const self = this;
    self.emit('debug', 'ftp:end');

    self.connected = false;
    let promise = new Promise((resolve, reject) => {
      self.client.on('end', () => {
        self.emit('debug', 'ftp:end:end');
        self.connected = false;
        resolve(true);
      });

      self.client.on('close', (hadErr) => {
        self.emit('debug', 'ftp:end:close');
        self.connected = false;
        resolve(true);
      });

      self.client.end();
    });

    return promise;
  }
}
<|endoftext|>


<commit_before>'use strict';

var compact    = require('es5-ext/lib/Array/prototype/compact')
  , i          = require('es5-ext/lib/Function/i')
  , partial    = require('es5-ext/lib/Function/prototype/partial')
  , mapToArray = require('es5-ext/lib/Object/map-to-array')
  , callable   = require('es5-ext/lib/Object/valid-callable')
  , value      = require('es5-ext/lib/Object/valid-value')
  , numPad     = require('es5-ext/lib/Number/prototype/pad')
  , pad        = require('es5-ext/lib/String/prototype/pad')
  , memoize    = require('memoizee')
  , clc        = require('cli-color')
  , readFile   = require('next/lib/fs/read-file')
  , normalize  = require('./_normalize-source')

  , isArray = Array.isArray, ceil = Math.ceil
  , log = process.stdout.write.bind(process.stdout)
  , nextTick = process.nextTick
  , reportFile, isStatus

  , eolRe = /(?:\r\n|[\n\r\u2028\u2029])/
  , lname = clc.bold, lgray = clc.gray, lerr = clc.yellow, lok = clc.green
	, lopts, lstatOk = i, lstatFail = clc.bold.red, lstatErr = clc.red
  , lcount = clc.xterm(232), lline = clc.xterm(240);

lopts = function (options) {
	options = compact.call(mapToArray(options, function (name, value) {
		if (value == true) {
			return name;
		} else if (value === false) {
			return null;
		} else if (isArray(value)) {
			return value.join(', ');
		} else {
			return value;
		}
	}));
	return lgray('(' + (options.length ? options.join('|') : 'no options')  + ')');
};

reportFile = function (name, report) {
	var str = '', countPad, srcPad;
	isStatus = false;
	str += lname(name) + ' ' + lopts(report.options);
	if (report.length) {
		str += '\n';
		countPad = partial.call(pad, ' ', String(report.length).length);
		srcPad = partial.call(numPad, String(report.srcLines.length).length);
		report.forEach(function (data, index) {
			str += lcount(countPad.call(index + 1) + ':') + ' ' + lerr(data.message)
				+ '\n' + lline(srcPad.call(data.line) + ':')
				+ report.srcLines[data.line - 1] + '\n';
		});
	} else {
		str += ' ' + lok('OK') + '\n';
	}
	log(str + '\n');
};

module.exports = function (report, options) {
	var root, reports, process, promise, reportStatus;
	callable(report) && value(options);

	process = function (report, name) {
		if (report.length) {
			if (!report.src) {
				return readFile(event.path, { loose: true })(function (src) {
					report.src = normalize(String(src));
					return process(report, name);
				});
			} else {
				report.srcLines = report.src.split(eolRe);
				reportFile(name, report);
				reportStatus(reports);
			}
		}
		return report;
	};

	reportStatus = memoize(function () {
		nextTick(function () {
			var ok;
			reports.slice(reports.parsed).forEach(function (report) {
				if (report.length) {
					++reports.failed;
					reports.errors += report.length;
				}
			});
			reports.parsed = reports.length;
			ok = (reports.length - reports.failed);
			log(lstatOk(ok + ' OK ['
				+ ((ok / reports.length) * 100).toFixed(2)
				+ '%]') + '  ' + lstatFail(reports.failed + ' Failed') + ' '
				+ lstatErr('[' + reports.errors + ' Errors]') + '\n\n');
			isStatus = true;
			reportStatus.clear();
		});
	});

	root = report.root;
	if (options.progress) {
		reports = [];
		reports.parsed = 0;
		reports.errors = 0;
		reports.failed = 0;
		report.on('change', function (event) {
			reports.push(process(event.report, event.name));
		});
		promise = report(reports);
	} else {
		promise = report(function (data) {
			reports = mapToArray(data, process);
			reports.parsed = 0;
			reports.errors = 0;
			reports.failed = 0;
			return reports;
		});
	}
	return promise(function (data) {
		if (!data.length) {
			console.log("No files to process");
		}
	});
};
<commit_msg>Fix source fallback code<commit_after>'use strict';

var compact    = require('es5-ext/lib/Array/prototype/compact')
  , i          = require('es5-ext/lib/Function/i')
  , partial    = require('es5-ext/lib/Function/prototype/partial')
  , mapToArray = require('es5-ext/lib/Object/map-to-array')
  , callable   = require('es5-ext/lib/Object/valid-callable')
  , value      = require('es5-ext/lib/Object/valid-value')
  , numPad     = require('es5-ext/lib/Number/prototype/pad')
  , pad        = require('es5-ext/lib/String/prototype/pad')
  , memoize    = require('memoizee')
  , clc        = require('cli-color')
  , readFile   = require('next/lib/fs/read-file')
  , normalize  = require('./_normalize-source')

  , isArray = Array.isArray, ceil = Math.ceil
  , log = process.stdout.write.bind(process.stdout)
  , nextTick = process.nextTick
  , reportFile, isStatus

  , eolRe = /(?:\r\n|[\n\r\u2028\u2029])/
  , lname = clc.bold, lgray = clc.gray, lerr = clc.yellow, lok = clc.green
	, lopts, lstatOk = i, lstatFail = clc.bold.red, lstatErr = clc.red
  , lcount = clc.xterm(232), lline = clc.xterm(240);

lopts = function (options) {
	options = compact.call(mapToArray(options, function (name, value) {
		if (value == true) {
			return name;
		} else if (value === false) {
			return null;
		} else if (isArray(value)) {
			return value.join(', ');
		} else {
			return value;
		}
	}));
	return lgray('(' + (options.length ? options.join('|') : 'no options')  + ')');
};

reportFile = function (name, report) {
	var str = '', countPad, srcPad;
	isStatus = false;
	str += lname(name) + ' ' + lopts(report.options);
	if (report.length) {
		str += '\n';
		countPad = partial.call(pad, ' ', String(report.length).length);
		srcPad = partial.call(numPad, String(report.srcLines.length).length);
		report.forEach(function (data, index) {
			str += lcount(countPad.call(index + 1) + ':') + ' ' + lerr(data.message)
				+ '\n' + lline(srcPad.call(data.line) + ':')
				+ report.srcLines[data.line - 1] + '\n';
		});
	} else {
		str += ' ' + lok('OK') + '\n';
	}
	log(str + '\n');
};

module.exports = function (report, options) {
	var root, reports, process, promise, reportStatus;
	callable(report) && value(options);

	process = function (report, name) {
		if (report.length) {
			if (!report.src) {
				return readFile(report.path, { loose: true })(function (src) {
					if (src) {
						report.src = normalize(String(src));
						return process(report, name);
					}
				});
			} else {
				report.srcLines = report.src.split(eolRe);
				reportFile(name, report);
				reportStatus(reports);
			}
		}
		return report;
	};

	reportStatus = memoize(function () {
		nextTick(function () {
			var ok;
			reports.slice(reports.parsed).forEach(function (report) {
				if (report.length) {
					++reports.failed;
					reports.errors += report.length;
				}
			});
			reports.parsed = reports.length;
			ok = (reports.length - reports.failed);
			log(lstatOk(ok + ' OK ['
				+ ((ok / reports.length) * 100).toFixed(2)
				+ '%]') + '  ' + lstatFail(reports.failed + ' Failed') + ' '
				+ lstatErr('[' + reports.errors + ' Errors]') + '\n\n');
			isStatus = true;
			reportStatus.clear();
		});
	});

	root = report.root;
	if (options.progress) {
		reports = [];
		reports.parsed = 0;
		reports.errors = 0;
		reports.failed = 0;
		report.on('change', function (event) {
			reports.push(process(event.report, event.name));
		});
		promise = report(reports);
	} else {
		promise = report(function (data) {
			reports = mapToArray(data, process);
			reports.parsed = 0;
			reports.errors = 0;
			reports.failed = 0;
			return reports;
		});
	}
	return promise(function (data) {
		if (!data.length) {
			console.log("No files to process");
		}
	});
};
<|endoftext|>


<commit_before>
var _ = require('lodash');
var amqp = require('@sazze/amqp');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

var consumer = null;

function Consumer(options) {
  EventEmitter.call(this);

  this.options = {
    exchange: {
      name: 'firehose',
      type: 'topic',
      options: {
        durable: true,
        autoDelete: false
      }
    },
    queue: {
      name: '',   // random name will be generated
      durable: false,
      exclusive: true,
      autoDelete: true
    },
    connection: {
      heartbeat: 1
    },
    channel: {
      prefetch: 20
    },
    consumeOptions: {
      noAck: true // auto ack messages
    }
  };

  if (!_.isPlainObject(options)) {
    options = {};
  }

  _.merge(this.options, options);

  this.consumers = {}
}

util.inherits(Consumer, EventEmitter);

Consumer.prototype.consume = function (socket, data) {
  if (!_.isUndefined(this.consumers[socket.id]) && !_.isUndefined(this.consumers[socket.id].routingKeys[data.routingKey])) {
    // already listening for this routing key
    return;
  }

  if (_.isUndefined(this.consumers[socket.id])) {
    this.consumers[socket.id] = {routingKeys: {}};
  }

  if (!_.isUndefined(this.consumers[socket.id].routingKeys['#'])) {
    // already listening for this routing key
    return;
  }

  var options = _.merge({}, this.options);

  options.routingKey = data.routingKey;

  this.consumers[socket.id].routingKeys[data.routingKey] = new amqp.consumer(options, handleMsg.bind(this, socket.id));

  this.consumers[socket.id].routingKeys[data.routingKey].start();
};

Consumer.prototype.stopConsume = function (socket, data) {
  if (_.isUndefined(this.consumers[socket.id])) {
    // nothing to stop
    return;
  }

  if (data.routingKey == '#') {
    // stop all consumers
    stopAll.call(this, socket);
    return;
  }

  if (data.routingKey == '##') {
    // this is a little modification we need to prevent stopAll() from looping infinitely
    data.routingKey = '#';
  }

  if (_.isUndefined(this.consumers[socket.id].routingKeys[data.routingKey])) {
    // nothing to stop
    return;
  }

  this.consumers[socket.id].routingKeys[data.routingKey].stop(function (err) {
    delete this.consumers[socket.id].routingKeys[data.routingKey];
  }.bind(this));
};

module.exports = function (options) {
  if (consumer) {
    return consumer;
  }

  consumer = new Consumer(options);

  return consumer;
};

/**
 * Private Methods
 **/

function handleMsg(socketId, dataStr) {
  this.emit('event:' + socketId, JSON.parse(dataStr));
}

/**
 * Stop all consumers linked to a socket
 *
 * @param socket
 */
function stopAll(socket) {
  if (_.isEmpty(this.consumers)) {
    return;
  }

  _.forOwn(this.consumers[socket.id].routingKeys, function (consumer, routingKey) {
    if (routingKey == '#') {
      // this must be done to prevent infinite stop loop
      routingKey = '##';
    }

    this.stopConsume(socket, {routingKey: routingKey});
  }.bind(this));
}<commit_msg>allow eventsd websocket clients to share the event messages by passing an "id" as part of the consume message.<commit_after>
var _ = require('lodash');
var amqp = require('@sazze/amqp');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

var consumer = null;

function Consumer(options) {
  EventEmitter.call(this);

  this.options = {
    exchange: {
      name: 'firehose',
      type: 'topic',
      options: {
        durable: true,
        autoDelete: false
      }
    },
    queue: {
      name: '',   // random name will be generated
      durable: false,
      exclusive: true,
      autoDelete: true
    },
    connection: {
      heartbeat: 1
    },
    channel: {
      prefetch: 20
    },
    consumeOptions: {
      noAck: true // auto ack messages
    }
  };

  if (!_.isPlainObject(options)) {
    options = {};
  }

  _.merge(this.options, options);

  this.consumers = {}
}

util.inherits(Consumer, EventEmitter);

Consumer.prototype.consume = function (socket, data) {
  if (!_.isUndefined(this.consumers[socket.id]) && !_.isUndefined(this.consumers[socket.id].routingKeys[data.routingKey])) {
    // already listening for this routing key
    return;
  }

  if (_.isUndefined(this.consumers[socket.id])) {
    this.consumers[socket.id] = {routingKeys: {}};
  }

  if (!_.isUndefined(this.consumers[socket.id].routingKeys['#'])) {
    // already listening for this routing key
    return;
  }

  var options = _.merge({}, this.options);

  options.routingKey = data.routingKey;

  if (!_.isUndefined(data.id) && _.isString(data.id)) {
    // this consume request is hinting at sharing the event workload with other clients (by specifying an explicit id).
    // allow clients with the same id and routingKey to bind to the same amqp queue
    var queueName = data.id.replace(/\W/g, '').trim();

    if (queueName) {
      options.queue.name = 'shared-' + data.routingKey.replace(/\W/g, '') + '-' + queueName;
      options.queue.exclusive = false;
    }
  }

  this.consumers[socket.id].routingKeys[data.routingKey] = new amqp.consumer(options, handleMsg.bind(this, socket.id));

  this.consumers[socket.id].routingKeys[data.routingKey].start();
};

Consumer.prototype.stopConsume = function (socket, data) {
  if (_.isUndefined(this.consumers[socket.id])) {
    // nothing to stop
    return;
  }

  if (data.routingKey == '#') {
    // stop all consumers
    stopAll.call(this, socket);
    return;
  }

  if (data.routingKey == '##') {
    // this is a little modification we need to prevent stopAll() from looping infinitely
    data.routingKey = '#';
  }

  if (_.isUndefined(this.consumers[socket.id].routingKeys[data.routingKey])) {
    // nothing to stop
    return;
  }

  this.consumers[socket.id].routingKeys[data.routingKey].stop(function (err) {
    delete this.consumers[socket.id].routingKeys[data.routingKey];
  }.bind(this));
};

module.exports = function (options) {
  if (consumer) {
    return consumer;
  }

  consumer = new Consumer(options);

  return consumer;
};

/**
 * Private Methods
 **/

function handleMsg(socketId, dataStr) {
  this.emit('event:' + socketId, JSON.parse(dataStr));
}

/**
 * Stop all consumers linked to a socket
 *
 * @param socket
 */
function stopAll(socket) {
  if (_.isEmpty(this.consumers)) {
    return;
  }

  _.forOwn(this.consumers[socket.id].routingKeys, function (consumer, routingKey) {
    if (routingKey == '#') {
      // this must be done to prevent infinite stop loop
      routingKey = '##';
    }

    this.stopConsume(socket, {routingKey: routingKey});
  }.bind(this));
}<|endoftext|>


<commit_before>var url = require('url');
var BasicBridgeItAdapter = require('./basicbridgeit');
var ld = require('lodash');
var util = require('util');
var log = util.debuglog('adapters');

var wrappedAdapter;

/**
 * An adapter is basically something that can send a request and get a response.  They
 * vary in the configuration values that they accept and process but they all follow the
 * basic pattern of execute(params, cb) where the adapter will execute a request using the
 * supplied parameters.  At a minimum, the params will contain a config property which
 * contains all the necessary values and settings for executing the request.
 *
 * This adapter is a very basic Push adapter for triggering notifications via the
 * Notification service via the REST API. This is an example of creating a wrapper
 * around the BasicHTTPAdapter that is specialized for a particular service.
 *
 * @constructor
 */
function BasicPushAdapter() {
    wrappedAdapter = new BasicBridgeItAdapter();
}
module.exports = BasicPushAdapter;

BasicPushAdapter.prototype.validateConfig = function (config) {

    wrappedAdapter.validateConfig(config);

    if (!config.body) {
        console.error('missing body');
        return false;
    }

    if (!config.body.access_token) {
        console.error('missing access_token in body');
        return false;
    }

    if (!config.body.push_configuration) {
        console.error('missing push configuration in body');
        return false;
    }

    return true;
};

BasicPushAdapter.prototype.execute = function (params, cb) {

    //Merge incoming configuration - overriding any defaults.
    var defaultConfig = {
        method: 'POST',
        scheme: 'http',
        host: 'web1',
        service: 'push/rest',
        path: 'groups',
        params: {},
        headers: {
            'Content-type': 'application/json; charset=utf-8'
        },
        body: {
            access_token: null,
            push_configuration: {
                subject: "No subject provided (BasicPushAdapter)",
                details: "No detail provided (BasicPushAdapter)"
            },
            groups: []
        }
    };

    if (!params.config) {
        params.config = {};
    }

    params.config = ld.merge(defaultConfig, params.config);
    log('BasicPushAdapter params', JSON.stringify(params));

    wrappedAdapter.execute(params, function (err, results) {
        if (err) {
            cb(err);
            return;
        }
        cb(null, results);
    });
};
<commit_msg>BasicPushAdapter - Set the push groups to the users by default<commit_after>var url = require('url');
var BasicBridgeItAdapter = require('./basicbridgeit');
var ld = require('lodash');
var util = require('util');
var log = util.debuglog('adapters');

var wrappedAdapter;

/**
 * An adapter is basically something that can send a request and get a response.  They
 * vary in the configuration values that they accept and process but they all follow the
 * basic pattern of execute(params, cb) where the adapter will execute a request using the
 * supplied parameters.  At a minimum, the params will contain a config property which
 * contains all the necessary values and settings for executing the request.
 *
 * This adapter is a very basic Push adapter for triggering notifications via the
 * Notification service via the REST API. This is an example of creating a wrapper
 * around the BasicHTTPAdapter that is specialized for a particular service.
 *
 * @constructor
 */
function BasicPushAdapter() {
    wrappedAdapter = new BasicBridgeItAdapter();
}
module.exports = BasicPushAdapter;

BasicPushAdapter.prototype.validateConfig = function (config) {

    wrappedAdapter.validateConfig(config);

    if (!config.body) {
        console.error('missing body');
        return false;
    }

    if (!config.body.access_token) {
        console.error('missing access_token in body');
        return false;
    }

    if (!config.body.push_configuration) {
        console.error('missing push configuration in body');
        return false;
    }

    return true;
};

BasicPushAdapter.prototype.execute = function (params, cb) {

    //Merge incoming configuration - overriding any defaults.
    var defaultConfig = {
        method: 'POST',
        scheme: 'http',
        host: 'web1',
        service: 'push/rest',
        path: 'groups',
        params: {},
        headers: {
            'Content-type': 'application/json; charset=utf-8'
        },
        body: {
            access_token: null,
            push_configuration: {
                subject: "No subject provided (BasicPushAdapter)",
                details: "No detail provided (BasicPushAdapter)"
            },
            groups: params.config && params.config.context && params.config.context.users ? params.config.context.users : []
        }
    };

    if (!params.config) {
        params.config = {};
    }

    params.config = ld.merge(defaultConfig, params.config);
    log('BasicPushAdapter params', JSON.stringify(params));

    wrappedAdapter.execute(params, function (err, results) {
        if (err) {
            cb(err);
            return;
        }
        cb(null, results);
    });
};
<|endoftext|>


<commit_before>'use strict'

const fs = require('fs')
const path = require('path')

const console = require('../helpers/color-console')
const sfs = require('../helpers/sfs')
const zip = require('../helpers/zip')
const {createTemplate} = require('./template')

const loader = require('./loader')

const assets = path.join(__dirname, '../../assets')

const assetsToCopy = [
  path.join(assets, 'css/master.css'),
  path.join(assets, 'css/normalize.css')
]

// -------------------------------------------------------------
// Module.
// -------------------------------------------------------------

function isDataFile (filename) {
  var ext = path.extname(filename)
  filename = path.basename(filename, ext)

  return filename === 'data' && ext === '.xml'
}

function generate (dataPath) {
  console.log('') // Newline
  console.log('Looking for data files in ' + dataPath)

  let presskits = {}
  presskits.products = []
  let count = 0

  // 1 - get all presskits
  // Inspect given path for data files
  let files = sfs.findAllFiles(dataPath, { maxDepth: 1, ignoredFolders: ['build', 'node_modules'] })

  files.filter(isDataFile).forEach(file => {
    try {
      let presskit = loader.loadDataFile(file)
      if (!presskit) return

      console.log('- ' + presskit.type + ': "' + presskit.title + '" (' + file + ')')

      if (presskit.type === 'company') {
        if (presskits.company) {
          console.error('Multiple companies detected. This is not supported yet, only the last will be used!')
        }

        presskits.company = { path: dataPath, presskit: presskit }
      } else {
        presskits.products.push({ path: file, presskit: presskit })
      }

      count++
    } catch (err) {
      console.error(err)
    }
  })

  console.log('')

  if (count === 0) {
    console.warn('No data files found!')
    return
  }

  console.log('Generating HTML...')

  // 2 - generates all presskits.
  let outputProducts = []
  for (let i = 0, len = presskits.products.length; i < len; i++) {
    let p = presskits.products[i]

    let outputPath = build(dataPath, p.path, p.presskit, [])
    outputProducts.push({path: outputPath, title: p.presskit.title})
  }

  // Company should be the last one, listing all the products
  if (presskits.company) {
    // Get a ref to all products
    build(dataPath, presskits.company.path, presskits.company.presskit, outputProducts)
  }

  console.log('')
  console.log('Exporting assets (CSS, etc)...')

  // Copy assets (CSS and stuff)
  copyMandatoryFiles(dataPath)

  console.log('')
  console.log('Done! 👌')
  console.log('')
}

// Build the HTML
function build (basePath, dataFile, presskit, relatedProducts) {
  const template = createTemplate(assets, presskit.type)

  // List all images before rendering the template
  // ---------------------------------------------------------------
  let stat = fs.statSync(dataFile)
  let assetsSourceDir = path.join(dataFile, 'images')
  if (stat.isFile()) {
    assetsSourceDir = path.join(path.dirname(dataFile), 'images')
  }

  let images = {screenshots: [], logos: [], icons: []}
  fs.readdirSync(assetsSourceDir).forEach(function (f) {
    let ext = path.extname(f)
    if (ext !== '.zip') {
      if (f.toLowerCase().startsWith('header')) {
        images.header = f
      } else if (f.toLowerCase().startsWith('logo')) {
        images.logos.push(f)
      } else if (f.toLowerCase().startsWith('icon')) {
        images.icons.push(f)
      } else {
        images.screenshots.push(f)
      }
    }
  })

  // Apply data to template.
  let html = template({
    presskit: presskit,
    products: relatedProducts,
    images: images,
    assets: (presskit.type !== 'company' ? '..' : '.')
  })

  // Export result in HTML
  // ---------------------------------------------------------------
  // -- Ensure ./build exists
  let buildDir = getBuildDir(basePath)

  let targetDir = buildDir

  if (presskit.type !== 'company') {
    // Company should be placed in the root folder
    // but each product is a subfolder, named as the source's folder name
    targetDir = path.join(buildDir, path.basename(path.dirname(dataFile)))
    sfs.createDir(targetDir)
  }

  // Prepare output filename
  let htmlFilename = 'index.html'
  let htmlDestination = path.join(targetDir, htmlFilename)

  console.log('- ' + presskit.title + ' ->  ' + htmlDestination)

  // -- Write HTML
  fs.writeFileSync(htmlDestination, html)

  // Copy all assets from images/ to build/images/
  // ---------------------------------------------------------------
  let assetsTargetDir = path.join(targetDir, 'images')
  if (fs.existsSync(assetsSourceDir)) {
    // Create target dir if necessary
    sfs.createDir(assetsTargetDir)

    // Copy all assets from source to target
    fs.readdirSync(assetsSourceDir).forEach(function (name) {
      let source = path.join(assetsSourceDir, name)
      let target = path.join(assetsTargetDir, name)
      fs.writeFileSync(target, fs.readFileSync(source))
    })
  }

  // Create an archive for images.
  zip('images.zip', images.screenshots, assetsSourceDir, assetsTargetDir)
  zip('logo.zip', images.logos.concat(images.icons), assetsSourceDir, assetsTargetDir)

  // Output relative path from build/
  return path.relative(buildDir, htmlDestination)
}

// Add css and any mandatory files specified to the build directory
function copyMandatoryFiles (basePath) {
  let buildDir = getBuildDir(basePath)

  for (let f of assetsToCopy) {
    let filepath = path.resolve(f)
    let filename = path.basename(filepath)
    let dir = path.basename(path.dirname(filepath))

    let targetDir = path.join(buildDir, dir)
    sfs.createDir(targetDir)

    let targetPath = path.join(targetDir, filename)

    console.log('- ' + targetPath)
    fs.writeFileSync(targetPath, fs.readFileSync(filepath))
  }
}

// Create and returns the path to the build directory from the given path
function getBuildDir (basePath) {
  let buildDir = path.join(basePath, 'build')
  sfs.createDir(buildDir)
  return buildDir
}

// -------------------------------------------------------------
// Exports.
// -------------------------------------------------------------

module.exports = {
  generate
}
<commit_msg>Improve console UI<commit_after>'use strict'

const fs = require('fs')
const path = require('path')

const chalk = require('chalk')

const console = require('../helpers/color-console')
const sfs = require('../helpers/sfs')
const zip = require('../helpers/zip')
const {createTemplate} = require('./template')

const loader = require('./loader')

const assets = path.join(__dirname, '../../assets')

const assetsToCopy = [
  path.join(assets, 'css/master.css'),
  path.join(assets, 'css/normalize.css')
]

// -------------------------------------------------------------
// Module.
// -------------------------------------------------------------

function isDataFile (filename) {
  var ext = path.extname(filename)
  filename = path.basename(filename, ext)

  return filename === 'data' && ext === '.xml'
}

function generate (dataPath) {
  console.log(`Starting directory: ${chalk.green(dataPath)}`)
  console.log('')
  console.log(chalk.yellow('Finding data…'))

  let presskits = {}
  presskits.products = []
  let count = 0

  // 1 - get all presskits
  // Inspect given path for data files
  let files = sfs.findAllFiles(dataPath, { maxDepth: 1, ignoredFolders: ['build', 'node_modules'] })

  files.filter(isDataFile).forEach(file => {
    try {
      let presskit = loader.loadDataFile(file)
      if (!presskit) return

      console.log(`- ${presskit.type}: "${presskit.title}" ${chalk.dim(file)}`)

      if (presskit.type === 'company') {
        if (presskits.company) {
          console.error('Multiple companies detected. This is not supported yet, only the last will be used!')
        }

        presskits.company = { path: dataPath, presskit: presskit }
      } else {
        presskits.products.push({ path: file, presskit: presskit })
      }

      count++
    } catch (err) {
      console.error(err)
    }
  })

  console.log('')

  if (count === 0) {
    console.warn('No data files found!')
    return
  }

  console.log(chalk.yellow('Generating HTML…'))

  // 2 - generates all presskits.
  let outputProducts = []
  for (let i = 0, len = presskits.products.length; i < len; i++) {
    let p = presskits.products[i]

    let outputPath = build(dataPath, p.path, p.presskit, [])
    outputProducts.push({path: outputPath, title: p.presskit.title})
  }

  // Company should be the last one, listing all the products
  if (presskits.company) {
    // Get a ref to all products
    build(dataPath, presskits.company.path, presskits.company.presskit, outputProducts)
  }

  console.log('')
  console.log(chalk.yellow('Exporting assets…'))

  // Copy assets (CSS and stuff)
  copyMandatoryFiles(dataPath)

  console.log('')
  console.log('Done! 👌')
}

// Build the HTML
function build (basePath, dataFile, presskit, relatedProducts) {
  const template = createTemplate(assets, presskit.type)

  // List all images before rendering the template
  // ---------------------------------------------------------------
  let stat = fs.statSync(dataFile)
  let assetsSourceDir = path.join(dataFile, 'images')
  if (stat.isFile()) {
    assetsSourceDir = path.join(path.dirname(dataFile), 'images')
  }

  let images = {screenshots: [], logos: [], icons: []}
  fs.readdirSync(assetsSourceDir).forEach(function (f) {
    let ext = path.extname(f)
    if (ext !== '.zip') {
      if (f.toLowerCase().startsWith('header')) {
        images.header = f
      } else if (f.toLowerCase().startsWith('logo')) {
        images.logos.push(f)
      } else if (f.toLowerCase().startsWith('icon')) {
        images.icons.push(f)
      } else {
        images.screenshots.push(f)
      }
    }
  })

  // Apply data to template.
  let html = template({
    presskit: presskit,
    products: relatedProducts,
    images: images,
    assets: (presskit.type !== 'company' ? '..' : '.')
  })

  // Export result in HTML
  // ---------------------------------------------------------------
  // -- Ensure ./build exists
  let buildDir = getBuildDir(basePath)

  let targetDir = buildDir

  if (presskit.type !== 'company') {
    // Company should be placed in the root folder
    // but each product is a subfolder, named as the source's folder name
    targetDir = path.join(buildDir, path.basename(path.dirname(dataFile)))
    sfs.createDir(targetDir)
  }

  // Prepare output filename
  let htmlFilename = 'index.html'
  let htmlDestination = path.join(targetDir, htmlFilename)

  console.log(`- "${presskit.title}" -> ${chalk.blue(htmlDestination)}`)

  // -- Write HTML
  fs.writeFileSync(htmlDestination, html)

  // Copy all assets from images/ to build/images/
  // ---------------------------------------------------------------
  let assetsTargetDir = path.join(targetDir, 'images')
  if (fs.existsSync(assetsSourceDir)) {
    // Create target dir if necessary
    sfs.createDir(assetsTargetDir)

    // Copy all assets from source to target
    fs.readdirSync(assetsSourceDir).forEach(function (name) {
      let source = path.join(assetsSourceDir, name)
      let target = path.join(assetsTargetDir, name)
      fs.writeFileSync(target, fs.readFileSync(source))
    })
  }

  // Create an archive for images.
  zip('images.zip', images.screenshots, assetsSourceDir, assetsTargetDir)
  zip('logo.zip', images.logos.concat(images.icons), assetsSourceDir, assetsTargetDir)

  // Output relative path from build/
  return path.relative(buildDir, htmlDestination)
}

// Add css and any mandatory files specified to the build directory
function copyMandatoryFiles (basePath) {
  let buildDir = getBuildDir(basePath)

  for (let f of assetsToCopy) {
    let filepath = path.resolve(f)
    let filename = path.basename(filepath)
    let dir = path.basename(path.dirname(filepath))

    let targetDir = path.join(buildDir, dir)
    sfs.createDir(targetDir)

    let targetPath = path.join(targetDir, filename)

    console.log('- ' + targetPath)
    fs.writeFileSync(targetPath, fs.readFileSync(filepath))
  }
}

// Create and returns the path to the build directory from the given path
function getBuildDir (basePath) {
  let buildDir = path.join(basePath, 'build')
  sfs.createDir(buildDir)
  return buildDir
}

// -------------------------------------------------------------
// Exports.
// -------------------------------------------------------------

module.exports = {
  generate
}
<|endoftext|>


<commit_before>/**
 * A JavaScript API for transmitting and verifying Credentials.
 *
 * @author Dave Longley
 *
 * Copyright (c) 2015 Digital Bazaar, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of Digital Bazaar, Inc. nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
var _ = require('lodash');
var async = require('async');
var request = require('request');
// TODO: include did-io for DID public key look ups
var jsigs;
var jsonld;

// module API
var api = {};
module.exports = api;

// constants
var IDENTITY_CONTEXT_URL = 'https://w3id.org/identity/v1';
var IDENTITY_FRAME = {
  '@context': IDENTITY_CONTEXT_URL,
  type: 'Identity'
};

/**
 * Allows injectables to be set or retrieved.
 *
 * @param name the name of the injectable to use (
 *          eg: `jsonld`, `jsonld-signatures`).
 * @param [api] the api to set for the injectable, only present for setter,
 *          omit for getter.
 */
api.use = function(name, api) {
  if(name === 'jsigs') {
    if(api) {
      jsigs = api;
    }
    if(!jsigs) {
      jsigs = require('jsonld-signatures')({
        inject: {jsonld: api.use('jsonld')}
      });
    }
    return jsigs;
  }

  if(name === 'jsonld') {
    if(api) {
      jsonld = api;
    }
    if(!jsonld) {
      jsonld = require('jsonld');
    }
    return jsonld;
  }

  return null;
};

/**
 * Verifies the digital signature on the given Identity object and any
 * credentials it contains. This call only checks authenticity, it does not
 * check a trust store for the identities used to sign.
 *
 * TODO: This call should accept a function for checking trusted identities,
 * domains, etc., so these things can be checked prior to normalization.
 *
 * The callback will be passed the following object:
 *
 * {
 *   verified: <true/false>,
 *   identity: <framed identity>,
 *   domain: <domain the signature was signed for>,
 *   publicKey: <public key object for the creator of the signature>,
 *   publicKeyOwner: <identity object for the creator of the signature>,
 *   // a map of verified objects for each credential (ID => verified)
 *   credentials: {}
 * }
 *
 * @param identity the identity to check.
 * @param options the options to use:
 *          [checkTimestamp]: check signature timestamp (default: true) on
 *            the main Identity credential.
 *          [maxTimestampDelta]: signature must be created within a window of
 *            this many seconds (default: 15 minutes).
 *          [documentLoader(url, callback(err, remoteDoc))] the document loader.
 * @param callback(err, result) called once the operation completes.
 */
api.verify = function(identity, options, callback) {
  if(typeof options === 'function') {
    callback = options;
    options = {};
  }
  options = options || {};

  var jsonld = api.use('jsonld');
  var jsigs = api.use('jsigs');

  // TODO: move any of opencred-verifier code here?
  var result = {
    credentials: {}
  };

  // TODO: remove and use implementation below
  result.identity = identity;
  return callback(null, result);

  async.auto({
    frame: function(callback) {
      jsonld.frame(
        identity, IDENTITY_FRAME, function(err, framed) {
          if(err) {
            return callback(err);
          }
          var id = framed['@graph'][0];
          if(!id) {
            return callback(new Error('Unknown Identity format.'));
          }
          id['@context'] = framed['@context'];
          callback(null, id);
        });
    },
    verifyIdentity: ['frame', function(callback, results) {
      result.identity = results.frame;
      var opts = _.assign({
        id: results.frame.id,
        checkTimestamp: true
      }, options, {
        checkDomain: checkDomain(result),
        checkKeyOwner: checkKeyOwner(result)
      });
      jsigs.verify(results.frame, opts, function(err, verified) {
        result.verified = verified;
        callback(err, result);
      });
    }],
    verifyCredentials: ['verifyIdentity', 'frame', function(callback, results) {
      var credentials = jsonld.getValues(results.frame, 'credential');
      async.forEach(credentials, function(credential, callback) {
        credential['@context'] = results.frame['@context'];
        var res = result.credentials[credential.id] = {};
        var opts = _.assign(options, {
          id: credential.id,
          checkDomain: checkDomain(res),
          checkKeyOwner: checkKeyOwner(res)
        });
        jsigs.verify(credential, opts, function(err, verified) {
          res.verified = verified;
          callback(err, res);
        });
      }, callback);
    }]
  }, function(err) {
    callback(err, result);
  });
};

api.send = function(options, callback) {
  var url = options.url;
  var credential = options.credential;
  var privateKeyPem = options.privateKeyPem;
  var publicKeyId = options.publicKeyId;

  var requestOptions = {
    url: url,
    body: credential,
    json: true,
    httpSignature: {
      key: privateKeyPem,
      keyId: publicKeyId,
      headers: ['date', 'host', 'request-line']
    }
  };

  request.post(requestOptions, callback);
};

// TODO: define a function for getting a public key via did-io

function checkDomain(result) {
  // TODO: check against some config value for the domain or similar
  return function(domain, options, callback) {
    result.domain = domain;
    return callback(null, true);
  };
}

function checkKeyOwner(result) {
  // TODO: check against a list of trusted owners, perhaps based on credential
  // type as well
  return function(owner, key, options, callback) {
    result.publicKey = key;
    result.publicKeyOwner = owner;
    return callback(null, true);
  };
}
<commit_msg>Skip identity framing until named graph framing support is ready.<commit_after>/**
 * A JavaScript API for transmitting and verifying Credentials.
 *
 * @author Dave Longley
 *
 * Copyright (c) 2015 Digital Bazaar, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of Digital Bazaar, Inc. nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
var _ = require('lodash');
var async = require('async');
var request = require('request');
// TODO: include did-io for DID public key look ups
var jsigs;
var jsonld;

// module API
var api = {};
module.exports = api;

// constants
var IDENTITY_CONTEXT_URL = 'https://w3id.org/identity/v1';
var IDENTITY_FRAME = {
  '@context': IDENTITY_CONTEXT_URL,
  type: 'Identity'
};

/**
 * Allows injectables to be set or retrieved.
 *
 * @param name the name of the injectable to use (
 *          eg: `jsonld`, `jsonld-signatures`).
 * @param [api] the api to set for the injectable, only present for setter,
 *          omit for getter.
 */
api.use = function(name, api) {
  if(name === 'jsigs') {
    if(api) {
      jsigs = api;
    }
    if(!jsigs) {
      jsigs = require('jsonld-signatures')({
        inject: {jsonld: api.use('jsonld')}
      });
    }
    return jsigs;
  }

  if(name === 'jsonld') {
    if(api) {
      jsonld = api;
    }
    if(!jsonld) {
      jsonld = require('jsonld');
    }
    return jsonld;
  }

  return null;
};

/**
 * Verifies the digital signature on the given Identity object and any
 * credentials it contains. This call only checks authenticity, it does not
 * check a trust store for the identities used to sign.
 *
 * TODO: This call should accept a function for checking trusted identities,
 * domains, etc., so these things can be checked prior to normalization.
 *
 * The callback will be passed the following object:
 *
 * {
 *   verified: <true/false>,
 *   identity: <framed identity>,
 *   domain: <domain the signature was signed for>,
 *   publicKey: <public key object for the creator of the signature>,
 *   publicKeyOwner: <identity object for the creator of the signature>,
 *   // a map of verified objects for each credential (ID => verified)
 *   credentials: {}
 * }
 *
 * @param identity the identity to check.
 * @param options the options to use:
 *          [checkTimestamp]: check signature timestamp (default: true) on
 *            the main Identity credential.
 *          [maxTimestampDelta]: signature must be created within a window of
 *            this many seconds (default: 15 minutes).
 *          [documentLoader(url, callback(err, remoteDoc))] the document loader.
 * @param callback(err, result) called once the operation completes.
 */
api.verify = function(identity, options, callback) {
  if(typeof options === 'function') {
    callback = options;
    options = {};
  }
  options = options || {};

  var jsonld = api.use('jsonld');
  var jsigs = api.use('jsigs');

  // TODO: assume identity is already properly framed prior to consumption;
  // this is a temporary requirement until jsonld.js supports named graph
  // framing

  // TODO: move any of opencred-verifier code here?
  var result = {
    credentials: {}
  };

  async.auto({
    frame: function(callback) {
      return callback(null, identity);
      /*
      jsonld.frame(
        identity, IDENTITY_FRAME, function(err, framed) {
          if(err) {
            return callback(err);
          }
          var id = framed['@graph'][0];
          if(!id) {
            return callback(new Error('Unknown Identity format.'));
          }
          id['@context'] = framed['@context'];
          callback(null, id);
        });*/
    },
    verifyIdentity: ['frame', function(callback, results) {
      result.identity = results.frame;
      var opts = _.assign({
        id: results.frame.id,
        checkTimestamp: true
      }, options, {
        checkDomain: checkDomain(result),
        checkKeyOwner: checkKeyOwner(result)
      });
      jsigs.verify(results.identity, opts, function(err, verified) {
        result.verified = verified;
        callback(err, result);
      });
    }],
    verifyCredentials: ['verifyIdentity', function(callback, results) {
      var credentials = jsonld.getValues(results.identity, 'credential');
      async.forEach(credentials, function(credential, callback) {
        credential = credential['@graph'];
        credential['@context'] = results.frame['@context'];
        var res = result.credentials[credential.id] = {};
        var opts = _.assign(options, {
          id: credential.id,
          checkDomain: checkDomain(res),
          checkKeyOwner: checkKeyOwner(res)
        });
        jsigs.verify(credential, opts, function(err, verified) {
          res.verified = verified;
          callback(err, res);
        });
      }, callback);
    }]
  }, function(err) {
    callback(err, result);
  });
};

api.send = function(options, callback) {
  var url = options.url;
  var credential = options.credential;
  var privateKeyPem = options.privateKeyPem;
  var publicKeyId = options.publicKeyId;

  var requestOptions = {
    url: url,
    body: credential,
    json: true,
    httpSignature: {
      key: privateKeyPem,
      keyId: publicKeyId,
      headers: ['date', 'host', 'request-line']
    }
  };

  request.post(requestOptions, callback);
};

// TODO: define a function for getting a public key via did-io

function checkDomain(result) {
  // TODO: check against some config value for the domain or similar
  return function(domain, options, callback) {
    result.domain = domain;
    return callback(null, true);
  };
}

function checkKeyOwner(result) {
  // TODO: check against a list of trusted owners, perhaps based on credential
  // type as well
  return function(owner, key, options, callback) {
    result.publicKey = key;
    result.publicKeyOwner = owner;
    return callback(null, true);
  };
}
<|endoftext|>


<commit_before>var cheerio = require('cheerio');
var _ = require('underscore');
var fn = require('./fn');

var parseRule = function( val ){
    var rule = {};
    rule.$type = val.constructor.name;
    switch( rule.$type ){
        case 'String':
            rule.$fn = fn.text;
            rule.$rule = val;
            rule.$type = 'function';
            break;
        case 'Array':
            val = val[0];
            rule.$rule = val.$rule || '';
            rule.$fn = parseRule( val );
            delete rule.$fn.$rule;
            break;
        case 'Object':
            var subRules = _.omit( val, '$rule', '$fn', '$type');
            if( Object.keys(subRules).length ){
                rule.$type = 'Object';
                rule.$fn = {};
                rule.$rule = val.$rule || '';
                Object.keys(subRules).forEach(function(k){
                    val = subRules[k];
                    rule.$fn[k] = parseRule( val );
                });
            } else {
                rule.$rule = val.$rule ||'';
                rule.$type = 'function';
                rule.$fn = val.$fn;
            }
            break;
    }
    return rule;
};

exports.parseRule = parseRule;

function Extractor( rules ){
    this.rules = parseRule(rules);
}

Extractor.prototype.extract = function(html){
    var $ = cheerio(html);
    out = getItem($, this.rules );
    return out;
};

var getItem = function( $, rule ){
    if(typeof $ === 'string' )
        $ = cheerio.load($);
    var out;
    var elem = rule.$rule? $.find(rule.$rule) : $;
    switch ( rule.$type ){
        case 'function':
            out = rule.$fn( elem );
            break;
        case 'Object':
            out = {};
            Object.keys(rule.$fn).forEach(function( key){
                var subRule = rule.$fn[key];
                out[key] = getItem( elem, subRule );
            });
            break;
        case 'Array':
            out = [];
            _.each( elem, function(item){
                var data = getItem(cheerio(item), rule.$fn );
                out.push(data);
            });
            break;
    }
    return out;
};

exports.getItem = getItem;

exports.Extractor = Extractor;
<commit_msg>Fix: check whether element is empty or not before applying function<commit_after>var cheerio = require('cheerio');
var _ = require('underscore');
var fn = require('./fn');

var parseRule = function( val ){
    var rule = {};
    rule.$type = val.constructor.name;
    switch( rule.$type ){
        case 'String':
            rule.$fn = fn.text;
            rule.$rule = val;
            rule.$type = 'function';
            break;
        case 'Array':
            val = val[0];
            rule.$rule = val.$rule || '';
            rule.$fn = parseRule( val );
            delete rule.$fn.$rule;
            break;
        case 'Object':
            var subRules = _.omit( val, '$rule', '$fn', '$type');
            if( Object.keys(subRules).length ){
                rule.$type = 'Object';
                rule.$fn = {};
                rule.$rule = val.$rule || '';
                Object.keys(subRules).forEach(function(k){
                    val = subRules[k];
                    rule.$fn[k] = parseRule( val );
                });
            } else {
                rule.$rule = val.$rule ||'';
                rule.$type = 'function';
                rule.$fn = val.$fn;
            }
            break;
    }
    return rule;
};

exports.parseRule = parseRule;

function Extractor( rules ){
    this.rules = parseRule(rules);
}

Extractor.prototype.extract = function(html){
    var $ = cheerio(html);
    out = getItem($, this.rules );
    return out;
};

var getItem = function( $, rule ){
    if(typeof $ === 'string' )
        $ = cheerio.load($);
    var out;
    var elem = rule.$rule? $.find(rule.$rule) : $;
    switch ( rule.$type ){
        case 'function':
            out = elem.length? rule.$fn( elem ) : '';
            break;
        case 'Object':
            out = {};
            if(elem.length) {
                Object.keys(rule.$fn).forEach(function( key){
                    var subRule = rule.$fn[key];
                    out[key] = getItem( elem, subRule );
                });
            }
            break;
        case 'Array':
            out = [];
            if(elem.length) {
                _.each( elem, function(item){
                    var data = getItem(cheerio(item), rule.$fn );
                    out.push(data);
                });
            }
            break;
    }
    return out;
};

exports.getItem = getItem;

exports.Extractor = Extractor;
<|endoftext|>


<commit_before>var Redis   = require('ioredis');
var _       = require('lodash');
var fs      = require('fs');
var path    = require('path');
var async   = require('async');

var DRIP_AND_TAKE_LUA_SCRIPT = fs.readFileSync(path.join(__dirname, 'drip_and_take.lua'), 'utf8');
// var TAKE_LUA_SCRIPT = fs.readFileSync(path.join(__dirname, 'take.lua'), 'utf8');
var TAKE_FROM_FIXED_LUA_SCRIPT = fs.readFileSync(path.join(__dirname, 'take_from_fixed.lua'), 'utf8');

function BucketDB (bucket_name, redis) {
  this._redis = redis;
  this._bucket_name = bucket_name;
  this._index_name = 'bucketidx';
}

BucketDB.prototype.add_or_update_index = function (key, callback) {
  this._redis.zadd(this._index_name, 0, key).then(function() {
    callback(key);
  });
};

BucketDB.prototype.remove_from_index = function (key, callback) {
  this._redis.zrem(this._index_name, key).then(function() {
    callback();
  });
};

BucketDB.prototype.drip_and_take = function (key, params, count, callback) {
  var self = this;
  self.add_or_update_index(this._bucket_name + ':' + key, function (key) {
    self._redis.drip_and_take(key, +new Date(), params.per_interval / params.interval, params.size, count, function (err, result) {
      if (err) return callback(err);
      callback(null, !!result[2], {
        lastDrip: parseInt(result[0], 10),
        content:  parseInt(result[1], 10),
        size:     params.size
      });
    });
  });
};

BucketDB.prototype.take_from_fixed = function (key, params, count, callback) {
  var self = this;
  self.add_or_update_index(this._bucket_name + ':' + key, function (key) {
    self._redis.take_from_fixed(key, params.size, count, function (err, result) {
      if (err) return callback(err);
      callback(null, result);
    });
  });
};

BucketDB.prototype.get = function (key, callback) {
  var redis_key = this._bucket_name + ':' + key;
  var self = this;

  return self._redis.get(redis_key, function (err, value) {
    if (!err && !value) {
      return self.remove_from_index(redis_key, function() {
        callback(err);
      });
    }
    if (err || !value) {
      return callback(err);
    }
    callback(null, JSON.parse(value));
  });
};

BucketDB.prototype.list = function (key, callback) {
  var redis_key = this._bucket_name + ':' + key;
  var self = this;
  var prefix = self._redis.options && self._redis.options.keyPrefix;

  this._redis.zrangebylex(this._index_name, '[' + redis_key, '[' + redis_key + '\xff', function(err, keys) {
    if (err) return callback(err);

    keys = !prefix ? keys : keys.map(function (key) {
                              return key.replace(prefix, '');
                            });

    if (!keys || keys.length === 0) {
      return callback(null, {});
    }

    async.map(keys, function (key, done) {
      var instance = key.substr(self._bucket_name.length + 1);

      self._redis.hgetall(key, function (err, bucket) {
        if (err && err.message.indexOf('WRONGTYPE Operation against') > -1) {
          return self._redis.get(key, function (err, content) {
            var result = {
              instance: instance,
              lastDrip: 0,
              content:  parseInt(content, 10)
            };

            done(null, result);
          });
        }

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

        var result = {
          instance: instance,
          lastDrip: parseInt(bucket.last_drip, 10),
          content:  parseInt(bucket.content, 10)
        };

        done(null, result);
      });
    }, function (err, results) {
      if (err) return callback(err);
      var result = results.reduce(function (result, current) {
        result[current.instance] = current;
        return result;
      }, {});

      callback(null, result);
    });

  });
};

module.exports = function (dbOptions) {
  var redis;

  if (Array.isArray(dbOptions.nodes)) {
    redis = new Redis.Cluster(dbOptions.nodes, _.omit(dbOptions, ['nodes']));
  } else {
    redis = new Redis(dbOptions);
  }

  redis.defineCommand('drip_and_take', {
    lua: DRIP_AND_TAKE_LUA_SCRIPT,
    numberOfKeys: 1
  });

  redis.defineCommand('take_from_fixed', {
    lua: TAKE_FROM_FIXED_LUA_SCRIPT,
    numberOfKeys: 1
  });

  return {
    create: function (bucket_name) {
      return new BucketDB(bucket_name, redis);
    }
  };
};<commit_msg>Expire old keys from the secondary index<commit_after>var Redis   = require('ioredis');
var _       = require('lodash');
var fs      = require('fs');
var path    = require('path');
var async   = require('async');

var DRIP_AND_TAKE_LUA_SCRIPT = fs.readFileSync(path.join(__dirname, 'drip_and_take.lua'), 'utf8');
// var TAKE_LUA_SCRIPT = fs.readFileSync(path.join(__dirname, 'take.lua'), 'utf8');
var TAKE_FROM_FIXED_LUA_SCRIPT = fs.readFileSync(path.join(__dirname, 'take_from_fixed.lua'), 'utf8');

function BucketDB (bucket_name, redis, redis_subscriber) {
  this._redis = redis;
  this._bucket_name = bucket_name;
  this._index_name = 'bucketidx';

  var self = this;
  if (redis_subscriber && redis_subscriber.listenerCount && redis_subscriber.listenerCount('pmessage') === 0) {
    redis_subscriber.on('pmessage', function(pattern, channel, message) {
      if (channel.match(/:expired$/)) {
        self.remove_from_index(message, _.noop);
      }
    });
  }
}

BucketDB.prototype.add_or_update_index = function (key, callback) {
  this._redis.zadd(this._index_name, 0, key).then(function(result_code) {
    callback(key, result_code);
  });
};

BucketDB.prototype.remove_from_index = function (key, callback) {
  var prefix = this._redis.options && this._redis.options.keyPrefix;
  prefix = prefix || '';
  this._redis.zrem(this._index_name, key.replace(prefix, '')).then(function(result_code) {
    callback(result_code);
  });
};

BucketDB.prototype.drip_and_take = function (key, params, count, callback) {
  var self = this;
  self.add_or_update_index(this._bucket_name + ':' + key, function (key) {
    self._redis.drip_and_take(key, +new Date(), params.per_interval / params.interval, params.size, count, function (err, result) {
      if (err) return callback(err);
      callback(null, !!result[2], {
        lastDrip: parseInt(result[0], 10),
        content:  parseInt(result[1], 10),
        size:     params.size
      });
    });
  });
};

BucketDB.prototype.take_from_fixed = function (key, params, count, callback) {
  var self = this;
  self.add_or_update_index(this._bucket_name + ':' + key, function (key) {
    self._redis.take_from_fixed(key, params.size, count, function (err, result) {
      if (err) return callback(err);
      callback(null, result);
    });
  });
};

BucketDB.prototype.get = function (key, callback) {
  var redis_key = this._bucket_name + ':' + key;
  var self = this;

  return self._redis.get(redis_key, function (err, value) {
    if (!err && !value) {
      return self.remove_from_index(redis_key, function() {
        callback(err);
      });
    }
    if (err || !value) {
      return callback(err);
    }
    callback(null, JSON.parse(value));
  });
};

BucketDB.prototype.list = function (key, callback) {
  var redis_key = this._bucket_name + ':' + key;
  var self = this;
  var prefix = self._redis.options && self._redis.options.keyPrefix;

  this._redis.zrangebylex(this._index_name, '[' + redis_key, '[' + redis_key + '\xff', function(err, keys) {
    if (err) return callback(err);

    keys = !prefix ? keys : keys.map(function (key) {
                              return key.replace(prefix, '');
                            });

    if (!keys || keys.length === 0) {
      return callback(null, {});
    }

    async.map(keys, function (key, done) {
      var instance = key.substr(self._bucket_name.length + 1);

      self._redis.hgetall(key, function (err, bucket) {
        if (err && err.message.indexOf('WRONGTYPE Operation against') > -1) {
          return self._redis.get(key, function (err, content) {
            var result = {
              instance: instance,
              lastDrip: 0,
              content:  parseInt(content, 10)
            };

            done(null, result);
          });
        }

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

        var result = {
          instance: instance,
          lastDrip: parseInt(bucket.last_drip, 10),
          content:  parseInt(bucket.content, 10)
        };

        done(null, result);
      });
    }, function (err, results) {
      if (err) return callback(err);
      var result = results.reduce(function (result, current) {
        result[current.instance] = current;
        return result;
      }, {});

      callback(null, result);
    });

  });
};

module.exports = function (dbOptions) {
  var redis;

  if (Array.isArray(dbOptions.nodes)) {
    redis = new Redis.Cluster(dbOptions.nodes, _.omit(dbOptions, ['nodes']));
  } else {
    redis = new Redis(dbOptions);
  }

  redis.defineCommand('drip_and_take', {
    lua: DRIP_AND_TAKE_LUA_SCRIPT,
    numberOfKeys: 1
  });

  redis.defineCommand('take_from_fixed', {
    lua: TAKE_FROM_FIXED_LUA_SCRIPT,
    numberOfKeys: 1
  });

  // a subscriber needs to be in a different connection
  var redis_subscriber = new Redis(dbOptions);
  redis_subscriber.psubscribe('__key*__:*', _.noop);

  return {
    create: function (bucket_name) {
      return new BucketDB(bucket_name, redis, redis_subscriber);
    }
  };
};<|endoftext|>


<commit_before>/**
 * @file:   Resolver class capable of identifying nodes in a given tree by
 *          pattern matching.
 *
 * @see:    * Sebastian Rönnau, Christian Pauli, and Uwe M. Borghoff. Merging
 *            changes in XML documents using reliable context fingerprints:
 *            http://dx.doi.org/10.1145/1410140.1410151
 *          * Original Sourcecode:
 *            https://launchpad.net/xcc
 *
 * @module  resolver
 */

/** @ignore */
var tree = require('./tree.js');

/** @ignore */
var contextmatcher = require('./contextmatcher.js');

/**
 * Constructor for ContextResolver instances.
 *
 * @param refnode   A tree.Node, typically the root node
 * @param nodeindex An index class capable of accessing nodes by offset to
 *                  other nodes. Typically an instance of
 *                  DocumentOrderIndex should be used for this purpose.
 * @param radius    The search radius for the fuzzy matching algorithm
 * @param threshold The threshold of the fuzzy matching algorithm. A value
 *                  between 0.5 and 1. The authors of the xcc patching
 *                  algorithm recommend 0.7.
 * @param matcher   (optional) A matcher instance. Defaults to a
 *                  WeightedContextMatcher with radius=4.
 *
 * @constructor
 */
function ContextResolver(refnode, nodeindex, radius, threshold, matcher) {
    this.refnode = refnode;
    this.nodeindex = nodeindex;

    if (typeof radius === 'undefined') {
        radius = 4;
    }
    this.r = radius;

    if (typeof threshold === 'undefined') {
        threshold = 0.7;
    }
    this.t = threshold;

    this.matcher = matcher || new contextmatcher.WeightedContextMatcher(4);

    // Install custom equality tester for content in matcher
    this.matcher.equalContent = (function(that){
        return function(subject, offset, value) {
            return that.equalContent(that.nodeindex.get(subject, offset), value);
        };
    }(this));
    // Install custom equality tester for context in matcher
    this.matcher.equalContext = (function(that){
        return function(subject, offset, value) {
            return that.equalContext(that.nodeindex.get(subject, offset), value);
        };
    }(this));

    this.resolver = new exports.TopDownPathResolver(refnode);
}

/**
 * Compare a document node against a content node from the patch. Return
 * true if the docnode matches the patnode.
 *
 * Override this method if you use something different than the value
 * property of tree.Node.
 *
 * @param docnode   A candidate node from the document
 * @param patnode   A body-node from the pattern
 */
ContextResolver.prototype.equalContent = function(docnode, patnode) {
    return docnode === undefined ? patnode === undefined :
        docnode && patnode && docnode.value === patnode.value;
};

/**
 * Compare a document node against a context node value. Return true if
 * the value of docnode matches the pattern value.
 *
 * Override this method if you use something different than the value
 * property of tree.Node.
 *
 * @param docnode   A candidate node from the document
 * @param patnode   The value from a context node
 */
ContextResolver.prototype.equalContext = function(docnode, value) {
    return docnode === undefined ? value === undefined :
        docnode.value === value;
};

/**
 * Locate a node at the given path starting at refnode. Try to locate the
 * target within a given radius using the fingerprint values if direct
 * lookup failed.
 *
 * @param   path        An array of numbers. Each value represents an index
 *                      into the childrens of a node in top-down order.
 * @param   body        An array containing the node sequence in question.
 *                      When resolving the location of insert operations,
 *                      the array is empty.  For updates, the array will
 *                      consist of exactly one node. Remove operations may
 *                      consist of one or more nodes.
 * @param   head        Leading context: An array containing the values of
 *                      leading nodes in the same generation.
 * @param   tail        Trailing context: An array containing the values of
 *                      trailing nodes in the same generation.
 *
 * @return array of nodes or undefined on failure.
 */
ContextResolver.prototype.find = function(path, body, head, tail) {
    var nodes, node, siblings, lastsib, i, q = 0, f, bestnode, result, flatbody;

    // top down resolver
    nodes = this.resolver.resolve(path);

    // resort to the last node in the subtree under the preceeding sibling if
    // top-down resolver did not came through to the very last path component.
    if (nodes.length === path.length) {
        siblings = nodes[path.length-1].children;
        lastsib = siblings[siblings.length-1];
        if (lastsib) {
            nodes.push(this.nodeindex.get(lastsib,
                        this.nodeindex.size(lastsib) - 1));
        }
    }

    node = nodes.pop();

    // concatenate all nodes contained in body into one array
    flatbody = [];
    body.forEach(function(n) {
        Array.prototype.push.apply(flatbody, this.nodeindex.flatten(n));
    }, this);

    // context verification and fuzzy matching
    if (node) {
        this.matcher.setPattern(flatbody, head, tail);
        for (i = -this.r; i <= this.r; i++) {
            f = this.matcher.matchQuality(node, i);
            if (f > q && f >= this.t) {
                q = f;
                bestnode = this.nodeindex.get(node, i);
            }
        }
    }

    // construct the trail of nodes up to refnode and return it
    for (result = []; bestnode; bestnode = bestnode.par) {
        result.unshift(bestnode);

        if (bestnode === this.refnode) {
            return result;
        }
    }
};

/**
 * Create a new instance of top-down path resolver
 *
 * @constructor
 */
function TopDownPathResolver(refnode) {
    this.refnode = refnode;
}

/**
 * Try to resolve the given path top-down. Return an array of nodes which
 * were resolved properly.
 *
 * @param path  Array of integers
 * @returns The tree.Node at path or undefined if it does not exist.
 */
TopDownPathResolver.prototype.resolve = function(path) {
    var node = this.refnode,
        result = [node],
        i;

    for (i = 0; i < path.length; i++) {
        node = node.children[path[i]];
        if (!node) {
            break;
        }
        result.push(node);
    }

    return result;
};

exports.ContextResolver = ContextResolver;
exports.TopDownPathResolver = TopDownPathResolver;
<commit_msg>Add parameter to resolver.find in order to pass in operation type, call content/context callbacks with this flag as a parameter<commit_after>/**
 * @file:   Resolver class capable of identifying nodes in a given tree by
 *          pattern matching.
 *
 * @see:    * Sebastian Rönnau, Christian Pauli, and Uwe M. Borghoff. Merging
 *            changes in XML documents using reliable context fingerprints:
 *            http://dx.doi.org/10.1145/1410140.1410151
 *          * Original Sourcecode:
 *            https://launchpad.net/xcc
 *
 * @module  resolver
 */

/** @ignore */
var tree = require('./tree.js');

/** @ignore */
var contextmatcher = require('./contextmatcher.js');

/**
 * Constructor for ContextResolver instances.
 *
 * @param refnode   A tree.Node, typically the root node
 * @param nodeindex An index class capable of accessing nodes by offset to
 *                  other nodes. Typically an instance of
 *                  DocumentOrderIndex should be used for this purpose.
 * @param radius    The search radius for the fuzzy matching algorithm
 * @param threshold The threshold of the fuzzy matching algorithm. A value
 *                  between 0.5 and 1. The authors of the xcc patching
 *                  algorithm recommend 0.7.
 * @param matcher   (optional) A matcher instance. Defaults to a
 *                  WeightedContextMatcher with radius=4.
 *
 * @constructor
 */
function ContextResolver(refnode, nodeindex, radius, threshold, matcher) {
    this.refnode = refnode;
    this.nodeindex = nodeindex;

    if (typeof radius === 'undefined') {
        radius = 4;
    }
    this.r = radius;

    if (typeof threshold === 'undefined') {
        threshold = 0.7;
    }
    this.t = threshold;

    this.matcher = matcher || new contextmatcher.WeightedContextMatcher(4);

    // Install custom equality tester for matcher
    this.matcher.equal = (function(that){
        return function(subject, offset, value, flag) {
            if (flag) {
                return that.equalContent(that.nodeindex.get(subject, offset), value, flag);
            }
            else {
                return that.equalContext(that.nodeindex.get(subject, offset), value);
            }
        };
    }(this));

    this.resolver = new exports.TopDownPathResolver(refnode);
}

/**
 * Compare a document node against a content node from the patch. Return
 * true if the docnode matches the patnode.
 *
 * Override this method if you use something different than the value
 * property of tree.Node.
 *
 * @param docnode   A candidate node from the document
 * @param patnode   A body-node from the pattern
 */
ContextResolver.prototype.equalContent = function(docnode, patnode) {
    return docnode === undefined ? patnode === undefined :
        docnode && patnode && docnode.value === patnode.value;
};

/**
 * Compare a document node against a context node value. Return true if
 * the value of docnode matches the pattern value.
 *
 * Override this method if you use something different than the value
 * property of tree.Node.
 *
 * @param docnode   A candidate node from the document
 * @param patnode   The value from a context node
 */
ContextResolver.prototype.equalContext = function(docnode, value) {
    return docnode === undefined ? value === undefined :
        docnode.value === value;
};

/**
 * Locate a node at the given path starting at refnode. Try to locate the
 * target within a given radius using the fingerprint values if direct
 * lookup failed.
 *
 * @param   path        An array of numbers. Each value represents an index
 *                      into the childrens of a node in top-down order.
 * @param   body        An array containing the node sequence in question.
 *                      When resolving the location of insert operations,
 *                      the array is empty.  For updates, the array will
 *                      consist of exactly one node. Remove operations may
 *                      consist of one or more nodes.
 * @param   head        Leading context: An array containing the values of
 *                      leading nodes in the same generation.
 * @param   tail        Trailing context: An array containing the values of
 *                      trailing nodes in the same generation.
 * @param   type        Operation type. This parameter is passed to the
 *                      equalContent callback.
 *
 * @return array of nodes or undefined on failure.
 */
ContextResolver.prototype.find = function(path, body, head, tail, type) {
    var nodes, node, siblings, lastsib, i, q = 0, f, bestnode, result, flatbody;

    // Need a trueish value in order to differentiate context from content
    if (typeof type === 'undefined') {
        type = true;
    }

    // top down resolver
    nodes = this.resolver.resolve(path);

    // resort to the last node in the subtree under the preceeding sibling if
    // top-down resolver did not came through to the very last path component.
    if (nodes.length === path.length) {
        siblings = nodes[path.length-1].children;
        lastsib = siblings[siblings.length-1];
        if (lastsib) {
            nodes.push(this.nodeindex.get(lastsib,
                        this.nodeindex.size(lastsib) - 1));
        }
    }

    node = nodes.pop();

    // concatenate all nodes contained in body into one array
    flatbody = [];
    body.forEach(function(n) {
        Array.prototype.push.apply(flatbody, this.nodeindex.flatten(n));
    }, this);

    // context verification and fuzzy matching
    if (node) {
        this.matcher.setPattern(flatbody, head, tail);
        for (i = -this.r; i <= this.r; i++) {
            f = this.matcher.matchQuality(node, i, type);
            if (f > q && f >= this.t) {
                q = f;
                bestnode = this.nodeindex.get(node, i);
            }
        }
    }

    // construct the trail of nodes up to refnode and return it
    for (result = []; bestnode; bestnode = bestnode.par) {
        result.unshift(bestnode);

        if (bestnode === this.refnode) {
            return result;
        }
    }
};

/**
 * Create a new instance of top-down path resolver
 *
 * @constructor
 */
function TopDownPathResolver(refnode) {
    this.refnode = refnode;
}

/**
 * Try to resolve the given path top-down. Return an array of nodes which
 * were resolved properly.
 *
 * @param path  Array of integers
 * @returns The tree.Node at path or undefined if it does not exist.
 */
TopDownPathResolver.prototype.resolve = function(path) {
    var node = this.refnode,
        result = [node],
        i;

    for (i = 0; i < path.length; i++) {
        node = node.children[path[i]];
        if (!node) {
            break;
        }
        result.push(node);
    }

    return result;
};

exports.ContextResolver = ContextResolver;
exports.TopDownPathResolver = TopDownPathResolver;
<|endoftext|>


<commit_before>'use strict';
const Discord = require('discord.js');

module.exports = function (utils, options, path) {

    var discord = new Discord.Client(); //New discord client
    var discordReconnectCount = 0; //Reconnect count

    discordConnect(); //Connect on startup

    function discordConnect() { //Attempt to connect to Discord
        discord.loginWithToken(options.bot_token, (err) => { //Use bot token
            utils.log("Connecting to Discord...");
            if (err) {
                utils.log("error: " + JSON.stringify(err));
                discordReconnect(); //Reconnect
            }
        });
    }

    discord.on('ready', () => { //The discord client has connected
        utils.log("Successful connection to Discord as '" + discord.user.username + "'");
        discordReconnectCount = 0; //Reset reconnect count
    });

    discord.on('serverCreated', (server) => { //The client has joined a server
        utils.log(discord.user.username + " joined Discord server: '" + server.name + "'");
    });

    discord.on('serverDeleted', (server) => { //The client has left a server
        utils.log(discord.user.username + " parted Discord server: '" + server.name + "'");
    });

    discord.on('disconnected', () => { //Discord was disconnected
        utils.log("Discord disconnected");
        discordReconnect(); //Reconnect
    });

    discord.on('error', (err) => { //Log any Discord Errors
        utils.log("Discord error: " + JSON.stringify(err));
    });

    function discordReconnect() { //Reconnect to Discord
        discordReconnectCount++; //Add to count
        if (discordReconnectCount >= 100) { //Exit if reconnection attempts is over 100
            utils.log("Discord reconnect failed an excessive amount - terminating script");
            process.exit();
        } else { //Wait and reconnect after x seconds
            utils.log("Connection to Discord failed");
            utils.log("Reconnecting to Discord in " + (discordReconnectCount - 1 ) * 10 + " seconds");
            setTimeout(discordConnect, 1000 * (discordReconnectCount - 1) * 10);
        }
    }

    function typeMessage(message, str, callback) {
        discord.startTyping(message, () => {
            setTimeout(() => {
                discord.stopTyping(message, () => {
                    discord.sendMessage(message, str, (err) => {
                        if (callback) (err) ? callback(err) : callback();
                    });
                });
            }, 1000);
        });
    }

    function quickMessage(message, str, callback) {
        discord.sendMessage(message, str, () => {
            if (callback) callback();
        });
    }

    function typeReply(message, str, callback) {
        discord.startTyping(message);
        setTimeout(() => {
            discord.reply(message, str, () => {
                if (callback) callback();
            });
            discord.stopTyping(message);
        }, str.length + 250);
    }

    function quickReply(message, str, callback) {
        discord.reply(message, str, () => {
            if (callback) callback();
        });
    }

    function typeFile(message, path, name, callback) {
        discord.startTyping(message, () => {
            setTimeout(() => {
                discord.stopTyping(message, () => {
                    discord.sendFile(message, path, name, () => {
                        if (callback) callback();
                    });
                });
            }, 500);
        });
    }

    function quickFile(message, path, name, callback) {
        discord.sendFile(message, path, name, () => {
            if (callback) callback();
        });
    }

    return {
        discord: discord,
        typeMessage: typeMessage,
        quickMessage: quickMessage,
        typeReply: typeReply,
        quickReply: quickReply,
        typeFile: typeFile,
        quickFile: quickFile
    };
};
<commit_msg>Altered the other typing methods to be more reliable<commit_after>'use strict';
const Discord = require('discord.js');

module.exports = function (utils, options, path) {

    var discord = new Discord.Client(); //New discord client
    var discordReconnectCount = 0; //Reconnect count

    discordConnect(); //Connect on startup

    function discordConnect() { //Attempt to connect to Discord
        discord.loginWithToken(options.bot_token, (err) => { //Use bot token
            utils.log("Connecting to Discord...");
            if (err) {
                utils.log("error: " + JSON.stringify(err));
                discordReconnect(); //Reconnect
            }
        });
    }

    discord.on('ready', () => { //The discord client has connected
        utils.log("Successful connection to Discord as '" + discord.user.username + "'");
        discordReconnectCount = 0; //Reset reconnect count
    });

    discord.on('serverCreated', (server) => { //The client has joined a server
        utils.log(discord.user.username + " joined Discord server: '" + server.name + "'");
    });

    discord.on('serverDeleted', (server) => { //The client has left a server
        utils.log(discord.user.username + " parted Discord server: '" + server.name + "'");
    });

    discord.on('disconnected', () => { //Discord was disconnected
        utils.log("Discord disconnected");
        discordReconnect(); //Reconnect
    });

    discord.on('error', (err) => { //Log any Discord Errors
        utils.log("Discord error: " + JSON.stringify(err));
    });

    function discordReconnect() { //Reconnect to Discord
        discordReconnectCount++; //Add to count
        if (discordReconnectCount >= 100) { //Exit if reconnection attempts is over 100
            utils.log("Discord reconnect failed an excessive amount - terminating script");
            process.exit();
        } else { //Wait and reconnect after x seconds
            utils.log("Connection to Discord failed");
            utils.log("Reconnecting to Discord in " + (discordReconnectCount - 1 ) * 10 + " seconds");
            setTimeout(discordConnect, 1000 * (discordReconnectCount - 1) * 10);
        }
    }

    function typeMessage(message, str, callback) {
        discord.startTyping(message);
        setTimeout(() => {
            discord.sendMessage(message, str, (err, result) => {
                if (callback) callback(err, result);
            });
            discord.stopTyping(message);
        }, str.length + 250);
    }

    function quickMessage(message, str, callback) {
        discord.sendMessage(message, str, (err, result) => {
            if (callback) callback(err, result);
        });
    }

    function typeReply(message, str, callback) {
        discord.startTyping(message);
        setTimeout(() => {
            discord.reply(message, str, (err, result) => {
                if (callback) callback(err, result);
            });
            discord.stopTyping(message);
        }, str.length + 250);
    }

    function quickReply(message, str, callback) {
        discord.reply(message, str, (err, result) => {
            if (callback) callback(err, result);
        });
    }

    function typeFile(message, path, name, callback) {
        discord.startTyping(message);
        setTimeout(() => {
            discord.sendFile(message, path, name, (err, result) => {
                if (callback) callback(err, result);
            });
            discord.stopTyping(message);
        }, 1000);
    }

    function quickFile(message, path, name, callback) {
        discord.sendFile(message, path, name, (err, result) => {
            if (callback) callback(err, result);
        });
    }

    return {
        discord: discord,
        typeMessage: typeMessage,
        quickMessage: quickMessage,
        typeReply: typeReply,
        quickReply: quickReply,
        typeFile: typeFile,
        quickFile: quickFile
    };
};
<|endoftext|>