<commit_before>let chokidar = require('chokidar');
let path = require('path');
const _ = require('underscore');

let fs = require('../core/fs.js');

// TODO: this should be receiving the config object not re-reading the
// embark.json file
class Watch {
  constructor(options) {
    this.logger = options.logger;
    this.events = options.events;
    this.fileWatchers = [];
  }

  start() {
    let self = this;
    // TODO: should come from the config object instead of reading the file
    // directly
    let embarkConfig = fs.readJSONSync("embark.json");

    this.watchAssets(embarkConfig, function () {
      self.logger.trace('ready to watch asset changes');
    });

    this.watchContracts(embarkConfig, function () {
      self.logger.trace('ready to watch contract changes');
    });

    this.watchConfigs(embarkConfig, function () {
      self.logger.trace('ready to watch config changes');
    });

    this.logger.info(__("ready to watch file changes"));
  }

  restart() {
    this.stop();
    this.start();
  }

  stop() {
    this.fileWatchers.forEach(fileWatcher => {
      if (fileWatcher.shouldClose) return;
      if (fileWatcher.isReady) fileWatcher.close();
      fileWatcher.shouldClose = true;
    });
  }

  watchAssets(embarkConfig, callback) {
    let self = this;
    let appConfig = embarkConfig.app;
    let filesToWatch = [];

    for (let targetFile in appConfig) {
      let files = appConfig[targetFile];
      let fileGlob = files;

      // workaround for imports issue
      // so embark reacts to changes made in imported js files
      // chokidar glob patterns only work with front-slashes
      if (!Array.isArray(files)) {
        fileGlob = path.join(path.dirname(files), '**', '*.*').replace(/\\/g, '/');
      } else if (files.length === 1) {
        fileGlob = path.join(path.dirname(files[0]), '**', '*.*').replace(/\\/g, '/');
      }

      filesToWatch.push(fileGlob);
    }
    filesToWatch = _.uniq(filesToWatch);

    this.watchFiles(
      filesToWatch,
      function (eventName, path) {
        self.logger.info(`${eventName}: ${path}`);
        self.events.emit('file-' + eventName, 'asset', path);
        self.events.emit('file-event', 'asset', path);
      },
      function () {
        callback();
      }
    );
  }

  watchContracts(embarkConfig, callback) {
    let self = this;
    this.watchFiles(
      [embarkConfig.contracts],
      function (eventName, path) {
        self.logger.info(`${eventName}: ${path}`);
        self.events.emit('file-' + eventName, 'contract', path);
        self.events.emit('file-event', 'contract', path);
      },
      function () {
        callback();
      }
    );
  }

  watchConfigs(embarkConfig, callback) {
    let self = this;
    let contractsConfig;
    if (typeof embarkConfig.config === 'object' || embarkConfig.config.contracts) {
      contractsConfig = embarkConfig.config.contracts;
    } else {
      let contractsFolder = embarkConfig.config.replace(/\\/g, '/');
      if (contractsFolder.charAt(contractsFolder.length - 1) !== '/') {
        contractsFolder += '/';
      }
      contractsConfig = [`${contractsFolder}**/contracts.json`, `${contractsFolder}**/contracts.js`]
    }
    this.watchFiles(contractsConfig,
      function (eventName, path) {
        self.logger.info(`${eventName}: ${path}`);
        self.events.emit('file-' + eventName, 'config', path);
        self.events.emit('file-event', 'config', path);
      },
      function () {
        callback();
      }
    );
  }

  watchFiles(files, changeCallback, doneCallback) {
    this.logger.trace('watchFiles');
    this.logger.trace(files);

    let configWatcher = chokidar.watch(files, {
      ignored: /[\/\\]\.|tmp_/, persistent: true, ignoreInitial: true, followSymlinks: true
    });
    this.fileWatchers.push(configWatcher);

    configWatcher
      .on('add', path => changeCallback('add', path))
      .on('change', path => changeCallback('change', path))
      .on('unlink', path => changeCallback('remove', path))
      .once('ready', () => {
        configWatcher.isReady = true;
        if (configWatcher.shouldClose) configWatcher.close();
        doneCallback();
      });
  }

}

module.exports = Watch;
<commit_msg>sorry senior lint<commit_after>let chokidar = require('chokidar');
let path = require('path');
const _ = require('underscore');

let fs = require('../core/fs.js');

// TODO: this should be receiving the config object not re-reading the
// embark.json file
class Watch {
  constructor(options) {
    this.logger = options.logger;
    this.events = options.events;
    this.fileWatchers = [];
  }

  start() {
    let self = this;
    // TODO: should come from the config object instead of reading the file
    // directly
    let embarkConfig = fs.readJSONSync("embark.json");

    this.watchAssets(embarkConfig, function () {
      self.logger.trace('ready to watch asset changes');
    });

    this.watchContracts(embarkConfig, function () {
      self.logger.trace('ready to watch contract changes');
    });

    this.watchConfigs(embarkConfig, function () {
      self.logger.trace('ready to watch config changes');
    });

    this.logger.info(__("ready to watch file changes"));
  }

  restart() {
    this.stop();
    this.start();
  }

  stop() {
    this.fileWatchers.forEach(fileWatcher => {
      if (fileWatcher.shouldClose) return;
      if (fileWatcher.isReady) fileWatcher.close();
      fileWatcher.shouldClose = true;
    });
  }

  watchAssets(embarkConfig, callback) {
    let self = this;
    let appConfig = embarkConfig.app;
    let filesToWatch = [];

    for (let targetFile in appConfig) {
      let files = appConfig[targetFile];
      let fileGlob = files;

      // workaround for imports issue
      // so embark reacts to changes made in imported js files
      // chokidar glob patterns only work with front-slashes
      if (!Array.isArray(files)) {
        fileGlob = path.join(path.dirname(files), '**', '*.*').replace(/\\/g, '/');
      } else if (files.length === 1) {
        fileGlob = path.join(path.dirname(files[0]), '**', '*.*').replace(/\\/g, '/');
      }

      filesToWatch.push(fileGlob);
    }
    filesToWatch = _.uniq(filesToWatch);

    this.watchFiles(
      filesToWatch,
      function (eventName, path) {
        self.logger.info(`${eventName}: ${path}`);
        self.events.emit('file-' + eventName, 'asset', path);
        self.events.emit('file-event', 'asset', path);
      },
      function () {
        callback();
      }
    );
  }

  watchContracts(embarkConfig, callback) {
    let self = this;
    this.watchFiles(
      [embarkConfig.contracts],
      function (eventName, path) {
        self.logger.info(`${eventName}: ${path}`);
        self.events.emit('file-' + eventName, 'contract', path);
        self.events.emit('file-event', 'contract', path);
      },
      function () {
        callback();
      }
    );
  }

  watchConfigs(embarkConfig, callback) {
    let self = this;
    let contractsConfig;
    if (typeof embarkConfig.config === 'object' || embarkConfig.config.contracts) {
      contractsConfig = embarkConfig.config.contracts;
    } else {
      let contractsFolder = embarkConfig.config.replace(/\\/g, '/');
      if (contractsFolder.charAt(contractsFolder.length - 1) !== '/') {
        contractsFolder += '/';
      }
      contractsConfig = [`${contractsFolder}**/contracts.json`, `${contractsFolder}**/contracts.js`];
    }
    this.watchFiles(contractsConfig,
      function (eventName, path) {
        self.logger.info(`${eventName}: ${path}`);
        self.events.emit('file-' + eventName, 'config', path);
        self.events.emit('file-event', 'config', path);
      },
      function () {
        callback();
      }
    );
  }

  watchFiles(files, changeCallback, doneCallback) {
    this.logger.trace('watchFiles');
    this.logger.trace(files);

    let configWatcher = chokidar.watch(files, {
      ignored: /[\/\\]\.|tmp_/, persistent: true, ignoreInitial: true, followSymlinks: true
    });
    this.fileWatchers.push(configWatcher);

    configWatcher
      .on('add', path => changeCallback('add', path))
      .on('change', path => changeCallback('change', path))
      .on('unlink', path => changeCallback('remove', path))
      .once('ready', () => {
        configWatcher.isReady = true;
        if (configWatcher.shouldClose) configWatcher.close();
        doneCallback();
      });
  }

}

module.exports = Watch;
<|endoftext|>


<commit_before>module.exports.addLink = addLink
module.exports.addLinks = addLinks
module.exports.parseMetadataFromHeader = parseMetadataFromHeader
module.exports.linksHandler = linksHandler
module.exports.addPermissions = addPermissions

var li = require('li')
var path = require('path')
var S = require('string')
var metadata = require('./metadata.js')
var debug = require('./debug.js')
var utils = require('./utils.js')
var error = require('./http-error')

const MODES = ['Read', 'Write', 'Append', 'Control']
const PERMISSIONS = MODES.map(m => m.toLowerCase())

function addLink (res, value, rel) {
  var oldLink = res.get('Link')
  if (oldLink === undefined) {
    res.set('Link', '<' + value + '>; rel="' + rel + '"')
  } else {
    res.set('Link', oldLink + ', ' + '<' + value + '>; rel="' + rel + '"')
  }
}

function addLinks (res, fileMetadata) {
  if (fileMetadata.isResource) {
    addLink(res, 'http://www.w3.org/ns/ldp#Resource', 'type')
  }
  if (fileMetadata.isSourceResource) {
    addLink(res, 'http://www.w3.org/ns/ldp#RDFSource', 'type')
  }
  if (fileMetadata.isContainer) {
    addLink(res, 'http://www.w3.org/ns/ldp#Container', 'type')
  }
  if (fileMetadata.isBasicContainer) {
    addLink(res, 'http://www.w3.org/ns/ldp#BasicContainer', 'type')
  }
  if (fileMetadata.isDirectContainer) {
    addLink(res, 'http://www.w3.org/ns/ldp#DirectContainer', 'type')
  }
}

