<commit_before>/*
 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
 *
 * A brief overview of this source file: what is its purpose.
 */

var async = require('async');
var common = require('./job-common');
var VERSION = '7.0.5';

/*
 * Sets up a CNAPI VM action request. Take a look at common.zoneAction. Here you
 * can set parameters such as:
 * - request endpoint (usually the VM endpoint)
 * - jobid (so CNAPI can post status updates back to the job info object)
 * - requestMethod
 * - expects (if you want to check a specific running status of the machine)
 */
function setupRequest(job, cb) {
    job.endpoint = '/servers/' +
                   job.params['server_uuid'] + '/vms/' +
                   job.params['vm_uuid'] + '/update';
    job.params.jobid = job.uuid;
    job.requestMethod = 'post';
    job.action = 'add_nics';

    return cb(null, 'Request has been setup!');
}

/*
 * Get server object so we can check if it has the corresponding NIC tags
 */
function getServerNicTags(job, cb) {
    var cnapi = new sdcClients.CNAPI({
        url: cnapiUrl,
        headers: { 'x-request-id': job.params['x-request-id'] }
    });

    // Goes inside the "NIC Names" array and extracts the NIC Tags for each NIC
    function mapNics(object) {
        var nics = [];

        for (var key in object) {
            var subNics = object[key]['NIC Names'];
            nics = nics.concat(subNics);
        }

        return nics;
    }

    return cnapi.getServer(job.params.server_uuid, function (err, server) {
        if (err) {
            return cb(err);
        } else {
            job.serverNicTags = mapNics(server.sysinfo['Network Interfaces']);
            return cb(null, 'Server NIC tags retrieved');
        }
    });
}

/*
 * Checks that the server has the NIC tags for every network that was passed
 */
function checkServerNicTags(job, cb) {
    var napi = new sdcClients.NAPI({
        url: napiUrl,
        headers: { 'x-request-id': job.params['x-request-id'] }
    });
    var networks = job.params.networks;

    async.mapSeries(networks, function (network, next) {
        napi.getNetwork(network.uuid, function (err, net) {
            if (err) {
                next(err);
            } else {
                if (job.serverNicTags.indexOf(net['nic_tag']) === -1) {
                    var msg = 'Server does not have NIC tag: ' + net['nic_tag'];
                    return next(new Error(msg));
                } else {
                    return next();
                }
            }
        });
    }, function (err) {
        if (err) {
            cb(err);
        } else {
            cb(null, 'Server has all the required NIC tags');
        }
    });
}

var workflow = module.exports = {
    name: 'add-nics-' + VERSION,
    version: VERSION,
    chain: [ {
        name: 'common.validate_params',
        timeout: 10,
        retry: 1,
        body: common.validateForZoneAction,
        modules: {}
    }, {
        name: 'common.setup_request',
        timeout: 10,
        retry: 1,
        body: setupRequest,
        modules: {}
    }, {
        name: 'napi.validate_networks',
        timeout: 10,
        retry: 1,
        body: common.validateNetworks,
        modules: { sdcClients: 'sdc-clients', async: 'async' }
    }, {
        name: 'cnapi.get_server',
        timeout: 10,
        retry: 1,
        body: getServerNicTags,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'napi.check_server_nic_tags',
        timeout: 10,
        retry: 1,
        body: checkServerNicTags,
        modules: { async: 'async', sdcClients: 'sdc-clients' }
    }, {
        name: 'napi.provision_nics',
        timeout: 20,
        retry: 1,
        body: common.addNics,
        modules: { async: 'async', sdcClients: 'sdc-clients' }
    }, {
        name: 'common.update_network_params',
        timeout: 10,
        retry: 1,
        body: common.updateNetworkParams,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'cnapi.acquire_vm_ticket',
        timeout: 10,
        retry: 1,
        body: common.acquireVMTicket,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'cnapi.wait_on_vm_ticket',
        timeout: 120,
        retry: 1,
        body: common.waitOnVMTicket,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'cnapi.update_vm',
        timeout: 10,
        retry: 1,
        body: common.zoneAction,
        modules: { restify: 'restify' }
    }, {
        name: 'cnapi.poll_task',
        timeout: 120,
        retry: 1,
        body: common.pollTask,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'vmapi.check_updated',
        timeout: 90,
        retry: 1,
        body: common.checkUpdated,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'fwapi.update',
        timeout: 10,
        retry: 1,
        body: common.updateFwapi,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'cnapi.release_vm_ticket',
        timeout: 60,
        retry: 1,
        body: common.releaseVMTicket,
        modules: { sdcClients: 'sdc-clients' }
    }],
    timeout: 300,
    onerror: [ {
        name: 'napi.cleanup_nics',
        timeout: 10,
        retry: 1,
        body: common.cleanupNics,
        modules: { sdcClients: 'sdc-clients', async: 'async' }
    }, {
        name: 'on_error.release_vm_ticket',
        modules: { sdcClients: 'sdc-clients' },
        body: function (job, cb) {
            var cnapi = new sdcClients.CNAPI({
                url: cnapiUrl,
                headers: { 'x-request-id': job.params['x-request-id'] }
            });
            cnapi.waitlistTicketRelease(job.ticket.uuid, function (err) {
                cb('Error executing job');
                return;
            });
        }
    }],
    oncancel: [ {
        name: 'on_cancel.release_vm_ticket',
        modules: { sdcClients: 'sdc-clients' },
        body: function (job, cb) {
            var cnapi = new sdcClients.CNAPI({
                url: cnapiUrl,
                headers: { 'x-request-id': job.params['x-request-id'] }
            });
            cnapi.waitlistTicketRelease(job.ticket.uuid, function (err) {
                cb();
                return;
            });
        }
    } ]
};
<commit_msg>ZAPI-522: Cannot specify a network pool when adding NICs to a VM<commit_after>/*
 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
 *
 * A brief overview of this source file: what is its purpose.
 */

var common = require('./job-common');
var VERSION = '7.0.6';

/*
 * Sets up a CNAPI VM action request. Take a look at common.zoneAction. Here you
 * can set parameters such as:
 * - request endpoint (usually the VM endpoint)
 * - jobid (so CNAPI can post status updates back to the job info object)
 * - requestMethod
 * - expects (if you want to check a specific running status of the machine)
 */
function setupRequest(job, cb) {
    job.endpoint = '/servers/' +
                   job.params['server_uuid'] + '/vms/' +
                   job.params['vm_uuid'] + '/update';
    job.params.jobid = job.uuid;
    job.requestMethod = 'post';
    job.action = 'add_nics';

    return cb(null, 'Request has been setup!');
}

/*
 * Get server object so we can check if it has the corresponding NIC tags
 */
function checkServerNicTags(job, cb) {
    var cnapi = new sdcClients.CNAPI({
        url: cnapiUrl,
        headers: { 'x-request-id': job.params['x-request-id'] }
    });

    // Goes inside the "NIC Names" array and extracts the NIC Tags for each NIC
    function mapNics(object) {
        var nics = [];

        for (var key in object) {
            var subNics = object[key]['NIC Names'];
            nics = nics.concat(subNics);
        }

        return nics;
    }

    cnapi.getServer(job.params.server_uuid, function (err, server) {
        if (err) {
            return cb(err);
        }

        var serverNicTags = mapNics(server.sysinfo['Network Interfaces']);
        for (var i = 0; i < job.nicTags.length; i++) {
            if (serverNicTags.indexOf(job.nicTags[i]) === -1) {
                var msg = 'Server does not have NIC tag: ' + job.nicTags[i];
                return cb(new Error(msg));
            }
        }

        return cb(null, 'Server has all the required NIC tags');
    });
}

var workflow = module.exports = {
    name: 'add-nics-' + VERSION,
    version: VERSION,
    chain: [ {
        name: 'common.validate_params',
        timeout: 10,
        retry: 1,
        body: common.validateForZoneAction,
        modules: {}
    }, {
        name: 'common.setup_request',
        timeout: 10,
        retry: 1,
        body: setupRequest,
        modules: {}
    }, {
        name: 'napi.validate_networks',
        timeout: 10,
        retry: 1,
        body: common.validateNetworks,
        modules: { sdcClients: 'sdc-clients', async: 'async' }
    }, {
        name: 'cnapi.check_server_nic_tags',
        timeout: 10,
        retry: 1,
        body: checkServerNicTags,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'napi.provision_nics',
        timeout: 20,
        retry: 1,
        body: common.addNics,
        modules: { async: 'async', sdcClients: 'sdc-clients' }
    }, {
        name: 'common.update_network_params',
        timeout: 10,
        retry: 1,
        body: common.updateNetworkParams,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'cnapi.acquire_vm_ticket',
        timeout: 10,
        retry: 1,
        body: common.acquireVMTicket,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'cnapi.wait_on_vm_ticket',
        timeout: 120,
        retry: 1,
        body: common.waitOnVMTicket,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'cnapi.update_vm',
        timeout: 10,
        retry: 1,
        body: common.zoneAction,
        modules: { restify: 'restify' }
    }, {
        name: 'cnapi.poll_task',
        timeout: 120,
        retry: 1,
        body: common.pollTask,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'vmapi.check_updated',
        timeout: 90,
        retry: 1,
        body: common.checkUpdated,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'fwapi.update',
        timeout: 10,
        retry: 1,
        body: common.updateFwapi,
        modules: { sdcClients: 'sdc-clients' }
    }, {
        name: 'cnapi.release_vm_ticket',
        timeout: 60,
        retry: 1,
        body: common.releaseVMTicket,
        modules: { sdcClients: 'sdc-clients' }
    }],
    timeout: 300,
    onerror: [ {
        name: 'napi.cleanup_nics',
        timeout: 10,
        retry: 1,
        body: common.cleanupNics,
        modules: { sdcClients: 'sdc-clients', async: 'async' }
    }, {
        name: 'on_error.release_vm_ticket',
        modules: { sdcClients: 'sdc-clients' },
        body: function (job, cb) {
            var cnapi = new sdcClients.CNAPI({
                url: cnapiUrl,
                headers: { 'x-request-id': job.params['x-request-id'] }
            });
            cnapi.waitlistTicketRelease(job.ticket.uuid, function (err) {
                cb('Error executing job');
                return;
            });
        }
    }],
    oncancel: [ {
        name: 'on_cancel.release_vm_ticket',
        modules: { sdcClients: 'sdc-clients' },
        body: function (job, cb) {
            var cnapi = new sdcClients.CNAPI({
                url: cnapiUrl,
                headers: { 'x-request-id': job.params['x-request-id'] }
            });
            cnapi.waitlistTicketRelease(job.ticket.uuid, function (err) {
                cb();
                return;
            });
        }
    } ]
};
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter,
    http = require('http'),
    https = require('https'),
    util = require('util'),
    underscore = require('underscore'),
    pkg = require('../package.json');
var VERSION = pkg.version;

function Morale(subdomain, key) {
  if (!(this instanceof Morale)) {
    return new Morale(subdomain, key);
  }

  console.assert(typeof subdomain === 'string' && subdomain !== "", 'FAIL: INVALID SUBDOMAIN');
  console.assert(typeof key === 'string' && key !== "", 'FAIL: INVALID KEY');
  console.assert(/^[a-zA-Z0-9\-]+$/.test(subdomain), 'FAIL: INVALID SUBDOMAIN');

  this.options = {
    subdomain: subdomain,
    api_key: key
  };

  return this;
}