function linksHandler (req, res, next) {
  var ldp = req.app.locals.ldp
  var root = !ldp.multiuser ? ldp.root : ldp.root + req.hostname + '/'
  var filename = utils.uriToFilename(req.url, root)

  filename = path.join(filename, req.path)
  if (path.extname(filename) === ldp.suffixMeta) {
    debug.metadata('Trying to access metadata file as regular file.')

    return next(error(404, 'Trying to access metadata file as regular file'))
  }
  var fileMetadata = new metadata.Metadata()
  if (S(filename).endsWith('/')) {
    fileMetadata.isContainer = true
    fileMetadata.isBasicContainer = true
  } else {
    fileMetadata.isResource = true
  }
  // Add LDP-required Accept-Post header for OPTIONS request to containers
  if (fileMetadata.isContainer && req.method === 'OPTIONS') {
    res.header('Accept-Post', '*/*')
  }
  // Add ACL and Meta Link in header
  addLink(res, utils.pathBasename(req.path) + ldp.suffixAcl, 'acl')
  addLink(res, utils.pathBasename(req.path) + ldp.suffixMeta, 'describedBy')
  // Add other Link headers
  addLinks(res, fileMetadata)
  next()
}

function parseMetadataFromHeader (linkHeader) {
  var fileMetadata = new metadata.Metadata()
  if (linkHeader === undefined) {
    return fileMetadata
  }
  var links = linkHeader.split(',')
  for (var linkIndex in links) {
    var link = links[linkIndex]
    var parsedLinks = li.parse(link)
    for (var rel in parsedLinks) {
      if (rel === 'type') {
        if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#Resource') {
          fileMetadata.isResource = true
        } else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#RDFSource') {
          fileMetadata.isSourceResource = true
        } else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#Container') {
          fileMetadata.isContainer = true
        } else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#BasicContainer') {
          fileMetadata.isBasicContainer = true
        } else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#DirectContainer') {
          fileMetadata.isDirectContainer = true
        }
      }
    }
  }
  return fileMetadata
}

// Adds a header that describes the user's permissions
function addPermissions (req, res, next) {
  const { acl, session } = req
  if (!acl) return next()

  // Turn permissions for the public and the user into a header
  const resource = utils.getFullUri(req)
  Promise.all([
    getPermissionsFor(acl, null, resource),
    getPermissionsFor(acl, session.userId, resource)
  ])
  .then(([publicPerms, userPerms]) => {
    debug.ACL(`Permissions for ${session.userId || '(none)'}: ${userPerms}`)
    debug.ACL(`Permissions for public: ${publicPerms}`)
    res.set('WAC-Allow', `user="${userPerms}",public="${publicPerms}"`)
  })
  .then(next, next)
}

// Gets the permissions string for the given user and resource
function getPermissionsFor (acl, user, resource) {
  return Promise.all(MODES.map(mode => acl.can(user, mode).catch(e => false)))
  .then(allowed => PERMISSIONS.filter((_, i) => allowed[i]).join(' '))
}
<commit_msg>Log resource on permissions check.<commit_after>module.exports.addLink = addLink
module.exports.addLinks = addLinks
module.exports.parseMetadataFromHeader = parseMetadataFromHeader
module.exports.linksHandler = linksHandler
module.exports.addPermissions = addPermissions

var li = require('li')
var path = require('path')
var S = require('string')
var metadata = require('./metadata.js')
var debug = require('./debug.js')
var utils = require('./utils.js')
var error = require('./http-error')

const MODES = ['Read', 'Write', 'Append', 'Control']
const PERMISSIONS = MODES.map(m => m.toLowerCase())

function addLink (res, value, rel) {
  var oldLink = res.get('Link')
  if (oldLink === undefined) {
    res.set('Link', '<' + value + '>; rel="' + rel + '"')
  } else {
    res.set('Link', oldLink + ', ' + '<' + value + '>; rel="' + rel + '"')
  }
}

function addLinks (res, fileMetadata) {
  if (fileMetadata.isResource) {
    addLink(res, 'http://www.w3.org/ns/ldp#Resource', 'type')
  }
  if (fileMetadata.isSourceResource) {
    addLink(res, 'http://www.w3.org/ns/ldp#RDFSource', 'type')
  }
  if (fileMetadata.isContainer) {
    addLink(res, 'http://www.w3.org/ns/ldp#Container', 'type')
  }
  if (fileMetadata.isBasicContainer) {
    addLink(res, 'http://www.w3.org/ns/ldp#BasicContainer', 'type')
  }
  if (fileMetadata.isDirectContainer) {
    addLink(res, 'http://www.w3.org/ns/ldp#DirectContainer', 'type')
  }
}

function linksHandler (req, res, next) {
  var ldp = req.app.locals.ldp
  var root = !ldp.multiuser ? ldp.root : ldp.root + req.hostname + '/'
  var filename = utils.uriToFilename(req.url, root)

  filename = path.join(filename, req.path)
  if (path.extname(filename) === ldp.suffixMeta) {
    debug.metadata('Trying to access metadata file as regular file.')

    return next(error(404, 'Trying to access metadata file as regular file'))
  }
  var fileMetadata = new metadata.Metadata()
  if (S(filename).endsWith('/')) {
    fileMetadata.isContainer = true
    fileMetadata.isBasicContainer = true
  } else {
    fileMetadata.isResource = true
  }
  // Add LDP-required Accept-Post header for OPTIONS request to containers
  if (fileMetadata.isContainer && req.method === 'OPTIONS') {
    res.header('Accept-Post', '*/*')
  }
  // Add ACL and Meta Link in header
  addLink(res, utils.pathBasename(req.path) + ldp.suffixAcl, 'acl')
  addLink(res, utils.pathBasename(req.path) + ldp.suffixMeta, 'describedBy')
  // Add other Link headers
  addLinks(res, fileMetadata)
  next()
}

function parseMetadataFromHeader (linkHeader) {
  var fileMetadata = new metadata.Metadata()
  if (linkHeader === undefined) {
    return fileMetadata
  }
  var links = linkHeader.split(',')
  for (var linkIndex in links) {
    var link = links[linkIndex]
    var parsedLinks = li.parse(link)
    for (var rel in parsedLinks) {
      if (rel === 'type') {
        if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#Resource') {
          fileMetadata.isResource = true
        } else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#RDFSource') {
          fileMetadata.isSourceResource = true
        } else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#Container') {
          fileMetadata.isContainer = true
        } else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#BasicContainer') {
          fileMetadata.isBasicContainer = true
        } else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#DirectContainer') {
          fileMetadata.isDirectContainer = true
        }
      }
    }
  }
  return fileMetadata
}

// Adds a header that describes the user's permissions
function addPermissions (req, res, next) {
  const { acl, session } = req
  if (!acl) return next()

  // Turn permissions for the public and the user into a header
  const resource = utils.getFullUri(req)
  Promise.all([
    getPermissionsFor(acl, null, resource),
    getPermissionsFor(acl, session.userId, resource)
  ])
  .then(([publicPerms, userPerms]) => {
    debug.ACL(`Permissions on ${resource} for ${session.userId || '(none)'}: ${userPerms}`)
    debug.ACL(`Permissions on ${resource} for public: ${publicPerms}`)
    res.set('WAC-Allow', `user="${userPerms}",public="${publicPerms}"`)
  })
  .then(next, next)
}

// Gets the permissions string for the given user and resource
function getPermissionsFor (acl, user, resource) {
  return Promise.all(MODES.map(mode => acl.can(user, mode).catch(e => false)))
  .then(allowed => PERMISSIONS.filter((_, i) => allowed[i]).join(' '))
}
<|endoftext|>


<commit_before>/*
 * openi_data_api
 * openi-ict.eu
 *
 * Copyright (c) 2013 dmccarthy
 */

'use strict';

var openiLogger  = require('openi-logger');
var openiUtils   = require('openi-cloudlet-utils');
var zmq          = require('m2nodehandler');
var url          = require('url');
var validator    = require('./validator.js');
var filter       = require('./filter.js');
var rrd          = require('openi_rrd');
var util         = require('util');
var tracklet     = require('tracklet');
var async        = require('async');
var _            = require('lodash');
var couchbase    = require('couchbase');
var cluster      = new couchbase.Cluster('couchbase://127.0.0.1');
var userBucket   = cluster.openBucket('users');
var objectBucket = cluster.openBucket('objects');
var typeBucket   = cluster.openBucket('types');
var ViewQuery    = couchbase.ViewQuery;

var init = function(logger_params, tracklet_config){
   this.logger = openiLogger(logger_params);
   tracklet.config(tracklet_config);
};

var genPostMessage = function(msg, senderToDao, senderToClient, terminal_handler){

   var objectId    = openiUtils.generateUUID("object");
   var cloudletId  = openiUtils.extractCloudletId(msg.path);
   var third_party = msg.token.context;

   cloudletId = (null === cloudletId) ? msg.cloudletId : cloudletId;

   if (!openiUtils.isCloudletId(cloudletId)){
         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error":"Invalid Cloudlet id" });
   }
   else if (cloudletId !== msg.cloudletId){
      senderToClient.send(msg.uuid, msg.connId, zmq.status.UNAUTHORIZED_401, zmq.standard_headers.json, {"error":"Unauthorised access" });
      return
   }
   else if (null === msg.json || undefined == msg.json){
      senderToClient.send(msg.uuid, msg.connId, zmq.status.ERROR, zmq.standard_headers.json, {"error":"Empty message body" });
      return
   }

   var type = msg.json['@openi_type'];
   var dbName            = cloudletId + '+' + objectId;
   var objectRestURL     = '/api/v1/objects/' + cloudletId + '/' + objectId;
   var typeLocation      = '/api/v1/types/' + type;

   if (!openiUtils.isTypeId(type)) {
      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error" : "Invalid format"});
      return;
   }

   validator.validateObject(cloudletId, msg.headers.host, msg.headers, typeLocation, msg.json['@data'], function(){

         var obj = {
            '@id'             : objectId,
            '@location'       : objectRestURL,
            '@cloudlet'       : cloudletId,
            '@openi_type'     : msg.json['@openi_type'],
            '@type_location'  : typeLocation,
            '@data'           : msg.json['@data'],
            _date_created     : new Date().toJSON(),
            _date_modified    : new Date().toJSON()
         };

         senderToDao.send( {
            'dao_actions'      : [
               {
                  'action'       : 'POST',
                  'database'    : dbName,
                  'object_name' : objectId,
                  'object_data' : obj,
                  'id'          : objectId,
                  'cloudlet_id' : cloudletId,
                  'third_party' : third_party,
                  'bucket'      : 'objects'
               }
            ],
            'mongrel_sink' : terminal_handler,
            'clients'      : [
               {
                  'uuid' : msg.uuid,
                  'connId' : msg.connId
               }
            ]
         });
      },
      function(errorMessage){
         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, errorMessage);
      });
};

var genPutMessage = function(msg, senderToDao, senderToClient, terminal_handler){

   var objectId    = openiUtils.extractObjectId(msg.path);
   var cloudletId  = openiUtils.extractCloudletId(msg.path);
   var third_party = msg.token.context;
   var property   = decodeURIComponent(msg.path.slice(msg.path.indexOf(objectId) + objectId.length + 1));

   if (null === cloudletId){
      cloudletId = msg.cloudletId
   }

   var dbName = cloudletId + '+' + objectId;

//   if (null !==  property && property.length > 0){
//      putWithProperty(dbName, msg, objectId, property, terminal_handler)
//      return
//   }

   var type = msg.json['@openi_type'];
   var typeLocation      = '/api/v1/types/' + type;

   if (undefined === type) {
      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error" : "Invalid format"});
      return;
   }

   var objectRestURL = '/api/v1/objects/' + cloudletId + '/' +  objectId;

   validator.validateObject(cloudletId, msg.headers.host, msg.headers, typeLocation, msg.json['@data'], function(){

         var obj = {
            '@id'            : objectId,
            '@location'      : objectRestURL,
            '@cloudlet'      : cloudletId,
            '@openi_type'    : msg.json['@openi_type'],
            '@type_location' : "https://" + msg.headers.host + "/api/v1/types/" + type,
            '@data'          : msg.json['@data'],
            _date_created    : "",
            'third_party'    : third_party,
            _date_modified   : new Date().toJSON()
         };

         senderToDao.send({
            'dao_actions'      : [
               {
                  'action'       : 'PUT',
                  'database'     : dbName,
                  'revision'     : msg.json['_revision'],
                  'object_data'  : obj,
                  'id'           : objectId,
                  'bucket'      : 'objects'
               }
            ],
            'mongrel_sink' : terminal_handler,
            'clients'      : [
               {
                  'uuid' : msg.uuid,
                  'connId' : msg.connId
               }
            ]
         });
      },
      function(errorMessage){
         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, errorMessage);
      });
};

var genGetMessage = function(msg, senderToDao, senderToClient, terminal_handler){

   var url_parts   = url.parse(msg.headers.URI, true);
   var query       = url_parts.query;
   var cloudletId  = openiUtils.extractCloudletId(msg.path);
   var objectId    = openiUtils.extractObjectId(msg.path);
   var third_party = msg.token.context;
   var userQuery;
   var objectQuery;
   var typeQuery;
   var company;
   var ref;
   var typeID;

   if (null === objectId){
      objectId = openiUtils.extractAttachmentId(msg.path);
   }

   if (null === cloudletId){
      cloudletId = msg.cloudletId
   }

   if (!openiUtils.isCloudletId(cloudletId)){
      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error":"Invalid Cloudlet id" });
   }
   else if (cloudletId !== msg.cloudletId){
      senderToClient.send(msg.uuid, msg.connId, zmq.status.UNAUTHORIZED_401, zmq.standard_headers.json, {"error":"Unauthorised access" });
      return
   }

   if(null !== objectId) {
      if ( !openiUtils.isAttachmentId(objectId) && !openiUtils.isObjectId(objectId) ){
         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error":"Invalid Object id" });
      }

      var property  = decodeURIComponent(msg.path.slice(msg.path.indexOf(objectId) + objectId.length + 1));

      var dbName = cloudletId + '+' + objectId;

      if (query.resolve === true) {
         rrd.monitorIncrement('object_get_resolve')
      }

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'      : 'GET',
               'database'    : dbName,
               'resolve'     : ('true' === query.resolve),
               'resp_type'   : 'object',
               'property'    : property,
               'meta'        : query.meta,
               'third_party' : third_party,
               'bucket'      : 'objects'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid'   : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });

      async.series([
         function(callback){
            userQuery = ViewQuery.from('user_views', 'get_name').key(msg.token.context);

            userBucket.query(userQuery, function(err, results) {

               if (err) {
                  callback(err);
               }

               if (_.isEmpty(results)) {
                  callback('no results');
               } else {
                  company = results[0].value;
                  callback(null, company);
               }
            });

         },
         function(callback){
            objectQuery = ViewQuery.from('objects_views', 'object_data').key(objectId);

            objectBucket.query(objectQuery, function(err, results) {

               if (err) {
                  callback(err);
               }

               if (_.isEmpty(results)) {
                  callback('no results');
               } else {
                  typeID = results[0].value;
                  callback(null, typeID);
               }
            });

         },
         function(callback){
            typeQuery = ViewQuery.from('type_views', 'get_ref').key(typeID);

            typeBucket.query(typeQuery, function(err, results) {

               if (err) {
                  callback(err);
               }

               if (_.isEmpty(results)) {
                  callback('no results');
               } else {
                  ref  = results[0].value;
                  callback(null, ref);
               }
            });

         }],
      function(err, results){
         if (err) {
            console.log(err);
         } else {
            console.log(results);
            console.log(objectId + ref);
            tracklet.track({
               cloudlet: cloudletId,
               app: msg.token.client_id,
               company: company,
               object: objectId,
               objData: ref
            });
         }
      });

   }
   else {
      var limit       = (undefined !== query.limit)  ? Number(query.limit)   : 30;
      var offset      = (undefined !== query.offset) ? Number(query.offset)  :  0;
      var prev        = msg.headers.URI.replace("offset="+offset, "offset="+ (((offset - limit) < 0) ? 0 : (offset - limit)));
      var next        = msg.headers.URI.replace("offset="+offset, "offset="+ (offset + limit));

      var meta = {
         "limit"       : limit,
         "offset"      : offset,
         "total_count" : 0,
         "prev"        : (0 === offset)? null : prev,
         "next"        : next
      };

      //TODO: Dodgy logic?
      if(undefined !== query.property_filter || undefined !== query.with_property){
         filter.filter(msg, meta, senderToClient, third_party, cloudletId);
         return;
      }

      var startKey      = [third_party, cloudletId];
      var endKey        = [third_party, cloudletId + '\uefff'];
      var viewName      = 'object_by_cloudlet_id';

      if (undefined !== query.type) {

         if (!openiUtils.isTypeId(query.type)){
            senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error": "invalid type id format"});
            return;
         }

         viewName = 'object_by_type';
         startKey = [third_party, query.type, cloudletId];
         endKey   = [third_party, query.type, cloudletId + '\uefff' ];
      }

      if (undefined !== query.only_show_properties) {
         query.only_show_properties = query.only_show_properties.split(',');
      }

      if (query.id_only === true) {
         rrd.monitorIncrement('object_get_id_only')
      }

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'      : 'VIEW',
               'start_key'   : startKey ,
               'end_key'     : endKey ,
               'design_doc'  : 'objects_views',
               'view_name'   : viewName,
               'meta'        : meta,
               'filter_show' : query.only_show_properties,
               'resp_type'   : 'object',
               'cloudlet'    : cloudletId,
               'third_party' : third_party,
               'id_only'     : ('true' === query.id_only),
               'bucket'      : 'objects'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
      userQuery = ViewQuery.from('user_views', 'get_name').key(msg.token.context);
      userBucket.query(userQuery, function(err, results) {
         company = results[0].value;

         tracklet.track({
            cloudlet: cloudletId,
            app: msg.token.client_id,
            company: company,
            object: 'all',
            objData: 'all'
         }, function(err) {
            if (err) {
               console.log(err);
            }
         });

      });
   }
};

var genDeleteMessage = function(msg, senderToDao, senderToClient, terminal_handler){

   var cloudletId  = openiUtils.extractCloudletId(msg.path);
   var objectId    = openiUtils.extractObjectId(msg.path);
   var third_party = msg.token.context;

   if (null === cloudletId){
      cloudletId = msg.cloudletId
   }

   var dbName = cloudletId + '+' + objectId;

   senderToDao.send({
      'dao_actions'      : [
         {
            'action'      : 'DELETE',
            'database'    : dbName,
            'bucket'      : 'objects',
            'third_party' : third_party
         }
      ],
      'mongrel_sink' : terminal_handler,
      'clients'      : [
         {
            'uuid' : msg.uuid,
            'connId' : msg.connId
         }
      ]
   });
};

var processMongrel2Message = function (msg, senderToDao, senderToClient, terminal_handler) {

   this.logger.logMongrel2Message(msg);

   //Deletes null variables
   for(var key in msg.json) {
      if(msg.json[key] === null) {
         delete msg.json[key];
      }
   }

   switch(msg.headers['METHOD']) {
   case 'POST':
      genPostMessage(msg, senderToDao, senderToClient, terminal_handler);
      break;
   case 'GET':
      genGetMessage(msg, senderToDao, senderToClient, terminal_handler);
      break;
   case 'PUT':
      genPutMessage(msg, senderToDao, senderToClient, terminal_handler);
      break;
   case 'DELETE':
      genDeleteMessage(msg, senderToDao, senderToClient, terminal_handler);
      break;
   default:
      break;
   }
};

module.exports.init                   = init;
module.exports.processMongrel2Message = processMongrel2Message;
<commit_msg>Stopped tracklet code crashing the server<commit_after>/*
 * openi_data_api
 * openi-ict.eu
 *
 * Copyright (c) 2013 dmccarthy
 */

'use strict';

var openiLogger  = require('openi-logger');
var openiUtils   = require('openi-cloudlet-utils');
var zmq          = require('m2nodehandler');
var url          = require('url');
var validator    = require('./validator.js');
var filter       = require('./filter.js');
var rrd          = require('openi_rrd');
var util         = require('util');
var tracklet     = require('tracklet');
var async        = require('async');
var _            = require('lodash');
var couchbase    = require('couchbase');
var cluster      = new couchbase.Cluster('couchbase://127.0.0.1');
var userBucket   = cluster.openBucket('users');
var objectBucket = cluster.openBucket('objects');
var typeBucket   = cluster.openBucket('types');
var ViewQuery    = couchbase.ViewQuery;

var init = function(logger_params, tracklet_config){
   this.logger = openiLogger(logger_params);
   tracklet.config(tracklet_config);
};