module.exports = Morale;

var makeRequest = function(reqOptions, reqData, callback) {
  if (typeof reqData === 'function') {
    callback = reqData;
    reqData = undefined;
  }

  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  console.assert(reqData === undefined || typeof reqData === 'object' || typeof reqData === 'string', 'FAIL: UNEXPECTED REQDATA');

  var requestData;
  var requestLength = 0;
  if (typeof reqData === 'object') {
    requestData = JSON.stringify(reqData);
    requestLength = requestData.length;
  }
  if (typeof reqData === 'string') {
    requestData = reqData;
    requestLength = requestData.length;
  }

  reqOptions.headers = {
      'Accept': '*/*',
      'Connection': 'close',
      'User-Agent': 'morale-node/' + VERSION,
      'Content-length': requestLength
  };

  var resCallback = function(res) {
    var responseData = '';
    res.setEncoding('utf8');

    res.on('data', function(chunk) {
      responseData += chunk;
    });

    res.on('end', function() {
      if (res.statusCode >= 200 && res.statusCode <= 202) {
        callback(null, JSON.parse(responseData));
      } else {
        var jsonData;
        if (typeof res.headers['content-type'] === 'string' && res.headers['content-type'] === 'application/json' && typeof responseData === 'string' && responseData !== "") { jsonData = JSON.parse(responseData); }
        var errMessage = (typeof jsonData === 'object' && jsonData.hasOwnProperty("error")) ? jsonData.error : http.STATUS_CODES[res.statusCode];

        callback({
          statusCode: res.statusCode,
          message: errMessage
        }, undefined);
      }
    });
  };

  var req = https.request(reqOptions, resCallback);

  req.on('error', function(err) {
    callback(err, null);
  });

  if (requestData !== undefined) { req.write(requestData); }

  req.end();
};

var _makeAnonymousRequest = function(reqHost, reqPath, reqMethod, reqData, callback) {
  if (typeof reqData === 'function') {
    callback = reqData;
    reqData = undefined;
  }

  console.assert(typeof reqHost === 'string' && reqHost !== "", 'FAIL: INVALID HOST');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  console.assert(reqPath.charAt(0) === '/', 'FAIL: UNEXPECTED URLPATH');
  console.assert(reqData === undefined || typeof reqData === 'object' || typeof reqData === 'string', 'FAIL: UNEXPECTED REQDATA');

  var requestOptions = {
    host: reqHost.toLowerCase(),
    path: reqPath,
    method: reqMethod
  };

  makeRequest.call(this, requestOptions, reqData, callback);
};

var makeAuthenticatedRequest = function(reqPath, reqMethod, reqData, callback) {
  if (typeof reqData === 'function') {
    callback = reqData;
    reqData = undefined;
  }

  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  console.assert(reqPath.charAt(0) === '/', 'FAIL: UNEXPECTED URLPATH');
  console.assert(reqData === undefined || typeof reqData === 'object', 'FAIL: UNEXPECTED REQDATA');

  var requestOptions = {
    host: util.format("%s.teammorale.com", this.options.subdomain).toLowerCase(),
    path: reqPath,
    method: reqMethod,
    auth: this.options.subdomain + ":" + this.options.api_key
  };

  makeRequest.call(this, requestOptions, reqData, callback);
};

var buildTicketCommand = function(ticket) {
  console.assert(typeof ticket === 'object', 'FAIL: INVALID TICKET');

  var propertyWhiteList = ["title", "description", "due_date", "type", "assigned_to", "priority"];
  var ticketProperties = underscore.intersection(underscore.keys(ticket), propertyWhiteList);
  var addPropertyToCommandArray = function(commands, property) {
    return commands.concat(util.format("%s: %s", property, ticket[property]));
  };

  var commandList = underscore.reduce(ticketProperties, addPropertyToCommandArray, []);
  return commandList.join(" ");
};

module.exports.GetApiToken = function(subdomain, email, password, callback) {
  console.assert(typeof subdomain === 'string' && subdomain !== "", 'FAIL: INVALID SUBDOMAIN');
  console.assert(typeof email === 'string' && email !== "", 'FAIL: INVALID EMAIL');
  console.assert(typeof password === 'string' && password !== "", 'FAIL: INVALID PASSWORD');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');

  var authenticationBody = util.format("email=%s&password=%s", email, password);
  var requestHost = subdomain + ".teammorale.com";
  _makeAnonymousRequest.call(this, requestHost, '/api/v1/in', 'POST', authenticationBody, callback);
  return this;
};

Morale.prototype.runTicketCommand = function(projectId, command, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof command === 'string' && command !== "", 'FAIL: INVALID COMMAND');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  var commandJson = {
    command: command
  };
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId + '/tickets', 'POST', commandJson, callback);
  return this;
};

Morale.prototype.getProjects = function(callback) {
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects', 'GET', callback);
  return this;
};

Morale.prototype.getProject = function(projectId, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId, 'GET', callback);
  return this;
};

Morale.prototype.addProject = function(project, callback) {
  console.assert(typeof project === 'object' && project.hasOwnProperty("name"), 'FAIL: INVALID PROJECT');
  console.assert(project.hasOwnProperty("name") && typeof project.name === 'string' && project.name !== "", 'FAIL: PROJECT MUST CONTAIN A NAME');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  var projectRequestData = {
    project: {
      name: project.name
    }
  };
  makeAuthenticatedRequest.call(this, '/api/v1/projects', 'POST', projectRequestData, callback);
  return this;
};

Morale.prototype.updateProject = function(project, callback) {
  console.assert(typeof project === 'object' && project.hasOwnProperty("name"), 'FAIL: INVALID PROJECT');
  console.assert(project.hasOwnProperty("id") && typeof project.id === 'number' && project.id > 0, 'FAIL: PROJECT MUST CONTAIN A VALID ID');
  console.assert(project.hasOwnProperty("name") && typeof project.name === 'string' && project.name !== "", 'FAIL: PROJECT MUST CONTAIN A NAME');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  var projectRequestData = {
    project: {
      name: project.name
    }
  };
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + project.id, 'PUT', projectRequestData, callback);
  return this;
};

Morale.prototype.deleteProject = function(projectId, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId, 'DELETE', callback);
  return this;
};

Morale.prototype.getTickets = function(projectId, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId + '/tickets', 'GET', callback);
  return this;
};

Morale.prototype.getTicket = function(projectId, ticketIdentifier, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof ticketIdentifier === 'number' && ticketIdentifier > 0, 'FAIL: INVALID TICKETID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId + '/tickets/' + ticketIdentifier, 'GET', callback);
  return this;
};

Morale.prototype.addTicket = function(ticket, callback) {
  console.assert(typeof ticket === 'object', 'FAIL: INVALID TICKET');
  console.assert(!ticket.hasOwnProperty("identifier"), 'FAIL: NEW TICKETS MUST NOT CONTAIN AN IDENTIFIER');
  console.assert(ticket.hasOwnProperty("title") && typeof ticket.title === 'string' && ticket.title !== "", 'FAIL: TICKET MUST CONTAIN A TITLE');
  console.assert(ticket.hasOwnProperty("project_id") && typeof ticket.project_id === 'number' && ticket.project_id > 0, 'FAIL: TICKET MUST CONTAIN A VALID PROJECT_ID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');

  return this.runTicketCommand(ticket.project_id, buildTicketCommand.call(this, ticket), callback);
};

Morale.prototype.updateTicket = function(ticket, callback) {
  console.assert(typeof ticket === 'object', 'FAIL: INVALID TICKET');
  console.assert(ticket.hasOwnProperty("identifier") && typeof ticket.identifier === 'number' && ticket.identifier > 0, 'FAIL: TICKET MUST CONTAIN A VALID IDENTIFIER');
  console.assert(ticket.hasOwnProperty("project_id") && typeof ticket.project_id === 'number' && ticket.project_id > 0, 'FAIL: TICKET MUST CONTAIN A VALID PROJECT_ID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');

  var command = util.format("#%d: %s", ticket.identifier, buildTicketCommand.call(this, ticket));
  return this.runTicketCommand(ticket.project_id, command, callback);
};

Morale.prototype.deleteTicket = function(projectId, ticketIdentifier, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof ticketIdentifier === 'number' && ticketIdentifier > 0, 'FAIL: INVALID TICKETID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  return this.runTicketCommand(projectId, util.format("d #%d:", ticketIdentifier), callback);
};

Morale.prototype.archiveTicket = function(projectId, ticketIdentifier, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof ticketIdentifier === 'number' && ticketIdentifier > 0, 'FAIL: INVALID TICKETID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  return this.runTicketCommand(projectId, util.format("a #%d:", ticketIdentifier), callback);
};
<commit_msg>Updated code according to some JSLint issues<commit_after>var EventEmitter = require('events').EventEmitter,
    http = require('http'),
    https = require('https'),
    util = require('util'),
    underscore = require('underscore'),
    pkg = require('../package.json');
var VERSION = pkg.version;

function Morale(subdomain, key) {
  if (!(this instanceof Morale)) {
    return new Morale(subdomain, key);
  }

  console.assert(typeof subdomain === 'string' && subdomain !== "", 'FAIL: INVALID SUBDOMAIN');
  console.assert(typeof key === 'string' && key !== "", 'FAIL: INVALID KEY');
  console.assert(/^[a-zA-Z0-9\-]+$/.test(subdomain), 'FAIL: INVALID SUBDOMAIN');

  this.options = {
    subdomain: subdomain,
    api_key: key
  };

  return this;
}

module.exports = Morale;

var makeRequest = function(reqOptions, reqData, callback) {
  if (typeof reqData === 'function') {
    callback = reqData;
    reqData = undefined;
  }

  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  console.assert(reqData === undefined || typeof reqData === 'object' || typeof reqData === 'string', 'FAIL: UNEXPECTED REQDATA');

  var requestData;
  var requestLength = 0;
  if (typeof reqData === 'object') {
    requestData = JSON.stringify(reqData);
    requestLength = requestData.length;
  }
  if (typeof reqData === 'string') {
    requestData = reqData;
    requestLength = requestData.length;
  }

  reqOptions.headers = {
      'Accept': '*/*',
      'Connection': 'close',
      'User-Agent': 'morale-node/' + VERSION,
      'Content-length': requestLength
  };

  var resCallback = function(res) {
    var responseData = '';
    res.setEncoding('utf8');

    res.on('data', function(chunk) {
      responseData += chunk;
    });

    res.on('end', function() {
      if (res.statusCode >= 200 && res.statusCode <= 202) {
        callback(null, JSON.parse(responseData));
      } else {
        var jsonData;
        if (typeof res.headers['content-type'] === 'string' && res.headers['content-type'] === 'application/json' && typeof responseData === 'string' && responseData !== "") { jsonData = JSON.parse(responseData); }
        var errMessage = (typeof jsonData === 'object' && jsonData.hasOwnProperty("error")) ? jsonData.error : http.STATUS_CODES[res.statusCode];

        callback({
          statusCode: res.statusCode,
          message: errMessage
        }, undefined);
      }
    });
  };

  var req = https.request(reqOptions, resCallback);

  req.on('error', function(err) {
    callback(err, null);
  });

  if (requestData !== undefined) { req.write(requestData); }

  req.end();
};