var genPostMessage = function(msg, senderToDao, senderToClient, terminal_handler){

   var objectId    = openiUtils.generateUUID("object");
   var cloudletId  = openiUtils.extractCloudletId(msg.path);
   var third_party = msg.token.context;

   cloudletId = (null === cloudletId) ? msg.cloudletId : cloudletId;

   if (!openiUtils.isCloudletId(cloudletId)){
         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error":"Invalid Cloudlet id" });
   }
   else if (cloudletId !== msg.cloudletId){
      senderToClient.send(msg.uuid, msg.connId, zmq.status.UNAUTHORIZED_401, zmq.standard_headers.json, {"error":"Unauthorised access" });
      return
   }
   else if (null === msg.json || undefined == msg.json){
      senderToClient.send(msg.uuid, msg.connId, zmq.status.ERROR, zmq.standard_headers.json, {"error":"Empty message body" });
      return
   }

   var type = msg.json['@openi_type'];
   var dbName            = cloudletId + '+' + objectId;
   var objectRestURL     = '/api/v1/objects/' + cloudletId + '/' + objectId;
   var typeLocation      = '/api/v1/types/' + type;

   if (!openiUtils.isTypeId(type)) {
      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error" : "Invalid format"});
      return;
   }

   validator.validateObject(cloudletId, msg.headers.host, msg.headers, typeLocation, msg.json['@data'], function(){

         var obj = {
            '@id'             : objectId,
            '@location'       : objectRestURL,
            '@cloudlet'       : cloudletId,
            '@openi_type'     : msg.json['@openi_type'],
            '@type_location'  : typeLocation,
            '@data'           : msg.json['@data'],
            _date_created     : new Date().toJSON(),
            _date_modified    : new Date().toJSON()
         };

         senderToDao.send( {
            'dao_actions'      : [
               {
                  'action'       : 'POST',
                  'database'    : dbName,
                  'object_name' : objectId,
                  'object_data' : obj,
                  'id'          : objectId,
                  'cloudlet_id' : cloudletId,
                  'third_party' : third_party,
                  'bucket'      : 'objects'
               }
            ],
            'mongrel_sink' : terminal_handler,
            'clients'      : [
               {
                  'uuid' : msg.uuid,
                  'connId' : msg.connId
               }
            ]
         });
      },
      function(errorMessage){
         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, errorMessage);
      });
};

var genPutMessage = function(msg, senderToDao, senderToClient, terminal_handler){

   var objectId    = openiUtils.extractObjectId(msg.path);
   var cloudletId  = openiUtils.extractCloudletId(msg.path);
   var third_party = msg.token.context;
   var property   = decodeURIComponent(msg.path.slice(msg.path.indexOf(objectId) + objectId.length + 1));

   if (null === cloudletId){
      cloudletId = msg.cloudletId
   }

   var dbName = cloudletId + '+' + objectId;

//   if (null !==  property && property.length > 0){
//      putWithProperty(dbName, msg, objectId, property, terminal_handler)
//      return
//   }

   var type = msg.json['@openi_type'];
   var typeLocation      = '/api/v1/types/' + type;

   if (undefined === type) {
      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error" : "Invalid format"});
      return;
   }

   var objectRestURL = '/api/v1/objects/' + cloudletId + '/' +  objectId;

   validator.validateObject(cloudletId, msg.headers.host, msg.headers, typeLocation, msg.json['@data'], function(){

         var obj = {
            '@id'            : objectId,
            '@location'      : objectRestURL,
            '@cloudlet'      : cloudletId,
            '@openi_type'    : msg.json['@openi_type'],
            '@type_location' : "https://" + msg.headers.host + "/api/v1/types/" + type,
            '@data'          : msg.json['@data'],
            _date_created    : "",
            'third_party'    : third_party,
            _date_modified   : new Date().toJSON()
         };

         senderToDao.send({
            'dao_actions'      : [
               {
                  'action'       : 'PUT',
                  'database'     : dbName,
                  'revision'     : msg.json['_revision'],
                  'object_data'  : obj,
                  'id'           : objectId,
                  'bucket'      : 'objects'
               }
            ],
            'mongrel_sink' : terminal_handler,
            'clients'      : [
               {
                  'uuid' : msg.uuid,
                  'connId' : msg.connId
               }
            ]
         });
      },
      function(errorMessage){
         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, errorMessage);
      });
};

var genGetMessage = function(msg, senderToDao, senderToClient, terminal_handler){

   var url_parts   = url.parse(msg.headers.URI, true);
   var query       = url_parts.query;
   var cloudletId  = openiUtils.extractCloudletId(msg.path);
   var objectId    = openiUtils.extractObjectId(msg.path);
   var third_party = msg.token.context;
   var userQuery;
   var objectQuery;
   var typeQuery;
   var company;
   var ref;
   var typeID;

   if (null === objectId){
      objectId = openiUtils.extractAttachmentId(msg.path);
   }

   if (null === cloudletId){
      cloudletId = msg.cloudletId
   }

   if (!openiUtils.isCloudletId(cloudletId)){
      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error":"Invalid Cloudlet id" });
   }
   else if (cloudletId !== msg.cloudletId){
      senderToClient.send(msg.uuid, msg.connId, zmq.status.UNAUTHORIZED_401, zmq.standard_headers.json, {"error":"Unauthorised access" });
      return
   }

   if(null !== objectId) {
      if ( !openiUtils.isAttachmentId(objectId) && !openiUtils.isObjectId(objectId) ){
         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error":"Invalid Object id" });
      }

      var property  = decodeURIComponent(msg.path.slice(msg.path.indexOf(objectId) + objectId.length + 1));

      var dbName = cloudletId + '+' + objectId;

      if (query.resolve === true) {
         rrd.monitorIncrement('object_get_resolve')
      }

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'      : 'GET',
               'database'    : dbName,
               'resolve'     : ('true' === query.resolve),
               'resp_type'   : 'object',
               'property'    : property,
               'meta'        : query.meta,
               'third_party' : third_party,
               'bucket'      : 'objects'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid'   : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });

      async.series([
         function(callback){
            userQuery = ViewQuery.from('user_views', 'get_name').key(msg.token.context);

            userBucket.query(userQuery, function(err, results) {

               if (err) {
                  callback(err);
               }

               if (_.isEmpty(results)) {
                  callback('no results');
               } else {
                  company = results[0].value;
                  callback(null, company);
               }
            });

         },
         function(callback){
            objectQuery = ViewQuery.from('objects_views', 'object_data').key(objectId);

            objectBucket.query(objectQuery, function(err, results) {

               if (err) {
                  callback(err);
               }

               if (_.isEmpty(results)) {
                  callback('no results');
               } else {
                  typeID = results[0].value;
                  callback(null, typeID);
               }
            });

         },
         function(callback){
            typeQuery = ViewQuery.from('type_views', 'get_ref').key(typeID);

            typeBucket.query(typeQuery, function(err, results) {

               if (err) {
                  callback(err);
               }

               if (_.isEmpty(results)) {
                  callback('no results');
               } else {
                  ref  = results[0].value;
                  callback(null, ref);
               }
            });

         }],
      function(err, results){
         if (err) {
            console.log(err);
         } else {
            console.log(results);
            console.log(objectId + ref);
            tracklet.track({
               cloudlet: cloudletId,
               app: msg.token.client_id,
               company: company,
               object: objectId,
               objData: ref
            });
         }
      });

   }
   else {
      var limit       = (undefined !== query.limit)  ? Number(query.limit)   : 30;
      var offset      = (undefined !== query.offset) ? Number(query.offset)  :  0;
      var prev        = msg.headers.URI.replace("offset="+offset, "offset="+ (((offset - limit) < 0) ? 0 : (offset - limit)));
      var next        = msg.headers.URI.replace("offset="+offset, "offset="+ (offset + limit));

      var meta = {
         "limit"       : limit,
         "offset"      : offset,
         "total_count" : 0,
         "prev"        : (0 === offset)? null : prev,
         "next"        : next
      };

      //TODO: Dodgy logic?
      if(undefined !== query.property_filter || undefined !== query.with_property){
         filter.filter(msg, meta, senderToClient, third_party, cloudletId);
         return;
      }

      var startKey      = [third_party, cloudletId];
      var endKey        = [third_party, cloudletId + '\uefff'];
      var viewName      = 'object_by_cloudlet_id';

      if (undefined !== query.type) {

         if (!openiUtils.isTypeId(query.type)){
            senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {"error": "invalid type id format"});
            return;
         }

         viewName = 'object_by_type';
         startKey = [third_party, query.type, cloudletId];
         endKey   = [third_party, query.type, cloudletId + '\uefff' ];
      }

      if (undefined !== query.only_show_properties) {
         query.only_show_properties = query.only_show_properties.split(',');
      }

      if (query.id_only === true) {
         rrd.monitorIncrement('object_get_id_only')
      }

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'      : 'VIEW',
               'start_key'   : startKey ,
               'end_key'     : endKey ,
               'design_doc'  : 'objects_views',
               'view_name'   : viewName,
               'meta'        : meta,
               'filter_show' : query.only_show_properties,
               'resp_type'   : 'object',
               'cloudlet'    : cloudletId,
               'third_party' : third_party,
               'id_only'     : ('true' === query.id_only),
               'bucket'      : 'objects'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
      userQuery = ViewQuery.from('user_views', 'get_name').key(msg.token.context);
      userBucket.query(userQuery, function(err, results) {

         if (null === results){
            return
         }

         company = results[0].value;

         tracklet.track({
            cloudlet: cloudletId,
            app: msg.token.client_id,
            company: company,
            object: 'all',
            objData: 'all'
         }, function(err) {
            if (err) {
               console.log(err);
            }
         });

      });
   }
};

var genDeleteMessage = function(msg, senderToDao, senderToClient, terminal_handler){

   var cloudletId  = openiUtils.extractCloudletId(msg.path);
   var objectId    = openiUtils.extractObjectId(msg.path);
   var third_party = msg.token.context;

   if (null === cloudletId){
      cloudletId = msg.cloudletId
   }

   var dbName = cloudletId + '+' + objectId;

   senderToDao.send({
      'dao_actions'      : [
         {
            'action'      : 'DELETE',
            'database'    : dbName,
            'bucket'      : 'objects',
            'third_party' : third_party
         }
      ],
      'mongrel_sink' : terminal_handler,
      'clients'      : [
         {
            'uuid' : msg.uuid,
            'connId' : msg.connId
         }
      ]
   });
};