var makeAnonymousRequest = function(reqHost, reqPath, reqMethod, reqData, callback) {
  if (typeof reqData === 'function') {
    callback = reqData;
    reqData = undefined;
  }

  console.assert(typeof reqHost === 'string' && reqHost !== "", 'FAIL: INVALID HOST');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  console.assert(reqPath.charAt(0) === '/', 'FAIL: UNEXPECTED URLPATH');
  console.assert(reqData === undefined || typeof reqData === 'object' || typeof reqData === 'string', 'FAIL: UNEXPECTED REQDATA');

  var requestOptions = {
    host: reqHost.toLowerCase(),
    path: reqPath,
    method: reqMethod
  };

  makeRequest.call(this, requestOptions, reqData, callback);
};

var makeAuthenticatedRequest = function(reqPath, reqMethod, reqData, callback) {
  if (typeof reqData === 'function') {
    callback = reqData;
    reqData = undefined;
  }

  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  console.assert(reqPath.charAt(0) === '/', 'FAIL: UNEXPECTED URLPATH');
  console.assert(reqData === undefined || typeof reqData === 'object', 'FAIL: UNEXPECTED REQDATA');

  var requestOptions = {
    host: util.format("%s.teammorale.com", this.options.subdomain).toLowerCase(),
    path: reqPath,
    method: reqMethod,
    auth: this.options.subdomain + ":" + this.options.api_key
  };

  makeRequest.call(this, requestOptions, reqData, callback);
};

var buildTicketCommand = function(ticket) {
  console.assert(typeof ticket === 'object', 'FAIL: INVALID TICKET');

  var propertyWhiteList = ["title", "description", "due_date", "type", "assigned_to", "priority"];
  var ticketProperties = underscore.intersection(underscore.keys(ticket), propertyWhiteList);
  var addPropertyToCommandArray = function(commands, property) {
    return commands.concat(util.format("%s: %s", property, ticket[property]));
  };

  var commandList = underscore.reduce(ticketProperties, addPropertyToCommandArray, []);
  return commandList.join(" ");
};

module.exports.GetApiToken = function(subdomain, email, password, callback) {
  console.assert(typeof subdomain === 'string' && subdomain !== "", 'FAIL: INVALID SUBDOMAIN');
  console.assert(typeof email === 'string' && email !== "", 'FAIL: INVALID EMAIL');
  console.assert(typeof password === 'string' && password !== "", 'FAIL: INVALID PASSWORD');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');

  var authenticationBody = util.format("email=%s&password=%s", email, password);
  var requestHost = subdomain + ".teammorale.com";
  makeAnonymousRequest.call(this, requestHost, '/api/v1/in', 'POST', authenticationBody, callback);
  return this;
};

Morale.prototype.runTicketCommand = function(projectId, command, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof command === 'string' && command !== "", 'FAIL: INVALID COMMAND');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  var commandJson = {
    command: command
  };
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId + '/tickets', 'POST', commandJson, callback);
  return this;
};

Morale.prototype.getProjects = function(callback) {
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects', 'GET', callback);
  return this;
};

Morale.prototype.getProject = function(projectId, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId, 'GET', callback);
  return this;
};

Morale.prototype.addProject = function(project, callback) {
  console.assert(typeof project === 'object' && project.hasOwnProperty("name"), 'FAIL: INVALID PROJECT');
  console.assert(project.hasOwnProperty("name") && typeof project.name === 'string' && project.name !== "", 'FAIL: PROJECT MUST CONTAIN A NAME');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  var projectRequestData = {
    project: {
      name: project.name
    }
  };
  makeAuthenticatedRequest.call(this, '/api/v1/projects', 'POST', projectRequestData, callback);
  return this;
};

Morale.prototype.updateProject = function(project, callback) {
  console.assert(typeof project === 'object' && project.hasOwnProperty("name"), 'FAIL: INVALID PROJECT');
  console.assert(project.hasOwnProperty("id") && typeof project.id === 'number' && project.id > 0, 'FAIL: PROJECT MUST CONTAIN A VALID ID');
  console.assert(project.hasOwnProperty("name") && typeof project.name === 'string' && project.name !== "", 'FAIL: PROJECT MUST CONTAIN A NAME');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  var projectRequestData = {
    project: {
      name: project.name
    }
  };
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + project.id, 'PUT', projectRequestData, callback);
  return this;
};

Morale.prototype.deleteProject = function(projectId, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId, 'DELETE', callback);
  return this;
};

Morale.prototype.getTickets = function(projectId, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId + '/tickets', 'GET', callback);
  return this;
};

Morale.prototype.getTicket = function(projectId, ticketIdentifier, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof ticketIdentifier === 'number' && ticketIdentifier > 0, 'FAIL: INVALID TICKETID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  makeAuthenticatedRequest.call(this, '/api/v1/projects/' + projectId + '/tickets/' + ticketIdentifier, 'GET', callback);
  return this;
};

Morale.prototype.addTicket = function(ticket, callback) {
  console.assert(typeof ticket === 'object', 'FAIL: INVALID TICKET');
  console.assert(!ticket.hasOwnProperty("identifier"), 'FAIL: NEW TICKETS MUST NOT CONTAIN AN IDENTIFIER');
  console.assert(ticket.hasOwnProperty("title") && typeof ticket.title === 'string' && ticket.title !== "", 'FAIL: TICKET MUST CONTAIN A TITLE');
  console.assert(ticket.hasOwnProperty("project_id") && typeof ticket.project_id === 'number' && ticket.project_id > 0, 'FAIL: TICKET MUST CONTAIN A VALID PROJECT_ID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');

  return this.runTicketCommand(ticket.project_id, buildTicketCommand.call(this, ticket), callback);
};

Morale.prototype.updateTicket = function(ticket, callback) {
  console.assert(typeof ticket === 'object', 'FAIL: INVALID TICKET');
  console.assert(ticket.hasOwnProperty("identifier") && typeof ticket.identifier === 'number' && ticket.identifier > 0, 'FAIL: TICKET MUST CONTAIN A VALID IDENTIFIER');
  console.assert(ticket.hasOwnProperty("project_id") && typeof ticket.project_id === 'number' && ticket.project_id > 0, 'FAIL: TICKET MUST CONTAIN A VALID PROJECT_ID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');

  var command = util.format("#%d: %s", ticket.identifier, buildTicketCommand.call(this, ticket));
  return this.runTicketCommand(ticket.project_id, command, callback);
};

Morale.prototype.deleteTicket = function(projectId, ticketIdentifier, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof ticketIdentifier === 'number' && ticketIdentifier > 0, 'FAIL: INVALID TICKETID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  return this.runTicketCommand(projectId, util.format("d #%d:", ticketIdentifier), callback);
};

Morale.prototype.archiveTicket = function(projectId, ticketIdentifier, callback) {
  console.assert(typeof projectId === 'number' && projectId > 0, 'FAIL: INVALID PROJECTID');
  console.assert(typeof ticketIdentifier === 'number' && ticketIdentifier > 0, 'FAIL: INVALID TICKETID');
  console.assert(typeof callback === 'function', 'FAIL: INVALID CALLBACK');
  return this.runTicketCommand(projectId, util.format("a #%d:", ticketIdentifier), callback);
};
<|endoftext|>


<commit_before>export default function renderImage ( img ) {
	var width, height, loadHandler;

	// if this is an <img>, and we're in a crap browser, we may need to prevent it
	// from overriding width and height when it loads the src
	if ( ( width = this.getAttribute( 'width' ) ) || ( height = this.getAttribute( 'height' ) ) ) {
		this.node.addEventListener( 'load', loadHandler = function () {
			if ( width ) {
				this.node.width = width.value;
			}

			if ( height ) {
				this.node.height = height.value;
			}

			this.node.removeEventListener( 'load', loadHandler, false );
		}, false );
	}
}
<commit_msg>leftover from #788<commit_after>export default function renderImage ( img ) {
	var width, height, loadHandler;

	// if this is an <img>, and we're in a crap browser, we may need to prevent it
	// from overriding width and height when it loads the src
	if ( ( width = img.getAttribute( 'width' ) ) || ( height = img.getAttribute( 'height' ) ) ) {
		img.node.addEventListener( 'load', loadHandler = function () {
			if ( width ) {
				img.node.width = width.value;
			}

			if ( height ) {
				img.node.height = height.value;
			}

			img.node.removeEventListener( 'load', loadHandler, false );
		}, false );
	}
}
<|endoftext|>