var processMongrel2Message = function (msg, senderToDao, senderToClient, terminal_handler) {

   this.logger.logMongrel2Message(msg);

   //Deletes null variables
   for(var key in msg.json) {
      if(msg.json[key] === null) {
         delete msg.json[key];
      }
   }

   switch(msg.headers['METHOD']) {
   case 'POST':
      genPostMessage(msg, senderToDao, senderToClient, terminal_handler);
      break;
   case 'GET':
      genGetMessage(msg, senderToDao, senderToClient, terminal_handler);
      break;
   case 'PUT':
      genPutMessage(msg, senderToDao, senderToClient, terminal_handler);
      break;
   case 'DELETE':
      genDeleteMessage(msg, senderToDao, senderToClient, terminal_handler);
      break;
   default:
      break;
   }
};

module.exports.init                   = init;
module.exports.processMongrel2Message = processMongrel2Message;
<|endoftext|>


<commit_before>

module.exports = function(dolphin){
  var opts = dolphin.opts;

  /**
   *
   * filters – a JSON encoded value of the filters (a map[string][]string) to process on the images list. Available filters:
      dangling=true
      label=key or label="key=value" of an image label
      before=(<image-name>[:<tag>], <image id> or <image@digest>)
      since=(<image-name>[:<tag>], <image id> or <image@digest>)
   **/
  var images = function(imageName){
    //return dolphin._list('images', filters, opts);
    return dolphin._get('images/' + imageName + '/json', null, opts);
  }

/**
 *
 *  We use docker command line since building images is quite a complex
 *  operation.
 *  */
  images.build = function (url, name, versions, opts) {
    var args = [].concat.apply([], versions.map(function(tag){
      return ['-t', name + ':' + tag];
    }));

    args.unshift('build');
    args.push(url);

    // TODO: Add options.
    return dolphin.docker(args).then(function(imageId){
      return {
        Id: imageId
      }
    })
  }

  return images;
}
<commit_msg>added support for pushing images<commit_after>

module.exports = function(dolphin){
  var opts = dolphin.opts;

  /**
   *
   * filters – a JSON encoded value of the filters (a map[string][]string) to process on the images list. Available filters:
      dangling=true
      label=key or label="key=value" of an image label
      before=(<image-name>[:<tag>], <image id> or <image@digest>)
      since=(<image-name>[:<tag>], <image id> or <image@digest>)
   **/
  var images = function(imageName){
    //return dolphin._list('images', filters, opts);
    return dolphin._get('images/' + imageName + '/json', null, opts);
  }

/**
 *
 *  We use docker command line since building images is quite a complex
 *  operation.
 *  */
  images.build = function (url, name, versions, opts) {
    var args = [].concat.apply([], versions.map(function(tag){
      return ['-t', name + ':' + tag];
    }));

    args.unshift('build');
    args.push(url);

    // TODO: Add options.
    return dolphin.docker(args).then(function(imageId){
      return {
        Id: imageId
      }
    })
  }

  images.push = function(image, tag){
    var url = 'images/'+name+'/push';
    if(tag){
      url += '?tag=' + tag;
    }
    return dolphin._post(url, null, opts);
  }

  return images;
}
<|endoftext|>


<commit_before>/* global Log, Config */
'use strict';

const EventEmitter = require('events').EventEmitter;
const StringTemplate = require('./string-template');
const Metadata = require('./source/metadata');
const S3 = require('./source/s3');

// TODO: Implement both of these source types
// const Consul = require('./source/consul');
// const File = require('./source/file');

const DEFAULT_INDEX_INTERVAL = Config.get('index:interval') || 60000; // eslint-disable-line rapid7/static-magic-numbers
const DEFAULT_INDEX_BUCKET = Config.get('index:bucket');
const DEFAULT_INDEX_PATH = Config.get('index:path');

/**
 * Recursively iterate through an object applying the callback to each k/v pair
 * @param {Object} obj
 * @param {Function} callback
 * @returns {Object}
 */
function iter(obj, callback) {
  const collected = {};
  let value;

  for (const prop in obj) {
    if (obj.hasOwnProperty(prop)) {
      if (typeof obj[prop] === 'object') {
        value = iter(obj[prop], callback);
      } else {
        value = callback(prop, obj[prop]);
      }
      collected[prop] = value;
    }
  }
  return collected;
}

class S3IndexParser {
  constructor() {
    this.properties = {};
  }

  update(data) {
    let properties = {};

    try {
      properties = JSON.parse(data.toString());
    } catch (e) {
      // We should have an error condition but tbd
    }
    this.properties = properties.sources;
  }
}

class PluginManager extends EventEmitter {
  /**
   * Creates a new instance of the PluginManager
   *
   * The following options are valid:
   * - interval (Number, default 60000) Interval for the index source timer
   * - bucket (String) The bucket where index data (and other configuration data) is stored
   * - path (String) The path to the index document in the bucket
   *
   * If a configuration document is provided when running the server, the options Object can be omitted
   * as the data will be filled from the global Config.
   *
   * @param {Storage} storage
   * @param {Object} options
   */
  constructor(storage, options) {
    super();

    const configuration = options || Object.create(null);

    this.storage = storage;

    this.index = new S3({
      interval: configuration.interval || DEFAULT_INDEX_INTERVAL,
      bucket: configuration.bucket || DEFAULT_INDEX_BUCKET,
      path: configuration.path || DEFAULT_INDEX_PATH,
      parser: new S3IndexParser()
    });
    this.index.on('error', (error) => {
      this._error(error);
    });
    this.index.on('update', () => {
      this._loadSourcesFromIndex();
    });

    this.metadata = new Metadata();
    this.metadata.service.host = configuration.metadataHost || Config.get('metadata:host');
    this.metadata.on('error', (error) => {
      this._error(error);
    });
    this.metadata.on('update', () => {
      this._loadSourcesFromIndex();
    });

    this._running = false;
    this._ok = true;
  }

  /**
   * Initialize the PluginManager's index and metadata source
   */
  initialize() {
    Log.info('Initializing index and metadata');
    this._running = true;
    this.index.initialize();
    this.metadata.initialize();
  }

  /**
   * Shutdown the plugin manager's sources and then the plugin manager
   * @param {Boolean} del
   */
  shutdown(del) {
    Log.info('Shutting down sources');
    this.storage.sources.forEach((source) => {
      source.shutdown();
    });

    if (del === true) {
      this.storage.sources = [];
    }

    this._running = false;
  }

  /**
   * Get the plugin manager's status
   * @returns {{running: boolean, ok: boolean, sources: Array}}
   */
  status() {
    return {
      running: this._running,
      ok: this._ok,
      sources: this._sourcesStatus()
    };
  }

  /**
   * Gets an array of source statuses
   * @returns {Array}
   * @private
   */
  _sourcesStatus() {
    return this.storage.sources.map((source) => {
      return {
        name: source.name,
        type: source.type,
        status: source.status()
      };
    });
  }

  /**
   */
  _loadSourcesFromIndex() {
    let sources = [];

    try {
      sources = this.index.properties.map((el) => {
        return iter(el, (k, v) => {
          // TODO: No handling for arrays. Are we expecting arrays anywhere in the index?
          return StringTemplate.coerce(v, this.metadata.properties);
        });
      });
    } catch (error) {
      this._error(error);
      return;
    }

    this._ok = true;
    this.emit('sources-generated', sources);
    this._registerSources(sources);
    this.emit('sources-registered', this.storage.sources);
  }

  /**
   * Register the sources with the storage layer and their event handlers
   * @param {Array} sources
   * @private
   */
  _registerSources(sources) {
    sources.forEach((source) => {
      let instance;

      switch (source.type.toLowerCase()) {
        case 's3':

          // TODO: Support for sources in other buckets
          instance = new S3(Object.assign(source.parameters, {bucket: this.index.bucket}));
          break;

        // case 'file':
        //  //
        //  break;
        // case 'consul':
        //  //
        //  break;
        default:
          this._error(new Error(`Source type ${source.type} not implemented`));
      }

      if (instance) {
        this.emit('source-instantiated', instance);

        this.storage.register(instance);
        this._registerSourceEvents(instance);
        this.emit('source-registered', instance);

        instance.initialize();
        this.emit('source-initialized', instance);
      }
    });
  }

  /**
   * Bind event handlers for each source.
   * @param {Metadata|S3} source
   * @private
   */
  _registerSourceEvents(source) {
    source.on('startup', () => {
      Log.info(`${source.name} started up.`);
    });

    source.on('shutdown', () => {
      Log.info(`${source.name} shut down.`);
    });

    source.on('update', () => {
      Log.info(`${source.name}'s data was updated from its underlying source data.`);
      this.storage.update();
    });

    source.on('no-update', () => {
      Log.info(`${source.name} has no update to its underlying source data.`);
    });

    source.on('error', (err) => {
      Log.error(`${source.name} encountered the following error: ${err}`);
      this._error(err);
    });
  }

  /**
   * General handler for all errors
   * @param {Error} err
   * @returns {PluginManager}
   * @private
   */
  _error(err) {
    Log.error(err, {
      source: this.name,
      type: this.type
    });

    this._ok = false;

    // Only emit an error event if there are listeners.
    if (this.listeners('error').length > 0) {
      this.emit('error', err);
    }

    return this;
  }
}

module.exports = PluginManager;
<commit_msg>Documents new PluginManager function for handling updates.<commit_after>/* global Log, Config */
'use strict';

const EventEmitter = require('events').EventEmitter;
const StringTemplate = require('./string-template');
const Metadata = require('./source/metadata');
const S3 = require('./source/s3');

// TODO: Implement both of these source types
// const Consul = require('./source/consul');
// const File = require('./source/file');

const DEFAULT_INDEX_INTERVAL = Config.get('index:interval') || 60000; // eslint-disable-line rapid7/static-magic-numbers
const DEFAULT_INDEX_BUCKET = Config.get('index:bucket');
const DEFAULT_INDEX_PATH = Config.get('index:path');

/**
 * Recursively iterate through an object applying the callback to each k/v pair
 * @param {Object} obj
 * @param {Function} callback
 * @returns {Object}
 */
function iter(obj, callback) {
  const collected = {};
  let value;

  for (const prop in obj) {
    if (obj.hasOwnProperty(prop)) {
      if (typeof obj[prop] === 'object') {
        value = iter(obj[prop], callback);
      } else {
        value = callback(prop, obj[prop]);
      }
      collected[prop] = value;
    }
  }
  return collected;
}

class S3IndexParser {
  constructor() {
    this.properties = {};
  }

  update(data) {
    let properties = {};

    try {
      properties = JSON.parse(data.toString());
    } catch (e) {
      // We should have an error condition but tbd
    }
    this.properties = properties.sources;
  }
}

class PluginManager extends EventEmitter {
  /**
   * Creates a new instance of the PluginManager
   *
   * The following options are valid:
   * - interval (Number, default 60000) Interval for the index source timer
   * - bucket (String) The bucket where index data (and other configuration data) is stored
   * - path (String) The path to the index document in the bucket
   *
   * If a configuration document is provided when running the server, the options Object can be omitted
   * as the data will be filled from the global Config.
   *
   * @param {Storage} storage
   * @param {Object} options
   */
  constructor(storage, options) {
    super();

    const configuration = options || Object.create(null);

    this.storage = storage;

    this.index = new S3({
      interval: configuration.interval || DEFAULT_INDEX_INTERVAL,
      bucket: configuration.bucket || DEFAULT_INDEX_BUCKET,
      path: configuration.path || DEFAULT_INDEX_PATH,
      parser: new S3IndexParser()
    });
    this.index.on('error', (error) => {
      this._error(error);
    });
    this.index.on('update', () => {
      this._loadSourcesFromIndex();
    });

    this.metadata = new Metadata();
    this.metadata.service.host = configuration.metadataHost || Config.get('metadata:host');
    this.metadata.on('error', (error) => {
      this._error(error);
    });
    this.metadata.on('update', () => {
      this._loadSourcesFromIndex();
    });

    this._running = false;
    this._ok = true;
  }

  /**
   * Initialize the PluginManager's index and metadata source
   */
  initialize() {
    Log.info('Initializing index and metadata');
    this._running = true;
    this.index.initialize();
    this.metadata.initialize();
  }

  /**
   * Shutdown the plugin manager's sources and then the plugin manager
   * @param {Boolean} del
   */
  shutdown(del) {
    Log.info('Shutting down sources');
    this.storage.sources.forEach((source) => {
      source.shutdown();
    });

    if (del === true) {
      this.storage.sources = [];
    }

    this._running = false;
  }

  /**
   * Get the plugin manager's status
   * @returns {{running: boolean, ok: boolean, sources: Array}}
   */
  status() {
    return {
      running: this._running,
      ok: this._ok,
      sources: this._sourcesStatus()
    };
  }

  /**
   * Gets an array of source statuses
   * @returns {Array}
   * @private
   */
  _sourcesStatus() {
    return this.storage.sources.map((source) => {
      return {
        name: source.name,
        type: source.type,
        status: source.status()
      };
    });
  }

  /**
   * Called when either the S3 index or EC2 metadata are updated. If parsing of
   * of composite properties fails, an error is logged. Once parsing succeeds,
   * any additional sources in the index are registered.
   * @private
   */
  _loadSourcesFromIndex() {
    let sources = [];

    try {
      sources = this.index.properties.map((el) => {
        return iter(el, (k, v) => {
          // TODO: No handling for arrays. Are we expecting arrays anywhere in the index?
          return StringTemplate.coerce(v, this.metadata.properties);
        });
      });
    } catch (error) {
      this._error(error);
      return;
    }

    this._ok = true;
    this.emit('sources-generated', sources);
    this._registerSources(sources);
    this.emit('sources-registered', this.storage.sources);
  }

  /**
   * Register the sources with the storage layer and their event handlers
   * @param {Array} sources
   * @private
   */
  _registerSources(sources) {
    sources.forEach((source) => {
      let instance;

      switch (source.type.toLowerCase()) {
        case 's3':

          // TODO: Support for sources in other buckets
          instance = new S3(Object.assign(source.parameters, {bucket: this.index.bucket}));
          break;

        // case 'file':
        //  //
        //  break;
        // case 'consul':
        //  //
        //  break;
        default:
          this._error(new Error(`Source type ${source.type} not implemented`));
      }

      if (instance) {
        this.emit('source-instantiated', instance);

        this.storage.register(instance);
        this._registerSourceEvents(instance);
        this.emit('source-registered', instance);

        instance.initialize();
        this.emit('source-initialized', instance);
      }
    });
  }

  /**
   * Bind event handlers for each source.
   * @param {Metadata|S3} source
   * @private
   */
  _registerSourceEvents(source) {
    source.on('startup', () => {
      Log.info(`${source.name} started up.`);
    });

    source.on('shutdown', () => {
      Log.info(`${source.name} shut down.`);
    });

    source.on('update', () => {
      Log.info(`${source.name}'s data was updated from its underlying source data.`);
      this.storage.update();
    });

    source.on('no-update', () => {
      Log.info(`${source.name} has no update to its underlying source data.`);
    });

    source.on('error', (err) => {
      Log.error(`${source.name} encountered the following error: ${err}`);
      this._error(err);
    });
  }

  /**
   * General handler for all errors
   * @param {Error} err
   * @returns {PluginManager}
   * @private
   */
  _error(err) {
    Log.error(err, {
      source: this.name,
      type: this.type
    });

    this._ok = false;

    // Only emit an error event if there are listeners.
    if (this.listeners('error').length > 0) {
      this.emit('error', err);
    }

    return this;
  }
}

module.exports = PluginManager;
<|endoftext|>


<commit_before>
(function () {

    var Proteus  = require("proteus"),
        FS       = require("fs"),
        uglylib  = require("uglify-js"),
        Infuser  = require("./infuser"),
        Iterator = require("./iterator"),
        astUtil  = require("./util-ast"),
        jsp      = uglylib.parser,
        jsb      = uglylib.uglify
    ;

    //-----------------------------------------------------------------------
    // Exports
    //-----------------------------------------------------------------------
    function infuse (src, opts) {
        var nominify = opts.nominify || false,
            beautify = { beautify: nominify },
            embed    = opts.embed || false,
            reserved = opts.reserved,
            infuser,
            infusions,
            count,
            ast,
            iter,
            modNode
        ;

        console.log(src);
        infuser = new Infuser({
            definitions: opts.definitions,
            reserved:    reserved,
            libPaths:    opts.libPaths,
            embed:       embed
        });

        ast = infuser.infuse(src);
        infusions = infuser.infusions;

        if (opts.dumpPaths) {
            return infusions.getPaths().join("\n") + "\n";
        }

        count = infusions.count;

        if (count) {
            ast[1] = infusions.toAst().concat(ast[1]);
        }

        if (!nominify) {
            ast = jsb.ast_lift_variables(ast);
            ast = jsb.ast_mangle(ast, {
                toplevel: true,
                except: reserved
            });
            ast = jsb.ast_squeeze(ast, {
                make_seqs: true,
                dead_code: true
            });
        }

        if (embed && count) {
            // if we are embedding the modules as strings we need to search
            // for our infused modules object
            iter = new Iterator(ast);

            modNode = iter.findAll(function (node) {
                return node[0] === "dot" &&
                    node[1] && node[1][0] && node[1][0] === "name" &&
                    node[1][1] === "$i" &&
                    node[2] && node[2] === "modules";
            })[3]; // probably shouldn't assume it's always the 4th one...

            iter.start();
            modNode = iter.skipToNode(modNode);
            modNode = iter.getParent(modNode);

            // replace the code with a string
            modNode[3][1].forEach(function (prop) {
                prop[1] = [
                    "string",
                    jsb.gen_code(prop[1], beautify)
                ];
            });
        }

        return opts.dumpAst ?
                require("util").inspect(ast, false, 1000000) :
                jsb.gen_code(ast, beautify);
    }

    Proteus.merge(infuse, {
        Iterator:    require("./iterator"),
        Infuser:     Infuser,
        Infusions:   require("./infusions.js"),
        astUtil:     require("./util-ast"),
        requireUtil: require("./util-require")
    });

    module.exports = infuse;
}());
<commit_msg>Remove console.log statement.<commit_after>
(function () {

    var Proteus  = require("proteus"),
        FS       = require("fs"),
        uglylib  = require("uglify-js"),
        Infuser  = require("./infuser"),
        Iterator = require("./iterator"),
        astUtil  = require("./util-ast"),
        jsp      = uglylib.parser,
        jsb      = uglylib.uglify
    ;

    //-----------------------------------------------------------------------
    // Exports
    //-----------------------------------------------------------------------
    function infuse (src, opts) {
        var nominify = opts.nominify || false,
            beautify = { beautify: nominify },
            embed    = opts.embed || false,
            reserved = opts.reserved,
            infuser,
            infusions,
            count,
            ast,
            iter,
            modNode
        ;

        infuser = new Infuser({
            definitions: opts.definitions,
            reserved:    reserved,
            libPaths:    opts.libPaths,
            embed:       embed
        });

        ast = infuser.infuse(src);
        infusions = infuser.infusions;

        if (opts.dumpPaths) {
            return infusions.getPaths().join("\n") + "\n";
        }

        count = infusions.count;

        if (count) {
            ast[1] = infusions.toAst().concat(ast[1]);
        }

        if (!nominify) {
            ast = jsb.ast_lift_variables(ast);
            ast = jsb.ast_mangle(ast, {
                toplevel: true,
                except: reserved
            });
            ast = jsb.ast_squeeze(ast, {
                make_seqs: true,
                dead_code: true
            });
        }

        if (embed && count) {
            // if we are embedding the modules as strings we need to search
            // for our infused modules object
            iter = new Iterator(ast);

            modNode = iter.findAll(function (node) {
                return node[0] === "dot" &&
                    node[1] && node[1][0] && node[1][0] === "name" &&
                    node[1][1] === "$i" &&
                    node[2] && node[2] === "modules";
            })[3]; // probably shouldn't assume it's always the 4th one...

            iter.start();
            modNode = iter.skipToNode(modNode);
            modNode = iter.getParent(modNode);

            // replace the code with a string
            modNode[3][1].forEach(function (prop) {
                prop[1] = [
                    "string",
                    jsb.gen_code(prop[1], beautify)
                ];
            });
        }

        return opts.dumpAst ?
                require("util").inspect(ast, false, 1000000) :
                jsb.gen_code(ast, beautify);
    }

    Proteus.merge(infuse, {
        Iterator:    require("./iterator"),
        Infuser:     Infuser,
        Infusions:   require("./infusions.js"),
        astUtil:     require("./util-ast"),
        requireUtil: require("./util-require")
    });

    module.exports = infuse;
}());
<|endoftext|>