<commit_before>function serialize( mixed_value ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Arpad Ray (mailto:arpad@php.net)
    // +   improved by: Dino
    // +   bugfixed by: Andrej Pavlovic
    // +   bugfixed by: Garagoth
    // +   bugfixed by: Russell Walker
    // %          note: We feel the main purpose of this function should be to ease the transport of data between php & js
    // %          note: Aiming for PHP-compatibility, we have to translate objects to arrays
    // *     example 1: serialize(['Kevin', 'van', 'Zonneveld']);
    // *     returns 1: 'a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}'
    // *     example 2: serialize({firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'});
    // *     returns 2: 'a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}'

    var _getType = function( inp ) {
        var type = typeof inp, match;
        var key;
        if (type == 'object' && !inp) {
            return 'null';
        }
        if (type == "object") {
            if (!inp.constructor) {
                return 'object';
            }
            var cons = inp.constructor.toString();
            match = cons.match(/(\w+)\(/);
            if (match) {
                cons = match[1].toLowerCase();
            }
            var types = ["boolean", "number", "string", "array"];
            for (key in types) {
                if (cons == types[key]) {
                    type = types[key];
                    break;
                }
            }
        }
        return type;
    };
    var type = _getType(mixed_value);
    var val, ktype = '';

    switch (type) {
        case "function":
            val = "";
            break;
        case "undefined":
            val = "N";
            break;
        case "boolean":
            val = "b:" + (mixed_value ? "1" : "0");
            break;
        case "number":
            val = (Math.round(mixed_value) == mixed_value ? "i" : "d") + ":" + mixed_value;
            break;
        case "string":
            val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + mixed_value + "\"";
            break;
        case "array":
        case "object":
            val = "a";
            /*
            if (type == "object") {
                var objname = mixed_value.constructor.toString().match(/(\w+)\(\)/);
                if (objname == undefined) {
                    return;
                }
                objname[1] = serialize(objname[1]);
                val = "O" + objname[1].substring(1, objname[1].length - 1);
            }
            */
            var count = 0;
            var vals = "";
            var okey;
            var key;
            for (key in mixed_value) {
                ktype = _getType(mixed_value[key]);
                if (ktype == "function") {
                    continue;
                }

                okey = (key.match(/^[0-9]+$/) ? parseInt(key, 10) : key);
                vals += serialize(okey) +
                        serialize(mixed_value[key]);
                count++;
            }
            val += ":" + count + ":{" + vals + "}";
            break;
    }
    if (type != "object" && type != "array") {
        val += ";";
    }
    return val;
}<commit_msg>utf-8 characters support (php uses size in bytes, js uses size in symbols)<commit_after>function serialize( mixed_value ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Arpad Ray (mailto:arpad@php.net)
    // +   improved by: Dino
    // +   bugfixed by: Andrej Pavlovic
    // +   bugfixed by: Garagoth
    // +      input by: DtTvB (http://dt.in.th/2008-09-16.string-length-in-bytes.html)
    // +   bugfixed by: Russell Walker
    // %          note: We feel the main purpose of this function should be to ease the transport of data between php & js
    // %          note: Aiming for PHP-compatibility, we have to translate objects to arrays
    // *     example 1: serialize(['Kevin', 'van', 'Zonneveld']);
    // *     returns 1: 'a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}'
    // *     example 2: serialize({firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'});
    // *     returns 2: 'a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}'

    var _getType = function( inp ) {
        var type = typeof inp, match;
        var key;
        if (type == 'object' && !inp) {
            return 'null';
        }
        if (type == "object") {
            if (!inp.constructor) {
                return 'object';
            }
            var cons = inp.constructor.toString();
            match = cons.match(/(\w+)\(/);
            if (match) {
                cons = match[1].toLowerCase();
            }
            var types = ["boolean", "number", "string", "array"];
            for (key in types) {
                if (cons == types[key]) {
                    type = types[key];
                    break;
                }
            }
        }
        return type;
    };
    var type = _getType(mixed_value);
    var val, ktype = '';

    switch (type) {
        case "function":
            val = "";
            break;
        case "undefined":
            val = "N";
            break;
        case "boolean":
            val = "b:" + (mixed_value ? "1" : "0");
            break;
        case "number":
            val = (Math.round(mixed_value) == mixed_value ? "i" : "d") + ":" + mixed_value;
            break;
        case "string":
            val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + mixed_value + "\"";
            break;
        case "array":
        case "object":
            val = "a";
            /*
            if (type == "object") {
                var objname = mixed_value.constructor.toString().match(/(\w+)\(\)/);
                if (objname == undefined) {
                    return;
                }
                objname[1] = serialize(objname[1]);
                val = "O" + objname[1].substring(1, objname[1].length - 1);
            }
            */
            var count = 0;
            var vals = "";
            var okey;
            var key;
            for (key in mixed_value) {
                ktype = _getType(mixed_value[key]);
                if (ktype == "function") {
                    continue;
                }

                okey = (key.match(/^[0-9]+$/) ? parseInt(key, 10) : key);
                vals += serialize(okey) +
                        serialize(mixed_value[key]);
                count++;
            }
            val += ":" + count + ":{" + vals + "}";
            break;
    }
    if (type != "object" && type != "array") {
        val += ";";
    }
    return val;
}<|endoftext|>


<commit_before>'use strict';

var util = require('util'),
    XModule = require('../../../core/XModule'),
    ltx = require('ltx'),
    JID = require('node-xmpp-core').JID,
    NS = require('../namespace'),
    Iq = require('node-xmpp-core').Stanza.Iq,
    pubutil = require('../Util');

var Subscription = function (storage, options) {
    XModule.call(this);
    this.logger = require('../../../core/Logger')('xep-0060:subscription');

    // storage
    this.storage = storage;

    // pubsub options
    this.options = options;
};

util.inherits(Subscription, XModule);

Subscription.prototype.name = 'XEP-0060: Subscription';

Subscription.prototype.match = function (stanza) {
    // owner requests
    return (stanza.is('iq') && stanza.getChild('pubsub', NS.PUBSUB) && stanza.getChild('pubsub', NS.PUBSUB).getChild('subscribe')) ||
    // configure requests
    (stanza.is('iq') && stanza.getChild('pubsub', NS.PUBSUB) && stanza.getChild('pubsub', NS.PUBSUB).getChild('unsubscribe'));
};

/**
 * @description sends the history to the given jid
 * @param node pub sub node
 * @param jid reciever of the histroy events
 *
 * TODO make dependend on node default
 * @see http://xmpp.org/extensions/xep-0060.html#subscriber-subscribe-last
 */
Subscription.prototype.sendHistory = function (node, jid) {
    this.logger.debug('send history');
    var self = this;
    node.getEvents().then(function (events) {
        // logger.debug(JSON.stringify(events));
        if (events && events.length && events.length > 0) {

            events.forEach(function(evt){
                // logger.debug(JSON.stringify(evt));
                try {
                    // extract message
                    var el = ltx.parse(evt.content);

                    // send to  jid
                    el.attrs.to = jid;

                    // route message
                    self.send(el, null);

                } catch (err) {
                    self.logger.error(err);
                }
            });
        }
    }).catch(function(err){
        self.logger.error(err);
    });
};

/**
 * @description subscribes a new jid
 * @param stanza full pubsub message stanza
 * @param pubsub already extracted pubsub child node
 * @see http://xmpp.org/extensions/xep-0060.html#subscriber-subscribe
 */
Subscription.prototype.handleSubscribe = function (user, node, stanza, subcribeEl) {
    this.logger.debug('handleSubscribe');
    var self = this;

    var fromJid = new JID(stanza.attrs.from);
    var subscriberJid = new JID(subcribeEl.attrs.jid);

    // check that jids match
    if (!fromJid.bare().equals(subscriberJid.bare())) {
        this.logger.error('subscriptions jids do not match : ' + fromJid.toString() + ' != ' + subscriberJid.toString());
        // this is a wrong stanza
        this.sendError(stanza, pubutil.Error.BadRequest);
        return;
    }

    this.logger.debug('Subscribe ' + subscriberJid.toString() + ' to ' + node.name);

    // store new subscriber
    node.subscribe(user, {
        affiliation: self.storage.ChannelSub.Affiliation.Member,
        substate: self.storage.ChannelSub.SubState.Subscribed
    }).then(function () {
            // Success Case, send confirmation
            var msg = new Iq({
                from: self.options.domain,
                to: stanza.attrs.from,
                id: stanza.attrs.id,
                type: 'result'
            });

            //var humanname = node.getConfiguration(NodeConfig.PUBSUB_NODE_Title);

            msg.c('pubsub', {
                'xmlns': NS.PUBSUB
            }).c('subscription', {
                'node': subcribeEl.attrs.node,
                'jid': subcribeEl.attrs.jid,
                'subscription': 'subscribed'
            });

            // send subscribe response
            self.send(msg);

            // send old itemes to new subscriber
            // TODO: only send history if persistence is activated
            self.sendHistory(node, subcribeEl.attrs.jid);

        }).
    catch (
        function (err) {
            // error
            self.logger.error(err);
            self.sendError(stanza, pubutil.Error.BadRequest);
        }
    );
};

/**
 * @description closes a subscription
 * @param stanza full pubsub message stanza
 * @param pubsub already extracted pubsub child node
 * @see http://xmpp.org/extensions/xep-0060.html#subscriber-unsubscribe
 */
Subscription.prototype.handleUnsubscribe = function (user, node, stanza, unsubscribeEl) {
    this.logger.debug('handleUnsubscribe');
    var self = this;

    var fromJid = new JID(stanza.attrs.from);
    var subscriberJid = new JID(unsubscribeEl.attrs.jid);

    // check that jids match
    if (!fromJid.bare().equals(subscriberJid)) {
        // this is a wrong stanza
        this.sendError(stanza, pubutil.Error.BadRequest);
        return;
    }

    // unregister subscriber
    this.logger.debug('user' + fromJid.bare().toString() + ' unsubscribe the node');
    node.unsubscribe(user).then(
        function () {
            self.sendSuccess(stanza);
        }).
    catch (
        function () {
            self.sendError(stanza, pubutil.Error.NotSubscribed);
        });
};

Subscription.prototype.handle = function (stanza) {
    var pubsub = stanza.getChild('pubsub', NS.PUBSUB);

    // detect method
    var method, pubsubEl = null;
    if (pubsub.getChild('subscribe')) {
        method = 'subscribe';
        pubsubEl = pubsub.getChild('subscribe');
    } else if (pubsub.getChild('unsubscribe')) {
        method = 'unsubscribe';
        pubsubEl = pubsub.getChild('unsubscribe');
    }

    // process
    var self = this;
    var fromJid = new JID(stanza.attrs.from).bare().toString();
    this.storage.findOrCreateUser(fromJid).then(function (user) {
        // detect node name
        var nodename = pubsubEl.attrs.node;
        self.logger.debug('nodename: ' + nodename);

        // okay, we have the user
        switch (method) {
        case 'subscribe':
            // todo use options
            pubutil.findChannel(self.storage, nodename, user, self.options.autoCreateChannel).then(function(node){
                self.logger.debug('found: ' + node);
                if (node) {
                    self.handleSubscribe(user, node, stanza, pubsubEl);
                } else {
                    self.sendError(stanza, pubutil.Error.NotFound);
                }
            });

            break;
        case 'unsubscribe':
            pubutil.findChannel(self.storage, nodename, user, false).then(function(node){
                self.logger.debug('found: ' + node);
                if (node) {
                    self.handleUnsubscribe(user, node, stanza, pubsubEl);
                } else {
                    self.sendError(stanza, pubutil.Error.NotFound);
                }
            });
            break;
        default:
            self.sendError(stanza, pubutil.Error.NotFound);
        }

    });

    return true;
};

module.exports = Subscription;
<commit_msg>improve logging output<commit_after>'use strict';

var util = require('util'),
    XModule = require('../../../core/XModule'),
    ltx = require('ltx'),
    JID = require('node-xmpp-core').JID,
    NS = require('../namespace'),
    Iq = require('node-xmpp-core').Stanza.Iq,
    pubutil = require('../Util');

var Subscription = function (storage, options) {
    XModule.call(this);
    this.logger = require('../../../core/Logger')('xep-0060:subscription');

    // storage
    this.storage = storage;

    // pubsub options
    this.options = options;
};

util.inherits(Subscription, XModule);

Subscription.prototype.name = 'XEP-0060: Subscription';

Subscription.prototype.match = function (stanza) {
    // owner requests
    return (stanza.is('iq') && stanza.getChild('pubsub', NS.PUBSUB) && stanza.getChild('pubsub', NS.PUBSUB).getChild('subscribe')) ||
    // configure requests
    (stanza.is('iq') && stanza.getChild('pubsub', NS.PUBSUB) && stanza.getChild('pubsub', NS.PUBSUB).getChild('unsubscribe'));
};

/**
 * @description sends the history to the given jid
 * @param node pub sub node
 * @param jid reciever of the histroy events
 *
 * TODO make dependend on node default
 * @see http://xmpp.org/extensions/xep-0060.html#subscriber-subscribe-last
 */
Subscription.prototype.sendHistory = function (node, jid) {
    this.logger.debug('send history');
    var self = this;
    node.getEvents().then(function (events) {
        // logger.debug(JSON.stringify(events));
        if (events && events.length && events.length > 0) {

            events.forEach(function(evt){
                // logger.debug(JSON.stringify(evt));
                try {
                    // extract message
                    var el = ltx.parse(evt.content);

                    // send to  jid
                    el.attrs.to = jid;

                    // route message
                    self.send(el, null);

                } catch (err) {
                    self.logger.error(err);
                }
            });
        }
    }).catch(function(err){
        self.logger.error(err);
    });
};

/**
 * @description subscribes a new jid
 * @param stanza full pubsub message stanza
 * @param pubsub already extracted pubsub child node
 * @see http://xmpp.org/extensions/xep-0060.html#subscriber-subscribe
 */
Subscription.prototype.handleSubscribe = function (user, node, stanza, subcribeEl) {
    this.logger.debug('handleSubscribe');
    var self = this;

    var fromJid = new JID(stanza.attrs.from);
    var subscriberJid = new JID(subcribeEl.attrs.jid);

    // check that jids match
    if (!fromJid.bare().equals(subscriberJid.bare())) {
        this.logger.error('subscriptions jids do not match : ' + fromJid.toString() + ' != ' + subscriberJid.toString());
        // this is a wrong stanza
        this.sendError(stanza, pubutil.Error.BadRequest);
        return;
    }

    this.logger.debug('Subscribe ' + subscriberJid.toString() + ' to ' + node.name);

    // store new subscriber
    node.subscribe(user, {
        affiliation: self.storage.ChannelSub.Affiliation.Member,
        substate: self.storage.ChannelSub.SubState.Subscribed
    }).then(function () {
            // Success Case, send confirmation
            var msg = new Iq({
                from: self.options.domain,
                to: stanza.attrs.from,
                id: stanza.attrs.id,
                type: 'result'
            });

            //var humanname = node.getConfiguration(NodeConfig.PUBSUB_NODE_Title);

            msg.c('pubsub', {
                'xmlns': NS.PUBSUB
            }).c('subscription', {
                'node': subcribeEl.attrs.node,
                'jid': subcribeEl.attrs.jid,
                'subscription': 'subscribed'
            });

            // send subscribe response
            self.send(msg);

            // send old itemes to new subscriber
            // TODO: only send history if persistence is activated
            self.sendHistory(node, subcribeEl.attrs.jid);

        }).
    catch (
        function (err) {
            // error
            self.logger.error(err);
            self.sendError(stanza, pubutil.Error.BadRequest);
        }
    );
};

/**
 * @description closes a subscription
 * @param stanza full pubsub message stanza
 * @param pubsub already extracted pubsub child node
 * @see http://xmpp.org/extensions/xep-0060.html#subscriber-unsubscribe
 */
Subscription.prototype.handleUnsubscribe = function (user, node, stanza, unsubscribeEl) {
    this.logger.debug('handleUnsubscribe');
    var self = this;

    var fromJid = new JID(stanza.attrs.from);
    var subscriberJid = new JID(unsubscribeEl.attrs.jid);

    // check that jids match
    if (!fromJid.bare().equals(subscriberJid)) {
        // this is a wrong stanza
        this.sendError(stanza, pubutil.Error.BadRequest);
        return;
    }

    // unregister subscriber
    this.logger.debug('user' + fromJid.bare().toString() + ' unsubscribe the node');
    node.unsubscribe(user).then(
        function () {
            self.sendSuccess(stanza);
        }).
    catch (
        function () {
            self.sendError(stanza, pubutil.Error.NotSubscribed);
        });
};

Subscription.prototype.handle = function (stanza) {
    var pubsub = stanza.getChild('pubsub', NS.PUBSUB);

    // detect method
    var method, pubsubEl = null;
    if (pubsub.getChild('subscribe')) {
        method = 'subscribe';
        pubsubEl = pubsub.getChild('subscribe');
    } else if (pubsub.getChild('unsubscribe')) {
        method = 'unsubscribe';
        pubsubEl = pubsub.getChild('unsubscribe');
    }

    // process
    var self = this;
    var fromJid = new JID(stanza.attrs.from).bare().toString();
    this.storage.findOrCreateUser(fromJid).then(function (user) {
        // detect node name
        var nodename = pubsubEl.attrs.node;
        self.logger.debug('nodename: ' + nodename);

        // okay, we have the user
        switch (method) {
        case 'subscribe':
            // todo use options
            pubutil.findChannel(self.storage, nodename, user, self.options.autoCreateChannel).then(function(node){
                self.logger.debug('subscribe: ' + JSON.stringify(node));
                if (node) {
                    self.handleSubscribe(user, node, stanza, pubsubEl);
                } else {
                    self.sendError(stanza, pubutil.Error.NotFound);
                }
            });

            break;
        case 'unsubscribe':
            pubutil.findChannel(self.storage, nodename, user, false).then(function(node){
                self.logger.debug('unsubscribe: ' + JSON.stringify(node));
                if (node) {
                    self.handleUnsubscribe(user, node, stanza, pubsubEl);
                } else {
                    self.sendError(stanza, pubutil.Error.NotFound);
                }
            });
            break;
        default:
            self.sendError(stanza, pubutil.Error.NotFound);
        }

    });

    return true;
};

module.exports = Subscription;
<|endoftext|>


<commit_before>/* MUDKit */
/* Engine to transform a MUD stream to HTML and manage Telnet negotiation. */

var net = require('net'),
    util = require('util'),
    stream = require('stream'),
    zlib = require('zlib');

const CLIENT = 'MUDKIT';

const IAC     = 255; // interpret as command
const DONT    = 254; // you are not to use option
const DO      = 253; // please use option
const WONT    = 252; // I won't use option
const WILL    = 251; // I will use option
const SB      = 250; // sub-negotiation
const GA      = 249; // Go-ahead
const EL      = 248; // Erase line
const EC      = 247; // Erase character
const AYT     = 246; // Are you there?
const AO      = 245; // Abort output (but let prog finish)
const IP      = 244; // Interrupt (permanently)
const BREAK   = 243;
const DM      = 242; // Data mark
const NOP     = 241;
const SE      = 240; // End sub-negotiation
const EOR     = 239; // End of record (transparent mode)
const ABORT   = 238; // Abort process
const SUSP    = 237; // Suspend process
const EOF     = 236; // End of file
const SYNCH   = 242;

const BEL     = 7;
const LF      = 10;
const CR      = 13;

const TELOPT_BINARY            = 0; // RFC 856
const TELOPT_ECHO              = 1; // RFC 857
const TELOPT_SGA               = 3; // RFC 858
const TELOPT_STATUS            = 5; // RFC 859
const TELOPT_TIMING_MARK       = 6; // RFC 860
const TELOPT_TTYPE             = 24; // RFC 930, 1091, http://tintin.sourceforge.net/mtts/
const TELOPT_EOR               = 25; // RFC 885
const TELOPT_NAWS              = 31; // RFC 1073 (Negotiate About Window Size)
const TELOPT_TERMINAL_SPEED    = 32; // RFC 1079
const TELOPT_LINE_MODE         = 34; // RFC 1184
const TELOPT_NEW_ENVIRON       = 39; // RFC 1572
const TELOPT_CHARSET           = 42; // RFC 2066
const TELOPT_MSDP              = 69;
const TELOPT_MSSP              = 70; // MUD Server Status Protocol
const TELOPT_COMPRESS          = 85; // MUD Client Compression Protocol (version 1)
const TELOPT_COMPRESS2         = 86; // MUD Client Compression Protocol (version 2) http://www.zuggsoft.com/zmud/mcp.htm
const TELOPT_MSP               = 90; // MUD Sound Protocol http://www.zuggsoft.com/zmud/msp.htm
const TELOPT_MXP               = 91; // MUD eXtension Protocol http://www.zuggsoft.com/zmud/mxp.htm
const TELOPT_ZMP               = 93; // Zenith MUD Protocol http://discworld.starturtle.net/external/protocols/zmp.html
const TELOPT_GCMP              = 201; // Generic Mud Communication Protocol http://www.ironrealms.com/gmcp-doc
const TELOPT_EXOPL             = 255; // RFC 861

const TELQUAL_SEND             = 1;
const TELQUAL_IS               = 0;

const ANSI_COLOR_START = [27, 91]; // ESC[
const ANSI_COLOR_END = 109; // m
const ANSI_COLOR_DELIM = 59; // ;
const COLORS = {
    0:  'normal',
    1:  'bold',
    2:  'faint',
    3:  'standout',
    4:  'underline',
    5:  'blink',
    8:  'invisible',
    10: 'normal-bg',
    22: 'normal-weight',
    23: 'no-standout',
    24: 'no-underline',
    25: 'no-blink',
    30: 'black',
    31: 'red',
    32: 'green',
    33: 'yellow',
    34: 'blue',
    35: 'magenta',
    36: 'cyan',
    37: 'white',
    39: 'default',
    40: 'black-bg',
    41: 'red-bg',
    42: 'green-bg',
    43: 'yellow-bg',
    44: 'blue-bg',
    45: 'magenta-bg',
    46: 'cyan-bg',
    47: 'white-bg',
    49: 'default-bg'
};

String.prototype.charCode = function () {
    for (var arr = this.split(''), i = this.length - 1; i >= 0; i--) {
        arr[i] = arr[i].charCodeAt(0);
    }

    return arr;
};

Array.prototype.flatten = function () {
    return this.reduce(function flatten(a, b) {
        if (!(a instanceof Array)) { a = [a]; }

        if (b instanceof Array) {
            b = b.reduce(flatten);
        }

        return a.concat(b);
    }, []);
};

/* A telnet protocol stream.
 * Emits processed data:
 *  'data' -> what the server sent
 *  other  -> telnet protocol command information
 */
function MUDKit (port, host, callback, terminal) {
    var mudkit = this;

    stream.Transform.call(this);

    this.state = {
        mtts: 0,
        mccp: false,
        color: 'normal'
    };

    this.env = {
        terminal: {
            rows: [0, 24],
            cols: [0, 80]
        }
    };

    this.encoding = 'ascii';

    if (terminal) {
        this.windowSize(terminal);
    }

    this.inflate = zlib.createInflate();
    this.inflate.on('data', function (chunk) {
        mudkit.processChunk(chunk);
    });

    port = port || 4242;
    host = host || localhost;
    this.connect(port, host, callback);

    this.mud.on('end', function () {
        mudkit.connect(port, host, callback);
    });
}
util.inherits(MUDKit, stream.Transform);

MUDKit.prototype._transform = function (chunk, encoding, callback) {
    var err;

    // Decompress the chunk if MCCP is enabled
    if (this.state.mccp) {
        try {
           this.inflate.write(chunk);
        } catch (err) {
           this.processChunk(chunk, encoding);
        }
    } else {
        this.processChunk(chunk, encoding);
    }

    err = this.err;
    this.err = null;

    callback(err);
};

MUDKit.prototype.connect = function (port, host, callback) {
    this.mud = net.createConnection(port, host);

    this.mud.pipe(this);
    this.mud.on('connected', callback);
};

MUDKit.prototype.sendCommand = function (command) {
    var bytes = [IAC];

    if (command instanceof Array) {
        bytes = bytes.concat(command);
    } else {
        bytes.push(command);
    }

    console.log('send: ' + bytes);

    this.mud.write(new Buffer(bytes, this.encoding));
};

MUDKit.prototype.processChunk = function (chunk, encoding) {
    var mudkit = this, i, l, last = 0, remaining = '',
        output = '';

    function sliceChunk () {
        return chunk.slice(last, i).toString(this.encoding);
    }

    function processNewLine () {
        output += '
';
        i++;
    }

    function process255 () {
        output += String.fromCharCode(255);
        console.log('double 255');
        i++;
    }

    function processANSIColor () {
        var color, classes = '';

        i += 2;
        last = i;

        for (; i < l; i++) {
            if (ANSI_COLOR_END === chunk[i]) {
                break;
            }
        }

        color = sliceChunk().split(';');

        for (var j = 0, ll = color.length; j < ll; j++) {
            if (COLORS[color[j]]) {
                classes += COLORS[color[j]] + ' ';
            }
        }

        mudkit.state.color = classes;

        if (!classes) { classes = 'normal'; }

        output += '';
    }

    function processCommand () {
        var cmd, opt;

        function processSB () {
            var sub, term;

            i++;
            sub = chunk[i];

            switch (sub) {
            case TELOPT_TTYPE:
                console.log('SB TTYPE');
                if (TELQUAL_SEND === chunk[i+1]) {
                    i++;

                    switch (mudkit.state.mtts) {
                    case 0:
                        term = CLIENT.charCode();
                        mudkit.state.mtts++;
                        break;
                    case 1:
                        term = 'ANSI'.charCode();
                        mudkit.state.mtts++;
                        break;
                    default:
                        term = 'MTTS 5'.charCode();
                    }

                    mudkit.sendCommand([SB, TELOPT_TTYPE, TELQUAL_IS, term, IAC, SE]);
                }
                break;

            case TELOPT_COMPRESS2:
                console.log('SB COMPRESS2');
                console.log('server now using deflate');
                mudkit.state.mccp = true;
                break;

            default:
                console.log('Unkown SB command: ' + sub);
            }

            for (; i < l; i++) {
                if (IAC === chunk[i] && SE === chunk[i+1]) {
                    i++;
                    break;
                }
            }
        }

        i++;
        cmd = chunk[i];

        switch (cmd) {
        case GA:
            console.log('GA');
            break;

        case SB:
            processSB();
            break;

        case DO:
            opt = chunk[++i];

            switch (opt) {
            case TELOPT_ECHO:
                console.log('DO ECHO - not implemented');
                break;

            case TELOPT_TTYPE:
                console.log('DO TTYPE');
                mudkit.sendCommand([WILL, TELOPT_TTYPE]);
                break;

            case TELOPT_NAWS:
                console.log('DO NAWS');
                mudkit.sendCommand([WILL, TELOPT_NAWS]);
                mudkit.state.naws = true;
                mudkit.windowSize();
                break;

            case TELOPT_MXP:
                console.log('DO MXP');
                mudkit.sendCommand([WONT, TELOPT_MXP]);
                break;

            case TELOPT_NEW_ENVIRON:
                console.log('DO NEW_ENVIRON');
                mudkit.sendCommand([WONT, TELOPT_NEW_ENVIRON]);
                break;

            default:
                console.log('Unkown DO telopt: ' + opt);
            }
            break;

        case DONT:
            opt = chunk[++i];

            switch (opt) {
            case TELOPT_ECHO:
                console.log('DONT ECHO - not implemented');
                break;

            case TELOPT_TTYPE:
                console.log('DONT TTYPE');
                mudkit.state.mtts = 0;
                break;

            default:
                console.log('Unkown DONT telopt: ' + opt);
            }
            break;

        case WILL:
            opt = chunk[++i];

            switch (opt) {
            case TELOPT_ECHO:
                console.log('WILL ECHO');
                mudkit.sendCommand([DO,TELOPT_ECHO]);
                break;

            case TELOPT_COMPRESS2:
                console.log('WILL COMPRESS2');
                mudkit.sendCommand([DO, TELOPT_COMPRESS2]);
                break;
            case TELOPT_MSSP:
                console.log('WILL MSSP');
                mudkit.sendCommand([DONT, TELOPT_MSSP]);
                break;
            case TELOPT_ZMP:
                console.log('WILL ZMP');
                mudkit.sendCommand([DONT, TELOPT_ZMP]);
                break;
            case TELOPT_GCMP:
                console.log('WILL GCMP');
                mudkit.sendCommand([DONT, TELOPT_GCMP]);
                break;
            default:
                console.log('Unkown WILL telopt: ' + opt);
            }
            break;

        case WONT:
            opt = chunk[++i];

            switch (opt) {
            case TELOPT_ECHO:
                console.log('WONT ECHO');
                mudkit.sendCommand([DONT,TELOPT_ECHO]);
                break;

            default:
                console.log('Unkown WONT telopt: ' + opt);
            }
            break;

        default:
            console.log('Unkown command: ' + cmd);
        }
    }

    if (!chunk || !chunk.length) { return; }
    if (!Buffer.isBuffer(chunk)) { chunk = new Buffer(chunk, encoding); }

    for (i = 0, l = chunk.length; i < l; i++) {
        //console.log(i + ': ' + chunk[i] + ' = ' + String.fromCharCode(chunk[i]));
        if (CR === chunk[i] && LF === chunk[i+1]) {
            output += sliceChunk();
            processNewLine();
            last = i + 1;
        } else if (ANSI_COLOR_START[0] === chunk[i] && ANSI_COLOR_START[1] === chunk[i+1]) {
            output += sliceChunk();
            processANSIColor();
            last = i + 1;
        } else if (IAC === chunk[i] && IAC !== chunk[i+1]) {
            output += sliceChunk();
            processCommand();
            last = i + 1;
        } else if (IAC === chunk[i] && IAC === chunk[i+1]) {
            output += sliceChunk();
            process255();
            last = i + 1;
        } /*else if () {
            // handle encoding characters for html
            function htmlEscape(str) {
                return String(str)
                    .replace(/&/g, '&') // 38
                    .replace(/"/g, '"') // 34
                    .replace(/'/g, ''') // 39
                    .replace(/</g, '<') // 60
                    .replace(/>/g, '>'); // 62
            }
        }*/
    }

    if (i > last) {
        remaining = sliceChunk();

        if (1 === remaining.length && 255 === remaining[0]) {
            // What is this?
            remaining = '';
        }
    }

    output += remaining + '';

    this.push(output);
};

MUDKit.prototype.windowSize = function (size) {
    const MAX = 65535;
    var cols, rows;

    function convert(num) {
        if (255 === num) { num = [255, 255]; }
        return num;
    }

    function first(num) {
        return convert(Math.floor(num / 256));
    }

    function second(num) {
        return convert(num % 256);
    }

    if (size) {
        if (size.cols > MAX) { size.cols = MAX; }
        if (size.rows > MAX) { size.rows = MAX; }

        cols = [first(size.cols), second(size.cols)].flatten();
        rows = [first(size.rows), second(size.rows)].flatten();

        this.env.terminal = {
            cols: cols,
            rows: rows
        };
    }

    if (this.state.naws) {
        this.sendCommand([SB, TELOPT_NAWS, this.env.terminal.cols, this.env.terminal.rows, IAC, SE].flatten());
    }
};

module.exports = MUDKit;
<commit_msg>Fix TTYPE negotiation<commit_after>/* MUDKit */
/* Engine to transform a MUD stream to HTML and manage Telnet negotiation. */

var net = require('net'),
    util = require('util'),
    stream = require('stream'),
    zlib = require('zlib');

const CLIENT = 'MUDKIT';

const IAC     = 255; // interpret as command
const DONT    = 254; // you are not to use option
const DO      = 253; // please use option
const WONT    = 252; // I won't use option
const WILL    = 251; // I will use option
const SB      = 250; // sub-negotiation
const GA      = 249; // Go-ahead
const EL      = 248; // Erase line
const EC      = 247; // Erase character
const AYT     = 246; // Are you there?
const AO      = 245; // Abort output (but let prog finish)
const IP      = 244; // Interrupt (permanently)
const BREAK   = 243;
const DM      = 242; // Data mark
const NOP     = 241;
const SE      = 240; // End sub-negotiation
const EOR     = 239; // End of record (transparent mode)
const ABORT   = 238; // Abort process
const SUSP    = 237; // Suspend process
const EOF     = 236; // End of file
const SYNCH   = 242;

const BEL     = 7;
const LF      = 10;
const CR      = 13;

const TELOPT_BINARY            = 0; // RFC 856
const TELOPT_ECHO              = 1; // RFC 857
const TELOPT_SGA               = 3; // RFC 858
const TELOPT_STATUS            = 5; // RFC 859
const TELOPT_TIMING_MARK       = 6; // RFC 860
const TELOPT_TTYPE             = 24; // RFC 930, 1091, http://tintin.sourceforge.net/mtts/
const TELOPT_EOR               = 25; // RFC 885
const TELOPT_NAWS              = 31; // RFC 1073 (Negotiate About Window Size)
const TELOPT_TERMINAL_SPEED    = 32; // RFC 1079
const TELOPT_LINE_MODE         = 34; // RFC 1184
const TELOPT_NEW_ENVIRON       = 39; // RFC 1572
const TELOPT_CHARSET           = 42; // RFC 2066
const TELOPT_MSDP              = 69;
const TELOPT_MSSP              = 70; // MUD Server Status Protocol
const TELOPT_COMPRESS          = 85; // MUD Client Compression Protocol (version 1)
const TELOPT_COMPRESS2         = 86; // MUD Client Compression Protocol (version 2) http://www.zuggsoft.com/zmud/mcp.htm
const TELOPT_MSP               = 90; // MUD Sound Protocol http://www.zuggsoft.com/zmud/msp.htm
const TELOPT_MXP               = 91; // MUD eXtension Protocol http://www.zuggsoft.com/zmud/mxp.htm
const TELOPT_ZMP               = 93; // Zenith MUD Protocol http://discworld.starturtle.net/external/protocols/zmp.html
const TELOPT_GCMP              = 201; // Generic Mud Communication Protocol http://www.ironrealms.com/gmcp-doc
const TELOPT_EXOPL             = 255; // RFC 861

const TELQUAL_SEND             = 1;
const TELQUAL_IS               = 0;

const ANSI_COLOR_START = [27, 91]; // ESC[
const ANSI_COLOR_END = 109; // m
const ANSI_COLOR_DELIM = 59; // ;
const COLORS = {
    0:  'normal',
    1:  'bold',
    2:  'faint',
    3:  'standout',
    4:  'underline',
    5:  'blink',
    8:  'invisible',
    10: 'normal-bg',
    22: 'normal-weight',
    23: 'no-standout',
    24: 'no-underline',
    25: 'no-blink',
    30: 'black',
    31: 'red',
    32: 'green',
    33: 'yellow',
    34: 'blue',
    35: 'magenta',
    36: 'cyan',
    37: 'white',
    39: 'default',
    40: 'black-bg',
    41: 'red-bg',
    42: 'green-bg',
    43: 'yellow-bg',
    44: 'blue-bg',
    45: 'magenta-bg',
    46: 'cyan-bg',
    47: 'white-bg',
    49: 'default-bg'
};

String.prototype.charCode = function () {
    for (var arr = this.split(''), i = this.length - 1; i >= 0; i--) {
        arr[i] = arr[i].charCodeAt(0);
    }

    return arr;
};

Array.prototype.flatten = function () {
    return this.reduce(function flatten(a, b) {
        if (!(a instanceof Array)) { a = [a]; }

        if (b instanceof Array) {
            b = b.reduce(flatten);
        }

        return a.concat(b);
    }, []);
};

/* A telnet protocol stream.
 * Emits processed data:
 *  'data' -> what the server sent
 *  other  -> telnet protocol command information
 */
function MUDKit (port, host, callback, terminal) {
    var mudkit = this;

    stream.Transform.call(this);

    this.state = {
        mtts: 0,
        mccp: false,
        color: 'normal'
    };

    this.env = {
        terminal: {
            rows: [0, 24],
            cols: [0, 80]
        }
    };

    this.encoding = 'ascii';

    if (terminal) {
        this.windowSize(terminal);
    }

    this.inflate = zlib.createInflate();
    this.inflate.on('data', function (chunk) {
        mudkit.processChunk(chunk);
    });

    port = port || 4242;
    host = host || localhost;
    this.connect(port, host, callback);

    this.mud.on('end', function () {
        // mudkit.unpipe();
        mudkit.connect(port, host, callback);
    });
}
util.inherits(MUDKit, stream.Transform);

MUDKit.prototype._transform = function (chunk, encoding, callback) {
    var err;

    // Decompress the chunk if MCCP is enabled
    if (this.state.mccp) {
        try {
           this.inflate.write(chunk);
        } catch (err) {
           this.processChunk(chunk, encoding);
        }
    } else {
        this.processChunk(chunk, encoding);
    }

    err = this.err;
    this.err = null;

    callback(err);
};

MUDKit.prototype._flush = function (callback) {
    callback();
};

MUDKit.prototype.connect = function (port, host, callback) {
    this.mud = net.createConnection(port, host);

    this.mud.pipe(this);
    this.mud.on('connected', callback);
};

MUDKit.prototype.sendCommand = function (command) {
    var bytes = [IAC];

    if (command instanceof Array) {
        bytes = bytes.concat(command);
    } else {
        bytes.push(command);
    }

    console.log('send: ' + bytes);

    this.mud.write(new Buffer(bytes, this.encoding));
};

MUDKit.prototype.processChunk = function (chunk, encoding) {
    var mudkit = this, i, l, last = 0, remaining = '',
        output = '';

    function sliceChunk () {
        return chunk.slice(last, i).toString(this.encoding);
    }

    function processNewLine () {
        output += '
';
        i++;
    }

    function process255 () {
        output += String.fromCharCode(255);
        console.log('double 255');
        i++;
    }

    function processANSIColor () {
        var color, classes = '';

        i += 2;
        last = i;

        for (; i < l; i++) {
            if (ANSI_COLOR_END === chunk[i]) {
                break;
            }
        }

        color = sliceChunk().split(';');

        for (var j = 0, ll = color.length; j < ll; j++) {
            if (COLORS[color[j]]) {
                classes += COLORS[color[j]] + ' ';
            }
        }

        mudkit.state.color = classes;

        if (!classes) { classes = 'normal'; }

        output += '';
    }

    function processCommand () {
        var cmd, opt;

        function processSB () {
            var sub, term;

            i++;
            sub = chunk[i];

            switch (sub) {
            case TELOPT_TTYPE:
                console.log('SB TTYPE');
                if (TELQUAL_SEND === chunk[i+1]) {
                    i++;

                    switch (mudkit.state.mtts) {
                    case 0:
                        console.log('SB TTYPE IS MUDKIT');
                        term = CLIENT.charCode();
                        mudkit.state.mtts++;
                        break;
                    case 1:
                        console.log('SB TTYPE IS ANSI');
                        term = 'ANSI'.charCode();
                        mudkit.state.mtts++;
                        break;
                    case 2:
                        console.log('SB TTYPE IS MTTS 5');
                        term = 'MTTS 5'.charCode();
                        mudkit.state.mtts++;
                        break;
                    default:
                        console.log('SB TTYPE IS MTTS 5');
                        term = 'MTTS 5'.charCode();
                        mudkit.state.mtts = 0;
                    }

                    mudkit.sendCommand([SB, TELOPT_TTYPE, TELQUAL_IS, term, IAC, SE].flatten());
                }
                break;

            case TELOPT_COMPRESS2:
                console.log('SB COMPRESS2');
                console.log('server now using deflate');
                mudkit.state.mccp = true;
                break;

            default:
                console.log('Unkown SB command: ' + sub);
            }

            for (; i < l; i++) {
                if (IAC === chunk[i] && SE === chunk[i+1]) {
                    i++;
                    break;
                }
            }
        }

        i++;
        cmd = chunk[i];

        switch (cmd) {
        case GA:
            console.log('GA');
            break;

        case SB:
            processSB();
            break;

        case DO:
            opt = chunk[++i];

            switch (opt) {
            case TELOPT_ECHO:
                console.log('DO ECHO - not implemented');
                break;

            case TELOPT_TTYPE:
                console.log('DO TTYPE');
                mudkit.sendCommand([WILL, TELOPT_TTYPE]);
                break;

            case TELOPT_NAWS:
                console.log('DO NAWS');
                mudkit.sendCommand([WILL, TELOPT_NAWS]);
                mudkit.state.naws = true;
                mudkit.windowSize();
                break;

            case TELOPT_MXP:
                console.log('DO MXP');
                mudkit.sendCommand([WONT, TELOPT_MXP]);
                break;

            case TELOPT_NEW_ENVIRON:
                console.log('DO NEW_ENVIRON');
                mudkit.sendCommand([WONT, TELOPT_NEW_ENVIRON]);
                break;

            default:
                console.log('Unkown DO telopt: ' + opt);
            }
            break;

        case DONT:
            opt = chunk[++i];

            switch (opt) {
            case TELOPT_ECHO:
                console.log('DONT ECHO - not implemented');
                break;

            case TELOPT_TTYPE:
                console.log('DONT TTYPE');
                mudkit.state.mtts = 0;
                break;

            default:
                console.log('Unkown DONT telopt: ' + opt);
            }
            break;

        case WILL:
            opt = chunk[++i];

            switch (opt) {
            case TELOPT_ECHO:
                console.log('WILL ECHO');
                mudkit.sendCommand([DO,TELOPT_ECHO]);
                break;

            case TELOPT_COMPRESS2:
                console.log('WILL COMPRESS2');
                mudkit.sendCommand([DO, TELOPT_COMPRESS2]);
                break;
            case TELOPT_MSSP:
                console.log('WILL MSSP');
                mudkit.sendCommand([DONT, TELOPT_MSSP]);
                break;
            case TELOPT_ZMP:
                console.log('WILL ZMP');
                mudkit.sendCommand([DONT, TELOPT_ZMP]);
                break;
            case TELOPT_GCMP:
                console.log('WILL GCMP');
                mudkit.sendCommand([DONT, TELOPT_GCMP]);
                break;
            default:
                console.log('Unkown WILL telopt: ' + opt);
            }
            break;

        case WONT:
            opt = chunk[++i];

            switch (opt) {
            case TELOPT_ECHO:
                console.log('WONT ECHO');
                mudkit.sendCommand([DONT,TELOPT_ECHO]);
                break;

            default:
                console.log('Unkown WONT telopt: ' + opt);
            }
            break;

        default:
            console.log('Unkown command: ' + cmd);
        }
    }

    if (!chunk || !chunk.length) { return; }
    if (!Buffer.isBuffer(chunk)) { chunk = new Buffer(chunk, encoding); }

    for (i = 0, l = chunk.length; i < l; i++) {
        //console.log(i + ': ' + chunk[i] + ' = ' + String.fromCharCode(chunk[i]));
        if (CR === chunk[i] && LF === chunk[i+1]) {
            output += sliceChunk();
            processNewLine();
            last = i + 1;
        } else if (ANSI_COLOR_START[0] === chunk[i] && ANSI_COLOR_START[1] === chunk[i+1]) {
            output += sliceChunk();
            processANSIColor();
            last = i + 1;
        } else if (IAC === chunk[i] && IAC !== chunk[i+1]) {
            output += sliceChunk();
            processCommand();
            last = i + 1;
        } else if (IAC === chunk[i] && IAC === chunk[i+1]) {
            output += sliceChunk();
            process255();
            last = i + 1;
        } /*else if () {
            // handle encoding characters for html
            function htmlEscape(str) {
                return String(str)
                    .replace(/&/g, '&') // 38
                    .replace(/"/g, '"') // 34
                    .replace(/'/g, ''') // 39
                    .replace(/</g, '<') // 60
                    .replace(/>/g, '>'); // 62
            }
        }*/
    }

    if (i > last) {
        remaining = sliceChunk();

        if (1 === remaining.length && 255 === remaining[0]) {
            // What is this?
            remaining = '';
        }
    }

    output += remaining + '';

    this.push(output);
};

MUDKit.prototype.windowSize = function (size) {
    const MAX = 65535;
    var cols, rows;

    function convert(num) {
        if (255 === num) { num = [255, 255]; }
        return num;
    }

    function first(num) {
        return convert(Math.floor(num / 256));
    }

    function second(num) {
        return convert(num % 256);
    }

    if (size) {
        if (size.cols > MAX) { size.cols = MAX; }
        if (size.rows > MAX) { size.rows = MAX; }

        cols = [first(size.cols), second(size.cols)].flatten();
        rows = [first(size.rows), second(size.rows)].flatten();

        this.env.terminal = {
            cols: cols,
            rows: rows
        };
    }

    if (this.state.naws) {
        this.sendCommand([SB, TELOPT_NAWS, this.env.terminal.cols, this.env.terminal.rows, IAC, SE].flatten());
    }
};

module.exports = MUDKit;
<|endoftext|>


<commit_before><commit_msg>creates solver class and solve method structure<commit_after>function sudokuSolver(){
  this.solve = function(puzzle){
  console.log(puzzle)

  };

};

var myPuzzle = new sudokuSolver();
myPuzzle.solve('000000001')
<|endoftext|>


<commit_before>var tag_passer = function(strs) {
  return function findMatches(q, cb) {
    cb(strs);
  };
};

var make_typeahead_tag = function(tag_id, hints){
  $(tag_id + ' .typeahead').typeahead({
      hint: false,
      minLength: 0,
      highlight: false,
    }, {
      limit: 20,
      source: tag_passer(hints),
      display: function (item) { return item.val; },
      templates: {
          empty: "
No results found.
",
          suggestion: function(item) {
            return "
" + item.source + ": " + item.val + "
";
          },
          footer: function(query) {
            return "
Analyzátory nalezené hodnoty.
"
          },
      },
  });
}

var make_multi_searchable_typeahead_tag  = function(){
  all_tags = [{
    hint: true,
    minLength: 0,
    highlight: true,
  }];

  tag_id = arguments[0];
  for (var i = 1; i < arguments.length; i++) {
    dataset = {
      source: arguments[i].data,
      templates: {
        header: '
' arguments[i].name + '
'
      }
    };

    all_tags.push(dataset);
  }

  $(tag_id + ' .typeahead').typeahead.apply(all_tags);
}

var destroy_typyahead_tag = function(tag_id){
  $(tag_id + ' .typeahead').typeahead("destroy");
}<commit_msg>#11: Fixed bugs in make_multi_searchable_typeahead_tag().<commit_after>var tag_passer = function(strs) {
  return function findMatches(q, cb) {
    cb(strs);
  };
};

var make_typeahead_tag = function(tag_id, hints){
  $(tag_id + ' .typeahead').typeahead({
      hint: false,
      minLength: 0,
      highlight: false,
    }, {
      limit: 20,
      source: tag_passer(hints),
      display: function (item) { return item.val; },
      templates: {
          empty: "
No results found.
",
          suggestion: function(item) {
            return "
" + item.source + ": " + item.val + "
";
          },
          footer: function(query) {
            return "
Analyzátory nalezené hodnoty.
"
          },
      },
  });
}

var make_multi_searchable_typeahead_tag  = function(){
  // start with basic typeahead settings
  all_tags = new Array({
    hint: true,
    minLength: 0,
    highlight: true,
  });

  // first argument is the ID of the tag which will be converted to typeahead
  tag_id = arguments[0];

  // map arguments to `all_tags` array
  for (var i = 1; i < arguments.length; i++) {
    dataset = {
      limit: 20,
      source: tag_passer(arguments[i].data),
      templates: {
        header: '
' + arguments[i].name + '
',
      }
    }

    all_tags.push(dataset);
  }

  // convert the tag to typeahead with multiple searchable data sources
  typeahead_tag = $(tag_id + ' .typeahead');
  typeahead_tag.typeahead.apply(typeahead_tag, all_tags);
}

var destroy_typyahead_tag = function(tag_id){
  $(tag_id + ' .typeahead').typeahead("destroy");
}<|endoftext|>


<commit_before>/*
 * Copyright 2012 Sakai Foundation (SF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var request = require('request');

var Redis = require('oae-util/lib/redis');
var TelemetryAPI = require('oae-telemetry');

var log = require('oae-logger').logger('telemetry-circonus');

var redisClient = null;

/**
 * Will push data into redis and publish histogram data to circonus via HTTPTrap
 */
module.exports.init = function() {
    if (redisClient === null) {
        redisClient = Redis.createClient();
    }
    setInterval(pushCountsToRedis, Config.telemetry.circonus.redisInterval);
    setInterval(pushHistogramsToCirconus, Config.telemetry.circonus.circonusInterval);
};

/**
 * Iterates over the counters and sends each key to redis.
 * Run once to start the interval.
 */
var pushCountsToRedis = function() {
    var counters = TelemetryAPI.getTelemetryData().counts;
    var modules = Object.keys(counters);
    var redisTodo = 0;
    var totalItems = 0;
    for (var i = 0; i < modules.length; i++) {
        var module = modules[i];

        var keys = Object.keys(counters[module]);
        for (var j = 0; j < keys.length;j++) {
            var key = keys[j];

            if (counters[module][key] > 0) {
                totalItems++;
                redisTodo++;
                redisClient.hincrby('telemetry', module + '.' + key, counters[module][key], function(err, res) {
                    if (err) {
                        log().error('Problem pushing telemetry data into redis.');
                    }
                });

                counters[module][key] = 0;
            }
        }
    }
};

var pushHistogramsToCirconus = function() {
    // Get the histogram data.
    var histograms = TelemetryAPI.getTelemetryData().histograms;

    // Format it so it's suitable for circonus consumption.
    // Circonus expects histogram data to be of the form:
    // { "_value": [12, 2, 44, 1], "_type": "I"}
    var circonusData = {};
    var histogramsToSend = 0;
    var modules = Object.keys(histograms);
    for (var i = 0; i < modules.length; i++) {
        var module = modules[i];
        circonusData[module] = {}

        var keys = Object.keys(histograms[module]);
        for (var j = 0; j < keys.length;j++) {
            var key = keys[j];
            if (histograms[module][key].length > 0) {
                histogramsToSend++;
                circonusData[module][key] = {
                    "_value": histograms[module][key],
                    "_type": "I"
                };
            }
        }
    }

    if (histogramsToSend !== 0) {
        // If we have something to send, PUT it to circonus.
        request({
            'method': 'PUT',
            'uri': Config.telemetry.circonus.url,
            'body': JSON.stringify(circonusData)
        }, function(err, response, body) {
            if (err) {
                log().warn("Couldn't publish telemetry data to circonus.");
            } else if (response.statusCode !== 200) {
                log().warn("Couldn't publish telemetry data to circonus: " + body);
            } else {
                // We published our histograms, reset them.
                // Warning: Because of the latency between publishing,
                // we might delete a couple of append()'s that we didn't publish
                var modules = Object.keys(histograms);
                for (var i = 0; i < modules.length; i++) {
                    var module = modules[i];

                    var keys = Object.keys(histograms[module]);
                    for (var j = 0; j < keys.length;j++) {
                        var key = keys[j];
                        histograms[module][key] = [];
                    }
                }
                log().info('Sent %d histograms to circonus', histogramsToSend);
            }
        });
    }
};<commit_msg>Added missing Config in circonus telemetry publisher.<commit_after>/*
 * Copyright 2012 Sakai Foundation (SF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var request = require('request');

var Redis = require('oae-util/lib/redis');
var TelemetryAPI = require('oae-telemetry');

var log = require('oae-logger').logger('telemetry-circonus');
var Config = require('oae-config').config;

var redisClient = null;

/**
 * Will push data into redis and publish histogram data to circonus via HTTPTrap
 */
module.exports.init = function() {
    if (redisClient === null) {
        redisClient = Redis.createClient();
    }
    setInterval(pushCountsToRedis, Config.telemetry.circonus.redisInterval);
    setInterval(pushHistogramsToCirconus, Config.telemetry.circonus.circonusInterval);
};

/**
 * Iterates over the counters and sends each key to redis.
 * Run once to start the interval.
 */
var pushCountsToRedis = function() {
    var counters = TelemetryAPI.getTelemetryData().counts;
    var modules = Object.keys(counters);
    var redisTodo = 0;
    var totalItems = 0;
    for (var i = 0; i < modules.length; i++) {
        var module = modules[i];

        var keys = Object.keys(counters[module]);
        for (var j = 0; j < keys.length;j++) {
            var key = keys[j];

            if (counters[module][key] > 0) {
                totalItems++;
                redisTodo++;
                redisClient.hincrby('telemetry', module + '.' + key, counters[module][key], function(err, res) {
                    if (err) {
                        log().error('Problem pushing telemetry data into redis.');
                    }
                });

                counters[module][key] = 0;
            }
        }
    }
};

var pushHistogramsToCirconus = function() {
    // Get the histogram data.
    var histograms = TelemetryAPI.getTelemetryData().histograms;

    // Format it so it's suitable for circonus consumption.
    // Circonus expects histogram data to be of the form:
    // { "_value": [12, 2, 44, 1], "_type": "I"}
    var circonusData = {};
    var histogramsToSend = 0;
    var modules = Object.keys(histograms);
    for (var i = 0; i < modules.length; i++) {
        var module = modules[i];
        circonusData[module] = {}

        var keys = Object.keys(histograms[module]);
        for (var j = 0; j < keys.length;j++) {
            var key = keys[j];
            if (histograms[module][key].length > 0) {
                histogramsToSend++;
                circonusData[module][key] = {
                    "_value": histograms[module][key],
                    "_type": "I"
                };
            }
        }
    }

    if (histogramsToSend !== 0) {
        // If we have something to send, PUT it to circonus.
        request({
            'method': 'PUT',
            'uri': Config.telemetry.circonus.url,
            'body': JSON.stringify(circonusData)
        }, function(err, response, body) {
            if (err) {
                log().warn("Couldn't publish telemetry data to circonus.");
            } else if (response.statusCode !== 200) {
                log().warn("Couldn't publish telemetry data to circonus: " + body);
            } else {
                // We published our histograms, reset them.
                // Warning: Because of the latency between publishing,
                // we might delete a couple of append()'s that we didn't publish
                var modules = Object.keys(histograms);
                for (var i = 0; i < modules.length; i++) {
                    var module = modules[i];

                    var keys = Object.keys(histograms[module]);
                    for (var j = 0; j < keys.length;j++) {
                        var key = keys[j];
                        histograms[module][key] = [];
                    }
                }
                log().info('Sent %d histograms to circonus', histogramsToSend);
            }
        });
    }
};<|endoftext|>


<commit_before>'use strict';

module.exports = function(name, description, defaultValue, init) {
  let usage = [];

  const assignShort = (name, options, short) => {
    if (options.find(flagName => flagName.usage[0] === short)) {
      short = name.charAt(0).toUpperCase();
    }
    return [short, name];
  };

  // If name is an array, pick the values
  // Otherwise just use the whole thing
  switch (name.constructor) {
    case String:
      usage = assignShort(name, this.details.options, name.charAt(0));
      break;
    case Array:
      usage = usage.concat(name);
      break;
    default:
      throw new Error('Invalid name for option');
  }

  // Throw error if short option is too long
  if (usage.length > 0 && usage[0].length > 1) {
    throw new Error('Short version of option is longer than 1 char');
  }

  const optionDetails = {
    defaultValue,
    usage,
    description
  };

  let defaultIsWrong;

  switch (defaultValue) {
    case false:
      defaultIsWrong = true;
      break;
    case null:
      defaultIsWrong = true;
      break;
    case undefined:
      defaultIsWrong = true;
      break;
    default:
      defaultIsWrong = false;
  }

  // Set initializer depending on type of default value
  if (!defaultIsWrong) {
    const initFunction = typeof init === 'function';
    optionDetails.init = initFunction ? init : this.handleType(defaultValue)[1];
  }

  // Register option to global scope
  this.details.options.push(optionDetails);

  // Allow chaining of .option()
  return this;
};
<commit_msg>Allow setting option init without default value (#114)<commit_after>'use strict';

module.exports = function(name, description, defaultValue, init) {
  let usage = [];

  const assignShort = (name, options, short) => {
    if (options.find(flagName => flagName.usage[0] === short)) {
      short = name.charAt(0).toUpperCase();
    }
    return [short, name];
  };

  // If name is an array, pick the values
  // Otherwise just use the whole thing
  switch (name.constructor) {
    case String:
      usage = assignShort(name, this.details.options, name.charAt(0));
      break;
    case Array:
      usage = usage.concat(name);
      break;
    default:
      throw new Error('Invalid name for option');
  }

  // Throw error if short option is too long
  if (usage.length > 0 && usage[0].length > 1) {
    throw new Error('Short version of option is longer than 1 char');
  }

  const optionDetails = {
    defaultValue,
    usage,
    description
  };

  let defaultIsWrong;

  switch (defaultValue) {
    case false:
      defaultIsWrong = true;
      break;
    case null:
      defaultIsWrong = true;
      break;
    case undefined:
      defaultIsWrong = true;
      break;
    default:
      defaultIsWrong = false;
  }

  if (typeof init === 'function') {
    optionDetails.init = init;
  } else if (!defaultIsWrong) {
    // Set initializer depending on type of default value
    optionDetails.init = this.handleType(defaultValue)[1];
  }

  // Register option to global scope
  this.details.options.push(optionDetails);

  // Allow chaining of .option()
  return this;
};
<|endoftext|>