<commit_before>var vec3 = require('vec3');
var Vec3 = vec3.Vec3;
var assert = require('assert');
var Painting = require('../painting');
var Location = require('../location');
var ChatMessage = require('../chat_message');

module.exports = inject;

var paintingFaceToVec = [
  new Vec3(0, 0, -1),
  new Vec3(-1, 0, 0),
  new Vec3(0, 0, 1),
  new Vec3(1, 0, 0)
];

function inject(bot,{version}) {
  var Chunk = require('prismarine-chunk')(version);
  var columns = {};
  var signs = {};
  var paintingsByPos = {};
  var paintingsById = {};

  function addPainting(painting) {
    paintingsById[painting.id] = painting;
    paintingsByPos[painting.position] = painting;
  }

  function deletePainting(painting) {
    delete paintingsById[painting.id];
    delete paintingsByPos[painting.position];
  }

  function addColumn(args) {
    var columnCorner = new Vec3(args.x * 16, 0, args.z * 16);
    var key = columnKeyXZ(columnCorner.x, columnCorner.z);
    if(!args.bitMap) {
      // stop storing the chunk column
      delete columns[key];
      bot.emit("chunkColumnUnload", columnCorner);
      return;
    }
    var column = columns[key];
    if(!column) columns[key] = column = new Chunk();

    column.load(args.data,args.bitMap);

    bot.emit("chunkColumnLoad", columnCorner);
  }

  function findBlock(options) {
    var check;
    if(typeof(options.matching) !== 'function') {
      if(!Array.isArray(options.matching)) {
        options.matching = [options.matching];
      }
      check=isMatchingType;
    }
    else check=options.matching;
    options.point = options.point || bot.entity.position;
    options.maxDistance = options.maxDistance || 16;
    var cursor = vec3();
    var point = options.point;
    var max = options.maxDistance;
    var found;
    for(cursor.x = point.x - max; cursor.x < point.x + max; cursor.x++) {
      for(cursor.y = point.y - max; cursor.y < point.y + max; cursor.y++) {
        for(cursor.z = point.z - max; cursor.z < point.z + max; cursor.z++) {
          found = bot.blockAt(cursor);
          if (check(found)) return found;
        }
      }
    }

    function isMatchingType(block) {
      return options.matching.indexOf(block.type) >= 0;
    }
  }

  function posInChunk(pos)
  {
    return pos.floored().modulus(new Vec3(16,256,16));
  }

  function blockAt(absolutePoint) {
    var loc = new Location(absolutePoint);
    var key = columnKeyXZ(loc.chunkCorner.x, loc.chunkCorner.z);

    var column = columns[key];
    // null column means chunk not loaded
    if(!column) return null;

    block=column.getBlock(posInChunk(absolutePoint));
    block.position = loc.floored;
    block.signText = signs[loc.floored];
    block.painting = paintingsByPos[loc.floored];

    return block;
  }

  function blockIsNotEmpty(pos) {
    var block = bot.blockAt(pos);
    return block!==null && block.boundingBox!=="empty";
  }

  // maybe this should be moved to math.js instead?
  function visiblePosition(a,b) {
    var v=b.minus(a);
    var t=Math.sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
    v=v.scaled(1/t);
    v=v.scaled(1/5);
    var u=t*5;
    var na;
    for(var i=1;i<u;i++) {
      na=a.plus(v);
      // check that blocks don't inhabit the same position
      if(!na.floored().equals(a.floored())) {
        // check block is not transparent
        if(blockIsNotEmpty(na)) return false;
      }
      a=na;
    }
    return true;
  }

  // if passed in block is within line of sight to the bot, returns true
  // also works on anything with a position value
  function canSeeBlock(block) {
    // this emits a ray from the center of the bots body to the block
    if(visiblePosition(bot.entity.position.offset(0,bot.entity.height*0.5,0),block.position) ) {
      return true;
    }
    return false;
  }

  function chunkColumn(x, z) {
    var key = columnKeyXZ(x, z);
    return columns[key];
  }

  function emitBlockUpdate(oldBlock, newBlock) {
    bot.emit("blockUpdate", oldBlock, newBlock);
    var position = oldBlock ? oldBlock.position :
      (newBlock ? newBlock.position : null);
    if(position) bot.emit("blockUpdate:" + newBlock.position, oldBlock, newBlock);
  }

  function updateBlock(point, type, metadata) {
    var oldBlock = blockAt(point);
    var loc = new Location(point);
    var key = columnKeyXZ(loc.chunkCorner.x, loc.chunkCorner.z);
    var column = columns[key];
    // sometimes minecraft server sends us block updates before it sends
    // us the column that the block is in. ignore this.
    if(!column) return;
    column.setBlockType(posInChunk(point),type);
    column.setBlockData(posInChunk(point),metadata);

    delete signs[loc.floored];

    var painting = paintingsByPos[loc.floored];
    if(painting) deletePainting(painting);

    emitBlockUpdate(oldBlock, blockAt(point));
  }

  bot._client.on('map_chunk', function(packet) {
    addColumn({
      x: packet.x,
      z: packet.z,
      bitMap: packet.bitMap,
      skyLightSent: bot.game.dimension!=="nether",
      groundUp: packet.groundUp,
      data: packet.chunkData,
    });
  });

  bot._client.on('map_chunk_bulk', function(packet) {
    var offset = 0;
    var meta, i, size;
    for(i = 0; i < packet.meta.length; ++i) {
      meta = packet.meta[i];
      size = (8192 + (packet.skyLightSent ? 2048 : 0)) *
        onesInShort(meta.bitMap) + // block ids
        2048 * onesInShort(meta.bitMap) + // (two bytes per block id)
        256; // biomes
      addColumn({
        x: meta.x,
        z: meta.z,
        bitMap: meta.bitMap,
        skyLightSent: packet.skyLightSent,
        groundUp: true,
        data: packet.data.slice(offset, offset + size),
      });
      offset += size;
    }

    assert.strictEqual(offset, packet.data.length);
  });

  bot._client.on('multi_block_change', function(packet) {
    // multi block change
    var i, record, metadata, type, blockX, blockZ, y, pt;
    for(i = 0; i < packet.records.length; ++i) {
      record = packet.records[i];

      metadata = (record.blockId & 0x0f);
      type = (record.blockId) >> 4;
      y = record.y;
      blockZ = (record.horizontalPos & 0x0f);
      blockX = (record.horizontalPos >> 4) & 0x0f;

      pt = new Vec3(packet.chunkX * 16 + blockX, y, packet.chunkZ * 16 + blockZ);
      updateBlock(pt, type, metadata);
    }
  });

  bot._client.on('block_change', function(packet) {
    // block change
    var pt = new Vec3(packet.location.x, packet.location.y, packet.location.z);
    updateBlock(pt, packet.type >> 4, packet.type & 0x0f);
  });

  bot._client.on('explosion', function(packet) {
    // explosion
    packet.affectedBlockOffsets.forEach(function(offset) {
      var pt = vec3(offset).offset(packet.x, packet.y, packet.z);
      updateBlock(pt.floor(), 0, 0);
    });
  });

  bot._client.on('spawn_entity_painting', function(packet) {
    var pos = new Vec3(packet.location.x, packet.location.y, packet.location.z);
    var painting = new Painting(packet.entityId,
      pos, packet.title, paintingFaceToVec[packet.direction]);
    addPainting(painting);
  });

  bot._client.on('entity_destroy', function(packet) {
    // destroy entity
    packet.entityIds.forEach(function(id) {
      var painting = paintingsById[id];
      if(painting) deletePainting(painting);
    });
  });

  bot._client.on('update_sign', function(packet) {
    // update sign
    var pos = new Vec3(packet.location.x, packet.location.y, packet.location.z);
    var oldBlock = blockAt(pos);

    // Some servers send blank lines in signs wrong, causing the chat handler to crash.
    if(packet.text1 == 'null' || packet.text1 == '') {
      packet.text1 = '""'
    }
    if(packet.text2 == 'null' || packet.text2 == '') {
      packet.text2 = '""'
    }
    if(packet.text3 == 'null' || packet.text3 == '') {
      packet.text3 = '""'
    }
    if(packet.text4 == 'null' || packet.text4 == '') {
      packet.text4 = '""'
    }

    signs[pos] = new ChatMessage(JSON.parse(packet.text1)) + "\n" + new ChatMessage(JSON.parse(packet.text2)) +
      "\n" + new ChatMessage(JSON.parse(packet.text3)) + "\n" + new ChatMessage(JSON.parse(packet.text4));
    emitBlockUpdate(oldBlock, blockAt(pos));
  });
  bot.updateSign = function(block, text) {
    var lines = text.split("\n");
    if(lines.length > 4) {
      bot.emit("error", new Error("too many lines for sign text"));
      return;
    }
    for(var i = 0; i < lines.length; ++i) {
      if(lines[i].length > 15) {
        bot.emit("error", new Error("signs have max line length 15"));
        return;
      }
    }
    bot._client.write('update_sign', {
      location: block.position,
      text1: JSON.stringify(lines[0]),
      text2: JSON.stringify(lines[1]),
      text3: JSON.stringify(lines[2]),
      text4: JSON.stringify(lines[3])
    });
  };

  // if we get a respawn packet and the dimension is changed,
  // unload all chunks from memory.
  var dimension;
  bot._client.on('login', function(packet) {
    dimension = packet.dimension
  });
  bot._client.on('respawn', function(packet) {
    if(dimension === packet.dimension) return;
    dimension = packet.dimension;
    columns = {};
  });

  bot.findBlock = findBlock;
  bot.canSeeBlock = canSeeBlock;
  bot.blockAt = blockAt;
  bot._chunkColumn = chunkColumn;
  bot._updateBlock = updateBlock;
}

function columnKeyXZ(x, z) {
  return x + ',' + z;
}

function onesInShort(n) {
  n = n & 0xffff;
  var count = 0;
  for(var i = 0; i < 16; ++i) {
    count = ((1 << i) & n) ? count + 1 : count;
  }
  return count;
}

function nibble(wholeByte, low) {
  return low ?
  wholeByte & 0x0f :
  wholeByte >> 4;
}

function Column() {
  this.blockType = new Array(16);
  this.light = new Array(16);
  this.skyLight = new Array(16);
  this.biome = null;
}
<commit_msg>Pass skyLightSent to Chunk.load<commit_after>var vec3 = require('vec3');
var Vec3 = vec3.Vec3;
var assert = require('assert');
var Painting = require('../painting');
var Location = require('../location');
var ChatMessage = require('../chat_message');

module.exports = inject;

var paintingFaceToVec = [
  new Vec3(0, 0, -1),
  new Vec3(-1, 0, 0),
  new Vec3(0, 0, 1),
  new Vec3(1, 0, 0)
];

function inject(bot,{version}) {
  var Chunk = require('prismarine-chunk')(version);
  var columns = {};
  var signs = {};
  var paintingsByPos = {};
  var paintingsById = {};

  function addPainting(painting) {
    paintingsById[painting.id] = painting;
    paintingsByPos[painting.position] = painting;
  }

  function deletePainting(painting) {
    delete paintingsById[painting.id];
    delete paintingsByPos[painting.position];
  }

  function addColumn(args) {
    var columnCorner = new Vec3(args.x * 16, 0, args.z * 16);
    var key = columnKeyXZ(columnCorner.x, columnCorner.z);
    if(!args.bitMap) {
      // stop storing the chunk column
      delete columns[key];
      bot.emit("chunkColumnUnload", columnCorner);
      return;
    }
    var column = columns[key];
    if(!column) columns[key] = column = new Chunk();

    column.load(args.data, args.bitMap, args.skyLightSent);

    bot.emit("chunkColumnLoad", columnCorner);
  }

  function findBlock(options) {
    var check;
    if(typeof(options.matching) !== 'function') {
      if(!Array.isArray(options.matching)) {
        options.matching = [options.matching];
      }
      check=isMatchingType;
    }
    else check=options.matching;
    options.point = options.point || bot.entity.position;
    options.maxDistance = options.maxDistance || 16;
    var cursor = vec3();
    var point = options.point;
    var max = options.maxDistance;
    var found;
    for(cursor.x = point.x - max; cursor.x < point.x + max; cursor.x++) {
      for(cursor.y = point.y - max; cursor.y < point.y + max; cursor.y++) {
        for(cursor.z = point.z - max; cursor.z < point.z + max; cursor.z++) {
          found = bot.blockAt(cursor);
          if (check(found)) return found;
        }
      }
    }

    function isMatchingType(block) {
      return options.matching.indexOf(block.type) >= 0;
    }
  }

  function posInChunk(pos)
  {
    return pos.floored().modulus(new Vec3(16,256,16));
  }

  function blockAt(absolutePoint) {
    var loc = new Location(absolutePoint);
    var key = columnKeyXZ(loc.chunkCorner.x, loc.chunkCorner.z);

    var column = columns[key];
    // null column means chunk not loaded
    if(!column) return null;

    block=column.getBlock(posInChunk(absolutePoint));
    block.position = loc.floored;
    block.signText = signs[loc.floored];
    block.painting = paintingsByPos[loc.floored];

    return block;
  }

  function blockIsNotEmpty(pos) {
    var block = bot.blockAt(pos);
    return block!==null && block.boundingBox!=="empty";
  }

  // maybe this should be moved to math.js instead?
  function visiblePosition(a,b) {
    var v=b.minus(a);
    var t=Math.sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
    v=v.scaled(1/t);
    v=v.scaled(1/5);
    var u=t*5;
    var na;
    for(var i=1;i<u;i++) {
      na=a.plus(v);
      // check that blocks don't inhabit the same position
      if(!na.floored().equals(a.floored())) {
        // check block is not transparent
        if(blockIsNotEmpty(na)) return false;
      }
      a=na;
    }
    return true;
  }

  // if passed in block is within line of sight to the bot, returns true
  // also works on anything with a position value
  function canSeeBlock(block) {
    // this emits a ray from the center of the bots body to the block
    if(visiblePosition(bot.entity.position.offset(0,bot.entity.height*0.5,0),block.position) ) {
      return true;
    }
    return false;
  }

  function chunkColumn(x, z) {
    var key = columnKeyXZ(x, z);
    return columns[key];
  }

  function emitBlockUpdate(oldBlock, newBlock) {
    bot.emit("blockUpdate", oldBlock, newBlock);
    var position = oldBlock ? oldBlock.position :
      (newBlock ? newBlock.position : null);
    if(position) bot.emit("blockUpdate:" + newBlock.position, oldBlock, newBlock);
  }

  function updateBlock(point, type, metadata) {
    var oldBlock = blockAt(point);
    var loc = new Location(point);
    var key = columnKeyXZ(loc.chunkCorner.x, loc.chunkCorner.z);
    var column = columns[key];
    // sometimes minecraft server sends us block updates before it sends
    // us the column that the block is in. ignore this.
    if(!column) return;
    column.setBlockType(posInChunk(point),type);
    column.setBlockData(posInChunk(point),metadata);

    delete signs[loc.floored];

    var painting = paintingsByPos[loc.floored];
    if(painting) deletePainting(painting);

    emitBlockUpdate(oldBlock, blockAt(point));
  }

  bot._client.on('map_chunk', function(packet) {
    addColumn({
      x: packet.x,
      z: packet.z,
      bitMap: packet.bitMap,
      skyLightSent: bot.game.dimension!=="nether",
      groundUp: packet.groundUp,
      data: packet.chunkData,
    });
  });

  bot._client.on('map_chunk_bulk', function(packet) {
    var offset = 0;
    var meta, i, size;
    for(i = 0; i < packet.meta.length; ++i) {
      meta = packet.meta[i];
      size = (8192 + (packet.skyLightSent ? 2048 : 0)) *
        onesInShort(meta.bitMap) + // block ids
        2048 * onesInShort(meta.bitMap) + // (two bytes per block id)
        256; // biomes
      addColumn({
        x: meta.x,
        z: meta.z,
        bitMap: meta.bitMap,
        skyLightSent: packet.skyLightSent,
        groundUp: true,
        data: packet.data.slice(offset, offset + size),
      });
      offset += size;
    }

    assert.strictEqual(offset, packet.data.length);
  });

  bot._client.on('multi_block_change', function(packet) {
    // multi block change
    var i, record, metadata, type, blockX, blockZ, y, pt;
    for(i = 0; i < packet.records.length; ++i) {
      record = packet.records[i];

      metadata = (record.blockId & 0x0f);
      type = (record.blockId) >> 4;
      y = record.y;
      blockZ = (record.horizontalPos & 0x0f);
      blockX = (record.horizontalPos >> 4) & 0x0f;

      pt = new Vec3(packet.chunkX * 16 + blockX, y, packet.chunkZ * 16 + blockZ);
      updateBlock(pt, type, metadata);
    }
  });

  bot._client.on('block_change', function(packet) {
    // block change
    var pt = new Vec3(packet.location.x, packet.location.y, packet.location.z);
    updateBlock(pt, packet.type >> 4, packet.type & 0x0f);
  });

  bot._client.on('explosion', function(packet) {
    // explosion
    packet.affectedBlockOffsets.forEach(function(offset) {
      var pt = vec3(offset).offset(packet.x, packet.y, packet.z);
      updateBlock(pt.floor(), 0, 0);
    });
  });

  bot._client.on('spawn_entity_painting', function(packet) {
    var pos = new Vec3(packet.location.x, packet.location.y, packet.location.z);
    var painting = new Painting(packet.entityId,
      pos, packet.title, paintingFaceToVec[packet.direction]);
    addPainting(painting);
  });

  bot._client.on('entity_destroy', function(packet) {
    // destroy entity
    packet.entityIds.forEach(function(id) {
      var painting = paintingsById[id];
      if(painting) deletePainting(painting);
    });
  });

  bot._client.on('update_sign', function(packet) {
    // update sign
    var pos = new Vec3(packet.location.x, packet.location.y, packet.location.z);
    var oldBlock = blockAt(pos);

    // Some servers send blank lines in signs wrong, causing the chat handler to crash.
    if(packet.text1 == 'null' || packet.text1 == '') {
      packet.text1 = '""'
    }
    if(packet.text2 == 'null' || packet.text2 == '') {
      packet.text2 = '""'
    }
    if(packet.text3 == 'null' || packet.text3 == '') {
      packet.text3 = '""'
    }
    if(packet.text4 == 'null' || packet.text4 == '') {
      packet.text4 = '""'
    }

    signs[pos] = new ChatMessage(JSON.parse(packet.text1)) + "\n" + new ChatMessage(JSON.parse(packet.text2)) +
      "\n" + new ChatMessage(JSON.parse(packet.text3)) + "\n" + new ChatMessage(JSON.parse(packet.text4));
    emitBlockUpdate(oldBlock, blockAt(pos));
  });
  bot.updateSign = function(block, text) {
    var lines = text.split("\n");
    if(lines.length > 4) {
      bot.emit("error", new Error("too many lines for sign text"));
      return;
    }
    for(var i = 0; i < lines.length; ++i) {
      if(lines[i].length > 15) {
        bot.emit("error", new Error("signs have max line length 15"));
        return;
      }
    }
    bot._client.write('update_sign', {
      location: block.position,
      text1: JSON.stringify(lines[0]),
      text2: JSON.stringify(lines[1]),
      text3: JSON.stringify(lines[2]),
      text4: JSON.stringify(lines[3])
    });
  };

  // if we get a respawn packet and the dimension is changed,
  // unload all chunks from memory.
  var dimension;
  bot._client.on('login', function(packet) {
    dimension = packet.dimension
  });
  bot._client.on('respawn', function(packet) {
    if(dimension === packet.dimension) return;
    dimension = packet.dimension;
    columns = {};
  });

  bot.findBlock = findBlock;
  bot.canSeeBlock = canSeeBlock;
  bot.blockAt = blockAt;
  bot._chunkColumn = chunkColumn;
  bot._updateBlock = updateBlock;
}

function columnKeyXZ(x, z) {
  return x + ',' + z;
}

function onesInShort(n) {
  n = n & 0xffff;
  var count = 0;
  for(var i = 0; i < 16; ++i) {
    count = ((1 << i) & n) ? count + 1 : count;
  }
  return count;
}

function nibble(wholeByte, low) {
  return low ?
  wholeByte & 0x0f :
  wholeByte >> 4;
}

function Column() {
  this.blockType = new Array(16);
  this.light = new Array(16);
  this.skyLight = new Array(16);
  this.biome = null;
}
<|endoftext|>