<commit_before>/*
   Chatbothub
*/
const fs = require('fs');
const readChunk = require('read-chunk');
const fileType = require('file-type');
const mime = require('mime-types')
const express = require('express');
const request = require('request');
const router = express.Router();
const winston = require('winston');

const FacebookAdapter = require('./adapter/FacebookAdapter');
const LineAdapter = require('./adapter/LineAdapter');

const SessionPool = require('./SessionPool');
const Const = require('../constants');

class AllBot {

    constructor(configuration,logger) {

        if(logger){
            this.logger = logger;
        }else{
            this.logger = new (winston.Logger)({
                level: configuration.loglevel,
                transports: [
                    new (winston.transports.Console)()
                ]
            });
        }

        this.configuration = configuration;
        this.listeners = [];
        this.adapters = {}

        this.configuration.logger = this.logger;

        const path = __dirname + "/adapter";

        // Autoload adapters
        fs.readdir(path, (err, items) => {

            for (var i=0; i<items.length; i++) {

                var fileName = items[i];
                var fullPath = path + '/' + items[i];

                const adapterName = require(fullPath);
                const adapterInstance = new adapterName(this.configuration);
                const adapterServiceName = adapterInstance.provider;

                if(adapterServiceName && adapterServiceName !== ''){
                    this.adapters[adapterServiceName] = adapterInstance;
                    this.logger.debug('loadad ' + adapterServiceName + ' apapter.');

                    if(adapterServiceName == Const.skype){

                        // Skype adapter needs initialize botbuilder framework.
                        const skypeListers = configuration.service.filter((service) => {
                            return service.provider == Const.skype;
                        });

                        adapterInstance.initializeBotbuilders(skypeListers);

                    } else if(adapterServiceName == Const.telegram){

                        // telegram has to register webhook dynamically
                        const telegramServices = configuration.service.filter((service) => {
                            return service.provider == Const.telegram;
                        });

                        telegramServices.forEach((service) => {

                            if(service.isTest) return;

                            adapterInstance.registerWebHook(
                                service,
                                configuration.baseURL +
                                configuration.endpointURL +
                                configuration.webhookReceiverURL +
                                "/" + service.identifier
                            );

                        });

                    } else if(adapterServiceName == Const.kik){

                        // kik has to register webhook dynamically too...
                        const kikServices = configuration.service.filter((service) => {
                            return service.provider == Const.kik;
                        });

                        kikServices.forEach((service) => {

                            if(service.isTest) return;

                            adapterInstance.registerWebHook(
                                service,
                                configuration.baseURL +
                                configuration.endpointURL +
                                configuration.webhookReceiverURL +
                                "/" + service.identifier
                            );

                        });

                    } else if(adapterServiceName == Const.viber){

                        // kik has to register webhook dynamically too...
                        const viberServices = configuration.service.filter((service) => {
                            return service.provider == Const.viber;
                        });

                        viberServices.forEach((service) => {

                            if(service.isTest) return;

                            adapterInstance.registerWebHook(
                                service,
                                configuration.baseURL +
                                configuration.endpointURL +
                                configuration.webhookReceiverURL +
                                "/" + service.identifier
                            );

                        });

                    } else {

                    }
                }

            }
        });

        router.get(this.configuration.webhookReceiverURL, (req,res) => {

            this.handleGetRequest(req,res);
        });

        router.post(this.configuration.webhookReceiverURL, (req,res) => {

            this.handlePostRequest(req,res);

        });

        router.get(this.configuration.webhookReceiverURL + '/:serviceIdentifier', (req,res) => {

            const serviceIdentifier = req.params.serviceIdentifier;

            const service = configuration.service.find((service) => {
                return service.identifier == serviceIdentifier;
            });

            this.handleGetRequest(req,res,service);

        });

        router.post(this.configuration.webhookReceiverURL + '/:serviceIdentifier', (req,res) => {
            const serviceIdentifier = req.params.serviceIdentifier;

            const service = configuration.service.find((service) => {
                return service.identifier == serviceIdentifier;
            });

            this.handlePostRequest(req,res,service);
        });

        // file sender URL
        router.get(this.configuration.fileSenderURL + '/:fileId', (req,res) => {

            const fileId = req.params.fileId;
            const filePath = this.configuration.downloadPath + "/" + fileId;
            const buffer = readChunk.sync(filePath, 0, 4100);

            let type = mime.lookup(fileId);

            if(!type){
                const typeObj = fileType(buffer);
                if(typeObj)
                    type = type.mime;

            }

            if(type){

                var options = {
                    root: this.configuration.downloadPath,
                    headers: {
                        'x-timestamp': Date.now(),
                        'x-sent': true
                    }
                };

                res.contentType(type);
                res.sendFile(fileId, options);

            } else {

                res.download(this.configuration.downloadPath + "/" + fileId);

            }

        });

        this.router = router;

    }

    onMessage(listener){

        this.listeners.push(listener);

    }

    clearListeners(){
        this.listeners = [];
    }

    handleGetRequest(req,res,service){

        //console.log('GET Request',JSON.stringify(req.query, null, 3));

        if(service)
            this.processToAdaptersWithService(req,res,service);
        else
            this.processToAdapters(req,res);

    }

    handlePostRequest(req,res,service){

        //console.log('POST Request',JSON.stringify(req.body, null, 3));

        if(service)
            this.processToAdaptersWithService(req,res,service);
        else
            this.processToAdapters(req,res);

    }

    processToAdaptersWithService(req,res,service){

        let processed = false;

        const adapter = this.adapters[service.provider];

        if(!adapter)
            return false;

        if(req.method == "POST"){

            processed |= adapter.processPost(req,res,service,(message,responseFromProvider) => {

                // save to pool
                var sesssionKey = SessionPool.set(
                    service.identifier,
                    service.provider,
                    message,
                    responseFromProvider);

                this.listeners.forEach((listener) => {

                    if(typeof listener === 'function'){
                        setTimeout(() => {
                            listener(sesssionKey,message);
                        },10);
                    }

                });

            });

        } else if(req.method == "GET"){

            processed |= adapter.processGet(req,res,service);

        }

        return processed;

    }

    processToAdapters(req,res){

        let processed = false;

        this.configuration.service.forEach((service) => {

            if(this.adapters[service.provider]){

                processed |= this.processToAdaptersWithService(req,res,service);

            }

        });

        if(!processed){
            this.logger.debug('Received unhandled request');
            res.send('');
        }

    }

    replyText(sessionId,text,hook){

        const sessionObj = SessionPool.get(sessionId);

        const service = this.configuration.service.find((service) => {
            return service.identifier == sessionObj.identifier;
        });

        if(this.adapters[sessionObj.provider]){
            return this.adapters[sessionObj.provider].replyText(
                text,
                sessionObj.originalReposonse,
                service,
                hook
            );
        }

    }

    sendText(userIdentifier,text,cb){

        const userIdentifierChunks = userIdentifier.split(':');
        const provider = userIdentifierChunks[0];
        const serviceIdentifier = userIdentifierChunks[1];

        const service = this.configuration.service.find((service) => {
            return service.identifier == serviceIdentifier;
        });

        if(this.adapters[provider]){

            return this.adapters[provider].sendText(
                text,
                userIdentifier,
                service
            );

        }

    }

    sendFile(userIdentifier,fileId){

        const userIdentifierChunks = userIdentifier.split(':');
        const provider = userIdentifierChunks[0];
        const serviceIdentifier = userIdentifierChunks[1];

        const service = this.configuration.service.find((service) => {
            return service.identifier == serviceIdentifier;
        });

        if(this.adapters[provider]){
            return this.adapters[provider].sendFile(
                fileId,
                userIdentifier,
                service
            );
        }

    }

    downloadFile(sessionId,path){

        const sessionObj = SessionPool.get(sessionId);

        const service = this.configuration.service.find((service) => {
            return service.identifier == sessionObj.identifier;
        });

        if(this.adapters[sessionObj.provider]){
            return this.adapters[sessionObj.provider].downloadFile(
                sessionObj.originalReposonse,
                path,
                service
            );
        }

    }

    sendLocation(userIdentifier,coordinate){

        const userIdentifierChunks = userIdentifier.split(':');
        const provider = userIdentifierChunks[0];
        const serviceIdentifier = userIdentifierChunks[1];

        const service = this.configuration.service.find((service) => {
            return service.identifier == serviceIdentifier;
        });

        if(this.adapters[provider]){

            return this.adapters[provider].sendLocation(
                userIdentifier,
                coordinate,
                service
            );

        }

    }

}

module["exports"] = AllBot;<commit_msg>Fix to work without config<commit_after>/*
   Chatbothub
*/
const fs = require('fs');
const readChunk = require('read-chunk');
const fileType = require('file-type');
const mime = require('mime-types')
const express = require('express');
const request = require('request');
const router = express.Router();
const winston = require('winston');

const FacebookAdapter = require('./adapter/FacebookAdapter');
const LineAdapter = require('./adapter/LineAdapter');

const SessionPool = require('./SessionPool');
const Const = require('../constants');

class AllBot {

    constructor(configuration,logger) {

        if(logger){
            this.logger = logger;
        }else{
            this.logger = new (winston.Logger)({
                level: configuration.loglevel,
                transports: [
                    new (winston.transports.Console)()
                ]
            });
        }

        this.configuration = configuration;
        this.listeners = [];
        this.adapters = {}

        this.configuration.logger = this.logger;

        const path = __dirname + "/adapter";

        // Autoload adapters
        fs.readdir(path, (err, items) => {

            for (var i=0; i<items.length; i++) {

                var fileName = items[i];
                var fullPath = path + '/' + items[i];

                const adapterName = require(fullPath);
                const adapterInstance = new adapterName(this.configuration);
                const adapterServiceName = adapterInstance.provider;

                if(adapterServiceName && adapterServiceName !== ''){
                    this.adapters[adapterServiceName] = adapterInstance;
                    this.logger.debug('loadad ' + adapterServiceName + ' apapter.');

                    if(adapterServiceName == Const.skype){

                        // Skype adapter needs initialize botbuilder framework.
                        const skypeListers = configuration.service.filter((service) => {
                            return service.provider == Const.skype;
                        });

                        if(skypeListers && skypeListers.length > 0)
                            adapterInstance.initializeBotbuilders(skypeListers);

                    } else if(adapterServiceName == Const.telegram){

                        // telegram has to register webhook dynamically
                        const telegramServices = configuration.service.filter((service) => {
                            return service.provider == Const.telegram;
                        });

                        telegramServices.forEach((service) => {

                            if(service.isTest) return;

                            adapterInstance.registerWebHook(
                                service,
                                configuration.baseURL +
                                configuration.endpointURL +
                                configuration.webhookReceiverURL +
                                "/" + service.identifier
                            );

                        });

                    } else if(adapterServiceName == Const.kik){

                        // kik has to register webhook dynamically too...
                        const kikServices = configuration.service.filter((service) => {
                            return service.provider == Const.kik;
                        });

                        kikServices.forEach((service) => {

                            if(service.isTest) return;

                            adapterInstance.registerWebHook(
                                service,
                                configuration.baseURL +
                                configuration.endpointURL +
                                configuration.webhookReceiverURL +
                                "/" + service.identifier
                            );

                        });

                    } else if(adapterServiceName == Const.viber){

                        // kik has to register webhook dynamically too...
                        const viberServices = configuration.service.filter((service) => {
                            return service.provider == Const.viber;
                        });

                        viberServices.forEach((service) => {

                            if(service.isTest) return;

                            adapterInstance.registerWebHook(
                                service,
                                configuration.baseURL +
                                configuration.endpointURL +
                                configuration.webhookReceiverURL +
                                "/" + service.identifier
                            );

                        });

                    } else {

                    }
                }

            }
        });

        router.get(this.configuration.webhookReceiverURL, (req,res) => {

            this.handleGetRequest(req,res);
        });

        router.post(this.configuration.webhookReceiverURL, (req,res) => {

            this.handlePostRequest(req,res);

        });

        router.get(this.configuration.webhookReceiverURL + '/:serviceIdentifier', (req,res) => {

            const serviceIdentifier = req.params.serviceIdentifier;

            const service = configuration.service.find((service) => {
                return service.identifier == serviceIdentifier;
            });

            this.handleGetRequest(req,res,service);

        });

        router.post(this.configuration.webhookReceiverURL + '/:serviceIdentifier', (req,res) => {
            const serviceIdentifier = req.params.serviceIdentifier;

            const service = configuration.service.find((service) => {
                return service.identifier == serviceIdentifier;
            });

            this.handlePostRequest(req,res,service);
        });

        // file sender URL
        router.get(this.configuration.fileSenderURL + '/:fileId', (req,res) => {

            const fileId = req.params.fileId;
            const filePath = this.configuration.downloadPath + "/" + fileId;
            const buffer = readChunk.sync(filePath, 0, 4100);

            let type = mime.lookup(fileId);

            if(!type){
                const typeObj = fileType(buffer);
                if(typeObj)
                    type = type.mime;

            }

            if(type){

                var options = {
                    root: this.configuration.downloadPath,
                    headers: {
                        'x-timestamp': Date.now(),
                        'x-sent': true
                    }
                };

                res.contentType(type);
                res.sendFile(fileId, options);

            } else {

                res.download(this.configuration.downloadPath + "/" + fileId);

            }

        });

        this.router = router;

    }

    onMessage(listener){

        this.listeners.push(listener);

    }

    clearListeners(){
        this.listeners = [];
    }

    handleGetRequest(req,res,service){

        //console.log('GET Request',JSON.stringify(req.query, null, 3));

        if(service)
            this.processToAdaptersWithService(req,res,service);
        else
            this.processToAdapters(req,res);

    }

    handlePostRequest(req,res,service){

        //console.log('POST Request',JSON.stringify(req.body, null, 3));

        if(service)
            this.processToAdaptersWithService(req,res,service);
        else
            this.processToAdapters(req,res);

    }

    processToAdaptersWithService(req,res,service){

        let processed = false;

        const adapter = this.adapters[service.provider];

        if(!adapter)
            return false;

        if(req.method == "POST"){

            processed |= adapter.processPost(req,res,service,(message,responseFromProvider) => {

                // save to pool
                var sesssionKey = SessionPool.set(
                    service.identifier,
                    service.provider,
                    message,
                    responseFromProvider);

                this.listeners.forEach((listener) => {

                    if(typeof listener === 'function'){
                        setTimeout(() => {
                            listener(sesssionKey,message);
                        },10);
                    }

                });

            });

        } else if(req.method == "GET"){

            processed |= adapter.processGet(req,res,service);

        }

        return processed;

    }

    processToAdapters(req,res){

        let processed = false;

        this.configuration.service.forEach((service) => {

            if(this.adapters[service.provider]){

                processed |= this.processToAdaptersWithService(req,res,service);

            }

        });

        if(!processed){
            this.logger.debug('Received unhandled request');
            res.send('');
        }

    }

    replyText(sessionId,text,hook){

        const sessionObj = SessionPool.get(sessionId);

        const service = this.configuration.service.find((service) => {
            return service.identifier == sessionObj.identifier;
        });

        if(this.adapters[sessionObj.provider]){
            return this.adapters[sessionObj.provider].replyText(
                text,
                sessionObj.originalReposonse,
                service,
                hook
            );
        }

    }

    sendText(userIdentifier,text,cb){

        const userIdentifierChunks = userIdentifier.split(':');
        const provider = userIdentifierChunks[0];
        const serviceIdentifier = userIdentifierChunks[1];

        const service = this.configuration.service.find((service) => {
            return service.identifier == serviceIdentifier;
        });

        if(this.adapters[provider]){

            return this.adapters[provider].sendText(
                text,
                userIdentifier,
                service
            );

        }

    }

    sendFile(userIdentifier,fileId){

        const userIdentifierChunks = userIdentifier.split(':');
        const provider = userIdentifierChunks[0];
        const serviceIdentifier = userIdentifierChunks[1];

        const service = this.configuration.service.find((service) => {
            return service.identifier == serviceIdentifier;
        });

        if(this.adapters[provider]){
            return this.adapters[provider].sendFile(
                fileId,
                userIdentifier,
                service
            );
        }

    }

    downloadFile(sessionId,path){

        const sessionObj = SessionPool.get(sessionId);

        const service = this.configuration.service.find((service) => {
            return service.identifier == sessionObj.identifier;
        });

        if(this.adapters[sessionObj.provider]){
            return this.adapters[sessionObj.provider].downloadFile(
                sessionObj.originalReposonse,
                path,
                service
            );
        }

    }

    sendLocation(userIdentifier,coordinate){

        const userIdentifierChunks = userIdentifier.split(':');
        const provider = userIdentifierChunks[0];
        const serviceIdentifier = userIdentifierChunks[1];

        const service = this.configuration.service.find((service) => {
            return service.identifier == serviceIdentifier;
        });

        if(this.adapters[provider]){

            return this.adapters[provider].sendLocation(
                userIdentifier,
                coordinate,
                service
            );

        }

    }

}

module["exports"] = AllBot;<|endoftext|>


<commit_before>var cproc = require('child_process');
var spawn = cproc.spawnSync;

// Load credentials to remotly connect to PBS server
var pbs_config = require("./config/pbsserver.json");

// Parse the command and return stdout of the process depending on the method
// TODO: treat errors
function spawnProcess(remote_cmd){
    remote_cmd = pbs_config.binaries_dir + remote_cmd;
    // Remote pbs server
    if (pbs_config.method == "ssh"){
        var spawnCommand = [pbs_config.username + "@" + pbs_config.serverName,"-i",pbs_config.secretAccessKey].concat(remote_cmd.split(" "));
        return spawn(pbs_config.ssh_exec, spawnCommand, { encoding : 'utf8' });
    }
    // Local server on the same machine as the node process
    if (pbs_config.method == "local"){
        return spawn(pbs_config.local_shell, remote_cmd.split(" "), { encoding : 'utf8' });
    }
}

//Takes an array to convert to JSON tree for queues and server properties
function jsonifyQmgr(output){
    var results=[];
    // JSON output will be indexed by queues and server
    results['queue']=[];
    results['queues']=[];
    results['server']={};
    //Loop on properties
    for (var i = 0; i < output.length; i++) {
        if (output[i].indexOf('=')!== -1){
            // Split key and value to 0 and 1
            var data = output[i].split('=');
            // Split at each space to create a node in JSON
            var keys = data[0].trim().split(' ');
            var value = data[1].trim();
            //TODO: do this more effentiely
            switch (keys[1].trim()){
                case 'server':
                    results['server'][keys[2].trim()] = value;
                    break;
                case 'queue':
                    // Order array under the queue name to easily store properties
                    results['queue'][keys[2].trim()] = results['queue'][keys[2].trim()] || {}; // initializes array if it is undefined
                    results['queue'][keys[2].trim()][keys[3].trim()] = value;
                    break;
            }
        }
    }
    // Loop on the sub-array 'queue' to reorganise it more JSON-like
    for (var x in results['queue']){
        // Add the name of the queue
        results['queue'][x]['name'] = x;
        results['queues'].push(results['queue'][x]);
    }
    // Clear the sub-array
    delete results['queue'];

    return results;
}

function jsonifyQnodes(output){
    var results={};
    // Store node name
    results["name"] = output[0];
    // Look for properties
    for (var i = 1; i < output.length; i++) {
        if (output[i].indexOf('=')!== -1){
           // Split key and value to 0 and 1
            var data = output[i].split('=');
            results[data.shift().trim()] = data.toString().trim();

        }
    }
    // Reorganise jobs into an array with jobId & jobProcs
    if (results['jobs']){
        var runningJobs = [];
        var jobData = results['jobs'].trim().split(/[,/]+/);
        // Parse jobs and forget trailing comma
        for (var j = 0; j < jobData.length-1; j+=2) {
            var newJob = {
                jobId       :   jobData[j+1],
                jobProcs    :   jobData[j],
            }
            runningJobs.push(newJob);
        }
        results['jobs'] = runningJobs;
    }
    // Reorganise status
    if (results['status']){
        var statusData = results['status'].trim().split(/[,]+/);
        for (var k = 0; k < statusData.length; k+=2) {
            // Skip jobs inside status for now : TODO: store those information
            if (statusData[k] == 'jobs'){
                while (statusData[k] != 'state'){
                    k++;
                }
            }
            results[statusData[k]] = statusData[k+1];
        }
        delete results['status'];
    }
    return results;
}

function jsonifyQstat(output){
    var results={};
    var status = {'Q' : 'Queued', 'R' : 'Running', 'C' : 'Completed', 'E' : 'Error'};
    results = {
        "jobId"     :   output[0],
        "name"      :   output[1],
        "user"      :   output[2],
        "time"      :   output[3],
        "status"    :   status[output[4]],
        "queue"     :   output[5],
    };
    return results;
}

// Return the list of nodes
function qnodes_js(nodeName){
    var remote_cmd = "qnodes";
    // Info on a specific node
    if(nodeName != undefined) {
        remote_cmd += " " + nodeName;
    }
    var output = spawnProcess(remote_cmd).stdout;
    //Detect empty values
    output = output.replace(/=,/g,"=null,");
    //Separate each node
    output = output.split('\n\n');
    var nodes = [];
    //Loop on each node, the last one is blank due to \n\n
    for (var i = 0; i < output.length-1; i++) {
        //Split at lign breaks
        output[i]  = output[i].trim().split(/[\n;]+/);
        nodes.push(jsonifyQnodes(output[i]));
    }
    return nodes;
}

// Return list of running jobs
// TODO: implement qstat -f
function qstat_js(jobId){
    var remote_cmd = "qstat";
    // Full information on a job
    if(jobId != undefined) {
        remote_cmd += " -f " + jobId;
    }
    var output = spawnProcess(remote_cmd).stdout;
    output = output.split('\n');
    // First 2 lines are not relevant
    var jobs = [];
    for (var i = 2; i < output.length-1; i++) {
        output[i]  = output[i].trim().split(/[\s]+/);
        jobs.push(jsonifyQstat(output[i]));
    }
    return jobs;
}

// Interface for qdel
// Delete the specified job Id and return the message and the status code
function qdel_js(jobId){
    var remote_cmd = "qdel";
    if(jobId == undefined) {
        return ['Please specify the jobId',1];
    }else{
        // Default print everything
        remote_cmd += " " + jobId;
    }
    var output = spawnProcess(remote_cmd);
    // Transmit the error if any
    if (output.stderr){
        return [output.stderr,output.status];
    }
    // Job deleted returns 0 exit code
    if (output.status == 0){
        return ['Job ' + jobId + ' successfully deleted',0];
    }
}

// Interface for qmgr
// For now only display server info
function qmgr_js(qmgrCmd){
    var remote_cmd = "qmgr -c ";
    if(qmgrCmd != undefined) {
        remote_cmd += qmgrCmd;
    }else{
        // Default print everything
        remote_cmd += "'p s'";
    }
    var output = spawnProcess(remote_cmd).stdout;
    output = output.split('\n');
    var results = jsonifyQmgr(output);

    return results;
}

module.exports = {
    qnodes_js   : qnodes_js,
    qstat_js    : qstat_js,
    qmgr_js     : qmgr_js,
    qdel_js     : qdel_js,
};<commit_msg>Added qsub function and handles file upload<commit_after>var cproc = require('child_process');
var spawn = cproc.spawnSync;
var fs = require("fs");

// Parse the command and return stdout of the process depending on the method
// TODO: treat errors
function spawnProcess(spawnCmd, spawnType, pbs_config){
    var spawnExec;
    switch (spawnType){
        case "shell":
            switch (pbs_config.method){
                case "ssh":
                    spawnExec = pbs_config.ssh_exec;
                    spawnCmd = [pbs_config.username + "@" + pbs_config.serverName,"-i",pbs_config.secretAccessKey].concat(spawnCmd.split(" "));
                    break;
                case "local":
                    spawnExec = pbs_config.local_shell;
                    spawnCmd = spawnCmd.split(" ");
                    break;
            }
            break;
        //Copy send the 2 files in an array
        case "copy":
            // Special case if we can use a shared file system
            if (pbs_config.useSharedDir){
                spawnExec = pbs_config.local_copy;
            }else{
                switch (pbs_config.method){
                    // Build the scp command
                    case "ssh":
                        spawnExec = pbs_config.scp_exec;
                        spawnCmd = ["-i",pbs_config.secretAccessKey,spawnCmd[0],pbs_config.username + "@" + pbs_config.serverName+":"+spawnCmd[1]];
                        break;
                    case "local":
                        spawnExec = pbs_config.local_copy;
                        break;
                }
            }
            break;
    }
    return spawn(spawnExec, spawnCmd, { encoding : 'utf8' });
}

function createUID()
{
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

function createJobWorkDir(pbs_config){
    // Get configuration working directory
    var jobWorkingDir = pbs_config.working_dir;
    if (jobWorkingDir.slice(-1) != '/'){jobWorkingDir += '/'}

    // Generate a UID for the working dir
    jobWorkingDir += createUID() + "/";

    //Create workdir
    var mkdirOutput = spawnProcess("[ -d "+jobWorkingDir+" ] || mkdir "+jobWorkingDir,"shell",pbs_config);

    //TODO:handles error
    return jobWorkingDir;
}

// Camelize the name for something UNIX-compatible
function camelize(str) {
    return str.toLowerCase().replace(/[^a-zA-Z0-9]/g,' ').replace(/[\s]+(\w|$)/g, function (_,x) {
        return x.toUpperCase();
    });
}

//Takes an array to convert to JSON tree for queues and server properties
function jsonifyQmgr(output){
    var results=[];
    // JSON output will be indexed by queues and server
    results['queue']=[];
    results['queues']=[];
    results['server']={};
    //Loop on properties
    for (var i = 0; i < output.length; i++) {
        if (output[i].indexOf('=')!== -1){
            // Split key and value to 0 and 1
            var data = output[i].split('=');
            // Split at each space to create a node in JSON
            var keys = data[0].trim().split(' ');
            var value = data[1].trim();
            //TODO: do this more effentiely
            switch (keys[1].trim()){
                case 'server':
                    results['server'][keys[2].trim()] = value;
                    break;
                case 'queue':
                    // Order array under the queue name to easily store properties
                    results['queue'][keys[2].trim()] = results['queue'][keys[2].trim()] || {}; // initializes array if it is undefined
                    results['queue'][keys[2].trim()][keys[3].trim()] = value;
                    break;
            }
        }
    }
    // Loop on the sub-array 'queue' to reorganise it more JSON-like
    for (var x in results['queue']){
        // Add the name of the queue
        results['queue'][x]['name'] = x;
        results['queues'].push(results['queue'][x]);
    }
    // Clear the sub-array
    delete results['queue'];

    return results;
}

function jsonifyQnodes(output){
    var results={};
    // Store node name
    results["name"] = output[0];
    // Look for properties
    for (var i = 1; i < output.length; i++) {
        if (output[i].indexOf('=')!== -1){
           // Split key and value to 0 and 1
            var data = output[i].split('=');
            results[data.shift().trim()] = data.toString().trim();

        }
    }
    // Reorganise jobs into an array with jobId & jobProcs
    if (results['jobs']){
        var runningJobs = [];
        var jobData = results['jobs'].trim().split(/[,/]+/);
        // Parse jobs and forget trailing comma
        for (var j = 0; j < jobData.length-1; j+=2) {
            var newJob = {
                jobId       :   jobData[j+1],
                jobProcs    :   jobData[j],
            }
            runningJobs.push(newJob);
        }
        results['jobs'] = runningJobs;
    }
    // Reorganise status
    if (results['status']){
        var statusData = results['status'].trim().split(/[,]+/);
        for (var k = 0; k < statusData.length; k+=2) {
            // Skip jobs inside status for now : TODO: store those information
            if (statusData[k] == 'jobs'){
                while (statusData[k] != 'state'){
                    k++;
                }
            }
            results[statusData[k]] = statusData[k+1];
        }
        delete results['status'];
    }
    return results;
}

function jsonifyQstat(output){
    var results={};
    var status = {'Q' : 'Queued', 'R' : 'Running', 'C' : 'Completed', 'E' : 'Error'};
    results = {
        "jobId"     :   output[0],
        "name"      :   output[1],
        "user"      :   output[2],
        "time"      :   output[3],
        "status"    :   status[output[4]],
        "queue"     :   output[5],
    };
    return results;
}

// Generate the script to run the job and write it to the specified path
// Job Arguments taken in input : TO COMPLETE
/* jobArgs = {
    shell           :   String      //  '/bin/bash'
    name            :   String      //  'XX'
    ressources      :   String      //  'nodes=X:ppn=X or select=X'
    walltime        :   String      //  'walltime=01:00:00'
    queue           :   String      //  'batch'
    exclusive       :   Boolean     //  '-n'
    mail            :   String      //  'myemail@mydomain.com'
    mailAbort       :   Boolean     //  '-m a'
    mailBegins      :   Boolean     //  '-m b'
    mailTerminates  :   Boolean     //  '-m e'
    commands        :   Array       //  'main commands to run'

}*/
// Return the full path of the SCRIPT
// TODO: Consider piping the commands to qsub instead of writing script
function qscript_js(jobArgs, localPath){
    // General PBS command inside script
    var PBScommand = "#PBS "
    var toWrite = "# Autogenerated script";

    // Camelize the name
    var jobName = camelize(jobArgs.name);

    // Generate the script path
    if (localPath.slice(-1) != '/'){localPath += '/'}
    var scriptFullPath = localPath + jobName;

    // Job Shell
    toWrite += "\n" + PBScommand + "-S " + jobArgs.shell;

    // Job Name
    toWrite += "\n" + PBScommand + "-N " + jobName;

    // Ressources
    toWrite += "\n" + PBScommand + "-l " + jobArgs.ressources;

    // Walltime
    toWrite += "\n" + PBScommand + "-l " + jobArgs.walltime;

    // Queue
    toWrite += "\n" +  PBScommand + "-q " + jobArgs.queue;

    // Job exclusive
    if (jobArgs.exclusive){
        toWrite += "\n" + PBScommand + "-n";
    }

    // Send mail
    if (jobArgs.mail){

    toWrite += "\n" + PBScommand + "-M " + jobArgs.mail;

        // Test when to send a mail
        var mailArgs;
        if(jobArgs.mailAbort){mailArgs = '-m a';}
        if(jobArgs.mailBegins){
          if (!mailArgs){mailArgs = '-m b'}else{mailArgs += 'b';}
        }
        if(jobArgs.mailTerminates){
          if (!mailArgs){mailArgs = '-m e'}else{mailArgs += 'e';}
        }

        if (mailArgs){
            toWrite += "\n" + PBScommand + mailArgs;
        }
    }

    for (var i = 0; i < jobArgs.commands.length; i++){
        toWrite += "\n" + jobArgs.commands[i];
    }
    toWrite += "\n";
    // Write to script
    fs.writeFileSync(scriptFullPath,toWrite)

    return {
            "code"      : 0,
            "message"   : 'Script successfully created',
            "path"      : scriptFullPath
        };
}

// Return the list of nodes
function qnodes_js(pbs_config,nodeName){
    var remote_cmd = pbs_config.binaries_dir + "qnodes2";
    // Info on a specific node
    if(nodeName != undefined) {
        remote_cmd += " " + nodeName;
    }
    var output = spawnProcess(remote_cmd,"shell",pbs_config).stdout;
    //Detect empty values
    output = output.replace(/=,/g,"=null,");
    //Separate each node
    output = output.split('\n\n');
    var nodes = [];
    //Loop on each node, the last one is blank due to \n\n
    for (var i = 0; i < output.length-1; i++) {
        //Split at lign breaks
        output[i]  = output[i].trim().split(/[\n;]+/);
        nodes.push(jsonifyQnodes(output[i]));
    }
    return nodes;
}

// Return list of running jobs
// TODO: implement qstat -f
function qstat_js(pbs_config,jobId){
    var remote_cmd = pbs_config.binaries_dir + "qstat";
    // Full information on a job
    if(jobId != undefined) {
        remote_cmd += " -f " + jobId;
    }
    var output = spawnProcess(remote_cmd,"shell",pbs_config).stdout;
    output = output.split('\n');
    // First 2 lines are not relevant
    var jobs = [];
    for (var i = 2; i < output.length-1; i++) {
        output[i]  = output[i].trim().split(/[\s]+/);
        jobs.push(jsonifyQstat(output[i]));
    }
    return jobs;
}

// Interface for qdel
// Delete the specified job Id and return the message and the status code
function qdel_js(pbs_config,jobId){
    var remote_cmd = pbs_config.binaries_dir + "qdel";
    if(jobId == undefined) {
        // Return an error code of 1
        return { "code" : 1, "message" : 'Please specify the jobId'};
    }else{
        // Default print everything
        remote_cmd += " " + jobId;
    }
    var output = spawnProcess(remote_cmd,"shell",pbs_config);
    // Transmit the error if any
    if (output.stderr){
        return { "code" : output.status, "message" : output.stderr};
    }
    // Job deleted returns 0 exit code
    if (output.status == 0){
        return { "code" : 0, "message" : 'Job ' + jobId + ' successfully deleted'};
    }
}

// Interface for qmgr
// For now only display server info
function qmgr_js(pbs_config,qmgrCmd){
    var remote_cmd = pbs_config.binaries_dir + "qmgr -c ";
    if(qmgrCmd != undefined) {
        remote_cmd += qmgrCmd;
    }else{
        // Default print everything
        remote_cmd += "'p s'";
    }
    var output = spawnProcess(remote_cmd,"shell",pbs_config).stdout;
    output = output.split('\n');
    var results = jsonifyQmgr(output);

    return results;
}

// Interface for qsub
// Submit a script by its absolute path
// Takes as args an array of required files to run with and to send to the server with the script in 0
function qsub_js(pbs_config,qsubArgs){
    var remote_cmd = pbs_config.binaries_dir + "qsub";
    if(qsubArgs.length < 1) {
        return { "code" : 1, "message" : 'Please submit the script to run'};
    }

    // Create a workdir if not defined
    // TODO: - test if accessible
    var jobWorkingDir = createJobWorkDir(pbs_config);

    // Send files by the copy command defined
    for (var i = 0; i < qsubArgs.length; i++){
        var copyCmd = spawnProcess([qsubArgs[i],jobWorkingDir],"copy",pbs_config);
        if (copyCmd.stderr){
            return { "code" : copyCmd.status, "message" : copyCmd.stderr.replace(/\n/g,"")};
        }
    }
    // Add script
    var scriptName = qsubArgs[0].split("/").pop();
    remote_cmd += " " + jobWorkingDir + scriptName;

    // Add directory to submission args
    remote_cmd += " -d " + jobWorkingDir;

    // Submit
    var output = spawnProcess(remote_cmd,"shell",pbs_config);
    // Transmit the error if any
    if (output.stderr){
        return { "code" : output.status, "message" : output.stderr.replace(/\n/g,"")};
    }
    // Job deleted returns 0 exit code and working directory
    if (output.status == 0){
        return {
            "code"      : 0,
            "message"   : 'Job ' + output.stdout.replace(/\n/g,"") + ' submitted',
            "path"      : jobWorkingDir
        };
    }
}

module.exports = {
    qnodes_js           : qnodes_js,
    qstat_js            : qstat_js,
    qmgr_js             : qmgr_js,
    qdel_js             : qdel_js,
    qsub_js             : qsub_js,
    qscript_js          : qscript_js,
};<|endoftext|>


<commit_before>'use strict';

var async = require('async');
var ApiRequest = require("./Request");
var config = require('./config');
var _ = require('lodash');

var Business = require('./Business');

/**
 *
 * Intialise the client
 * @param  {String} accessKey     API key from eversign dashboard
 * @param  {Number} businessId Business ID from eversign dashboard
 * @return {Client}            Client object
 */
function Client(accessKey, businessId){
  var accessKey = accessKey;
  var businessId = businessId;
  var businesses = [];
  var selectedBusiness = undefined;

  /**
   *
   * Retrieves the documents from eversign API
   * @return {Promise} Which resolves to An array of documents
   */
  this.getAllDocuments = function () {
    return getDocuments();
  };

  /**
   *
   * Retrieves all canceled documents from eversign API
   * @return {Promise} Which resolves to An array of documents
   */
  this.getCancelledDocuments = function () {
    return getDocuments("cancelled");
  };

  /**
   *
   * Returns all Documents for the Client which require Actions
   * @return {Promise} Which resolves to An array of documents
   */
  this.getActionRequiredDocuments = function () {
    return getDocuments("my_action_required");
  };

  /**
   *
   * Returns all Documents for the Client which are waiting on responses
   * @return {Promise} Which resolves to An array of documents
   */
  this.getWaitingForOthersDocuments = function () {
    return getDocuments("waiting_for_others");
  };

  /**
   *
   * Returns a list of Documents which are set to be Templates
   * @return {Promise} Which resolves to An array of documents
   */
  this.getTemplates = function () {
    return getDocuments("templates", 'Template');
  };

  /**
   *
   * Returns a list of Documents which are set to be Templates
   * @return {Promise} Which resolves to An array of documents
   */
  this.getArchivedTemplates = function () {
    return getDocuments("templates_archived", 'Template');
  };

  /**
   *
   * Returns a list of Documents which are set to be Templates
   * @return {Promise} Which resolves to An array of documents
   */
  this.getDraftTemplates = function () {
    return getDocuments("template_drafts", 'Template');
  };

  function getDocuments(type, dataType) {
    if(!type) type = "all"
    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
      "type" : type
    };
    var request = new ApiRequest("GET", accessKey, config.DOCUMENT_URL, dataType ? dataType : 'Document', parameters);
    return request.startRequest();
  }

  this.createDocument = function (document) {
    var self = this;
    document = document.toObject();
    if(!document.getSigners() && !document.getSigners().length )
      throw new Error('Document needs at least 1 Signer to be created');

    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
    };

    async.forEachOf(document.getFiles(), function (file, index, callback) {
      if(file.getFilePath() || file.getFileBase64()) {
        self.uploadFile(file)
        .then(function (response) {

          var changedFile = document.files[index];
          changedFile.filePath = '';
          changedFile.fileUrl = '';
          changedFile.fileId = '';
          changedFile.fileBase64 = '';

          changedFile.fileId = response.file_id;
          callback();
        })
        .catch(function (error) {
          callback(error);
        });
      }
      else {
        callback();
      }
    }, function(err){
      // All files saved. Now save the document

      _.forEach(document.files, function(file, index) {
        var sendFile = _.mapKeys(file, function(value, key) {
          return _.snakeCase(key);
        });
        document.files[index] = sendFile;
      });

      _.forEach(document.fields, function(field, index) {
         _.forEach(document.fields[index], function(innerField, innerIndex) {
            var sendField = _.mapKeys(innerField, function(value, key) {
              return _.snakeCase(key);
            });
            document.fields[index][innerIndex] = sendField;
         });
      });

      var request = new ApiRequest("POST", accessKey, config.DOCUMENT_URL, undefined, parameters, document);
      return request.startRequest();
    });

  };

  this.uploadFile = function (file) {
    if(!file.getFilePath())
      throw new Error('File needs a local file Path to be uploaded');

    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
    };

    var request = new ApiRequest("POST", accessKey, config.FILE_URL, undefined, parameters, file.getFilePath());
    return request.startMultipartUpload();
  }

  /**
   * Retrieves all available Business for the current Client
   *
   * @return {Promise} A promise that resolves to Businesses
   */
  this.fetchBusinesses = function (setDefault = true) {
    return new Promise(function (resolve, reject) {
      var request = new ApiRequest("GET", accessKey, config.BUSINESS_URL, 'Business');
      request.startRequest().then(function (response) {
        businesses = response;
        if(setDefault && businesses.length){
          if(!businessId){
              selectedBusiness = businesses.filter(function functionName(business) { return business.isPrimary(); })[0];
              resolve(businesses);
          }else{
            var filteredBbusinesses = businesses.filter(function (business) {
              return business.getBusinessId() === businessId;
            });

            if(!filteredBbusinesses || !filteredBbusinesses.length){
              reject('No Business found with the specified Business Id');
            }else{
              selectedBusiness = filteredBbusinesses[0];
              resolve(businesses);
            }
          }
        }else{
          resolve(businesses);
        }
      }).catch(function (err) {
        reject(err);
      })
    });
  };

  this.setSelectedBusiness = function (business) {
    if(business instanceof Business){
      selectedBusiness = business;
    }else{
      throw new Error('Provide a valid business');
    }
  };

  this.setSelectedBusinessById = function (_businessId) {
    businessId = _businessId;
  };

  this.getBusinesses = function () {
    return businesses;
  };

  /**
   * Sending a Reminder to a specific Signer inside a Document
   * Both properties are required in order to send the request.
   * Returns true or false whether the reminder has been sent
   * @param  {Document} document [description]
   * @param  {Signer} signer   [description]
   * @return {Promise}          [description]
   */
  this.sendReminderForDocument = function (document, signer) {
    if(!document.getDocumentHash() || !document.getSigners() )
      throw new Error('Sending Reminders requires the Document Hash and an approriate Signer');
    var parameters = {
      business_id: selectedBusiness ? selectedBusiness.getBusinessId(): businessId
    };
    var payload =  {
      document_hash: document.getDocumentHash(),
      signer_id: signer.getId()
    };
    var request = new ApiRequest("POST", accessKey, config.REMINDER_URL, undefined, parameters, payload);
    return request.startRequest();
  };

  /**
   * Fetches the Document with the specified Hash from the API
   *
   * @param  {String} documentHash [description]
   * @return {Promise}              [description]
   */
  this.getDocumentByHash = function (documentHash) {
    var parameters = {
      business_id : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
      document_hash : documentHash
    };
    var request = new ApiRequest("GET", accessKey, config.DOCUMENT_URL, 'Document', parameters);
    return request.startRequest();
  };

  /**
   *
   *
   * @param  {[type]} template [description]
   * @return {[type]}          [description]
   */
  this.createDocumentFromTemplate = function (template) {
    template = template.toObject();

    if(!template.getTemplateId()){
      throw new Error("Template needs a Template Id to create a document from it");
    }
    if( !template.getSigners() || !template.getSigners().length ){
      throw new Error("Template needs at least 1 Signer to create a Document");
    }
    parameters = {
      business_id: selectedBusiness ? selectedBusiness.getBusinessId(): businessId
    };

    var request = new ApiRequest("POST", accessKey, config.DOCUMENT_URL, 'Document', parameters, template);
    return request.startRequest();

  };

  /**
   * Deletes the specified Document. Only works on Drafts and canceled Documents
   *
   * @param  {Document} document [description]
   * @param  {String} type     [description]
   * @return {Promise}          [description]
   */
  this.deleteDocument = function (document, type) {
    if (!document.getDocumentHash()) {
      throw new Error('Deleting the Document requires the Document Hash');
    }
    if (!document.getIsDraft() && !document.getIsCanceled()) {
      throw new Error('Only Drafts and cancelled Documents can be deleted');
    }
    if (document.getIsDeleted()) {
      throw new Error('The Document has been deleted already');
    }
    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
      "document_hash":  document.getDocumentHash()
    };
    if(type) {
      parameters[type] = 1;
    }
    var request = new ApiRequest("DELETE", accessKey, config.DOCUMENT_URL, undefined, $parameters);
    return request.startRequest();
  };

  this.cancelDocument = function (document) {
     return this.deleteDocument(document, "cancel");
  };

  /**
   * Downloads the completed Document. Only works on Documents that have
   * been completed. If you want the Audit Trail on the downloaded File
   * as well, set the auditTrail Parameter to true
   * @param  {Document}  document           [description]
   * @param  {String}  path               [description]
   * @param  {Boolean} [auditTrail=false] [description]
   * @return {Promise}                     [description]
   */
  this.downloadFinalDocumentToPath = function (document, path, auditTrail = false) {
    if(!document.getIsCompleted())
      throw new Error('To Download the final File the Document needs to be completed first');
    return downloadDocumentToPath(document, path, auditTrail);
  };

  /**
   * Downloads the raw Document to the specified Path.
   * Returns true if saving was successful, otherwise false
   * @param  {Document} Document [description]
   * @param  {String} path    [description]
   * @return {Promise}          [description]
   */
  this.downloadRawDocumentToPath = function(document, path) {
    return downloadDocumentToPath(document, path, 0, config.DOCUMENT_RAW_URL);
  };

  function downloadDocumentToPath(document, path, auditTrail = 0, type = config.DOCUMENT_FINAL_URL) {
    if (!path || !document) {
      throw new Error('To Download the Document you need to set a path and the document');
    }
    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
      "document_hash" : document.getDocumentHash(),
      "audit_trail" : auditTrail
    };

    var payload = {
      sink: path
    };

    var request = new ApiRequest("GET", accessKey, type, "Document", parameters, payload);
    return request.startDownload();
  };
  /**
   * INIT
   */
  this.fetchBusinesses();
}

module.exports = Client;
<commit_msg>switch Template data type to Document<commit_after>'use strict';

var async = require('async');
var ApiRequest = require("./Request");
var config = require('./config');
var _ = require('lodash');

var Business = require('./Business');

/**
 *
 * Intialise the client
 * @param  {String} accessKey     API key from eversign dashboard
 * @param  {Number} businessId Business ID from eversign dashboard
 * @return {Client}            Client object
 */
function Client(accessKey, businessId){
  var accessKey = accessKey;
  var businessId = businessId;
  var businesses = [];
  var selectedBusiness = undefined;

  /**
   *
   * Retrieves the documents from eversign API
   * @return {Promise} Which resolves to An array of documents
   */
  this.getAllDocuments = function () {
    return getDocuments();
  };

  /**
   *
   * Retrieves all canceled documents from eversign API
   * @return {Promise} Which resolves to An array of documents
   */
  this.getCancelledDocuments = function () {
    return getDocuments("cancelled");
  };

  /**
   *
   * Returns all Documents for the Client which require Actions
   * @return {Promise} Which resolves to An array of documents
   */
  this.getActionRequiredDocuments = function () {
    return getDocuments("my_action_required");
  };

  /**
   *
   * Returns all Documents for the Client which are waiting on responses
   * @return {Promise} Which resolves to An array of documents
   */
  this.getWaitingForOthersDocuments = function () {
    return getDocuments("waiting_for_others");
  };

  /**
   *
   * Returns a list of Documents which are set to be Templates
   * @return {Promise} Which resolves to An array of documents
   */
  this.getTemplates = function () {
    return getDocuments("templates", 'Document');
  };

  /**
   *
   * Returns a list of Documents which are set to be Templates
   * @return {Promise} Which resolves to An array of documents
   */
  this.getArchivedTemplates = function () {
    return getDocuments("templates_archived", 'Document');
  };

  /**
   *
   * Returns a list of Documents which are set to be Templates
   * @return {Promise} Which resolves to An array of documents
   */
  this.getDraftTemplates = function () {
    return getDocuments("template_drafts", 'Document');
  };

  function getDocuments(type, dataType) {
    if(!type) type = "all"
    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
      "type" : type
    };
    var request = new ApiRequest("GET", accessKey, config.DOCUMENT_URL, dataType ? dataType : 'Document', parameters);
    return request.startRequest();
  }

  this.createDocument = function (document) {
    var self = this;
    document = document.toObject();
    if(!document.getSigners() && !document.getSigners().length )
      throw new Error('Document needs at least 1 Signer to be created');

    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
    };

    async.forEachOf(document.getFiles(), function (file, index, callback) {
      if(file.getFilePath() || file.getFileBase64()) {
        self.uploadFile(file)
        .then(function (response) {

          var changedFile = document.files[index];
          changedFile.filePath = '';
          changedFile.fileUrl = '';
          changedFile.fileId = '';
          changedFile.fileBase64 = '';

          changedFile.fileId = response.file_id;
          callback();
        })
        .catch(function (error) {
          callback(error);
        });
      }
      else {
        callback();
      }
    }, function(err){
      // All files saved. Now save the document

      _.forEach(document.files, function(file, index) {
        var sendFile = _.mapKeys(file, function(value, key) {
          return _.snakeCase(key);
        });
        document.files[index] = sendFile;
      });

      _.forEach(document.fields, function(field, index) {
         _.forEach(document.fields[index], function(innerField, innerIndex) {
            var sendField = _.mapKeys(innerField, function(value, key) {
              return _.snakeCase(key);
            });
            document.fields[index][innerIndex] = sendField;
         });
      });

      var request = new ApiRequest("POST", accessKey, config.DOCUMENT_URL, undefined, parameters, document);
      return request.startRequest();
    });

  };

  this.uploadFile = function (file) {
    if(!file.getFilePath())
      throw new Error('File needs a local file Path to be uploaded');

    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
    };

    var request = new ApiRequest("POST", accessKey, config.FILE_URL, undefined, parameters, file.getFilePath());
    return request.startMultipartUpload();
  }

  /**
   * Retrieves all available Business for the current Client
   *
   * @return {Promise} A promise that resolves to Businesses
   */
  this.fetchBusinesses = function (setDefault = true) {
    return new Promise(function (resolve, reject) {
      var request = new ApiRequest("GET", accessKey, config.BUSINESS_URL, 'Business');
      request.startRequest().then(function (response) {
        businesses = response;
        if(setDefault && businesses.length){
          if(!businessId){
              selectedBusiness = businesses.filter(function functionName(business) { return business.isPrimary(); })[0];
              resolve(businesses);
          }else{
            var filteredBbusinesses = businesses.filter(function (business) {
              return business.getBusinessId() === businessId;
            });

            if(!filteredBbusinesses || !filteredBbusinesses.length){
              reject('No Business found with the specified Business Id');
            }else{
              selectedBusiness = filteredBbusinesses[0];
              resolve(businesses);
            }
          }
        }else{
          resolve(businesses);
        }
      }).catch(function (err) {
        reject(err);
      })
    });
  };

  this.setSelectedBusiness = function (business) {
    if(business instanceof Business){
      selectedBusiness = business;
    }else{
      throw new Error('Provide a valid business');
    }
  };

  this.setSelectedBusinessById = function (_businessId) {
    businessId = _businessId;
  };

  this.getBusinesses = function () {
    return businesses;
  };

  /**
   * Sending a Reminder to a specific Signer inside a Document
   * Both properties are required in order to send the request.
   * Returns true or false whether the reminder has been sent
   * @param  {Document} document [description]
   * @param  {Signer} signer   [description]
   * @return {Promise}          [description]
   */
  this.sendReminderForDocument = function (document, signer) {
    if(!document.getDocumentHash() || !document.getSigners() )
      throw new Error('Sending Reminders requires the Document Hash and an approriate Signer');
    var parameters = {
      business_id: selectedBusiness ? selectedBusiness.getBusinessId(): businessId
    };
    var payload =  {
      document_hash: document.getDocumentHash(),
      signer_id: signer.getId()
    };
    var request = new ApiRequest("POST", accessKey, config.REMINDER_URL, undefined, parameters, payload);
    return request.startRequest();
  };

  /**
   * Fetches the Document with the specified Hash from the API
   *
   * @param  {String} documentHash [description]
   * @return {Promise}              [description]
   */
  this.getDocumentByHash = function (documentHash) {
    var parameters = {
      business_id : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
      document_hash : documentHash
    };
    var request = new ApiRequest("GET", accessKey, config.DOCUMENT_URL, 'Document', parameters);
    return request.startRequest();
  };

  /**
   *
   *
   * @param  {[type]} template [description]
   * @return {[type]}          [description]
   */
  this.createDocumentFromTemplate = function (template) {
    template = template.toObject();

    if(!template.getTemplateId()){
      throw new Error("Template needs a Template Id to create a document from it");
    }
    if( !template.getSigners() || !template.getSigners().length ){
      throw new Error("Template needs at least 1 Signer to create a Document");
    }
    parameters = {
      business_id: selectedBusiness ? selectedBusiness.getBusinessId(): businessId
    };

    var request = new ApiRequest("POST", accessKey, config.DOCUMENT_URL, 'Document', parameters, template);
    return request.startRequest();

  };

  /**
   * Deletes the specified Document. Only works on Drafts and canceled Documents
   *
   * @param  {Document} document [description]
   * @param  {String} type     [description]
   * @return {Promise}          [description]
   */
  this.deleteDocument = function (document, type) {
    if (!document.getDocumentHash()) {
      throw new Error('Deleting the Document requires the Document Hash');
    }
    if (!document.getIsDraft() && !document.getIsCanceled()) {
      throw new Error('Only Drafts and cancelled Documents can be deleted');
    }
    if (document.getIsDeleted()) {
      throw new Error('The Document has been deleted already');
    }
    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
      "document_hash":  document.getDocumentHash()
    };
    if(type) {
      parameters[type] = 1;
    }
    var request = new ApiRequest("DELETE", accessKey, config.DOCUMENT_URL, undefined, $parameters);
    return request.startRequest();
  };

  this.cancelDocument = function (document) {
     return this.deleteDocument(document, "cancel");
  };

  /**
   * Downloads the completed Document. Only works on Documents that have
   * been completed. If you want the Audit Trail on the downloaded File
   * as well, set the auditTrail Parameter to true
   * @param  {Document}  document           [description]
   * @param  {String}  path               [description]
   * @param  {Boolean} [auditTrail=false] [description]
   * @return {Promise}                     [description]
   */
  this.downloadFinalDocumentToPath = function (document, path, auditTrail = false) {
    if(!document.getIsCompleted())
      throw new Error('To Download the final File the Document needs to be completed first');
    return downloadDocumentToPath(document, path, auditTrail);
  };

  /**
   * Downloads the raw Document to the specified Path.
   * Returns true if saving was successful, otherwise false
   * @param  {Document} Document [description]
   * @param  {String} path    [description]
   * @return {Promise}          [description]
   */
  this.downloadRawDocumentToPath = function(document, path) {
    return downloadDocumentToPath(document, path, 0, config.DOCUMENT_RAW_URL);
  };

  function downloadDocumentToPath(document, path, auditTrail = 0, type = config.DOCUMENT_FINAL_URL) {
    if (!path || !document) {
      throw new Error('To Download the Document you need to set a path and the document');
    }
    var parameters = {
      "business_id" : selectedBusiness ? selectedBusiness.getBusinessId(): businessId,
      "document_hash" : document.getDocumentHash(),
      "audit_trail" : auditTrail
    };

    var payload = {
      sink: path
    };

    var request = new ApiRequest("GET", accessKey, type, "Document", parameters, payload);
    return request.startDownload();
  };
  /**
   * INIT
   */
  this.fetchBusinesses();
}

module.exports = Client;
<|endoftext|>


<commit_before>import React from 'react';
import {
  FlatList,
  Text,
  View,
  StyleSheet,
  ActivityIndicator,
  Animated,
  RefreshControl,
  Alert,
  Platform,
} from 'react-native';
import { connect } from 'react-redux';
import { NavigationActions } from 'react-navigation';
import { ListItem } from 'react-native-elements';
import moment from 'moment';

import {
  foiRequestsFetchData,
  foiRequestsRefreshData,
  foiRequestsErrorClearAction,
} from '../../actions/foiRequests';
import publicBodyFile from '../../../scraper/public_bodies/public_bodies_cleaned.json';
import jurisdictionFile from '../../data/jurisdiction.json';
import statusFile from '../../data/status.json';
import FoiRequestsListFilterButton from './FoiRequestsListFilterButton';
import { getItemById, mapToRealStatus } from '../../utils';
import FoiRequestsListHeader from './FoiRequestsListHeader';
import {
  primaryColor,
  primaryColorLight,
  greyDark,
  greyLight,
} from '../../styles/colors';

const LIST_HEADER_HEIGHT = 64;

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);

class FoiRequestsListScreen extends React.Component {
  constructor(props) {
    super(props);

    const scrollAnim = new Animated.Value(0);
    const offsetAnim = new Animated.Value(0);

    this.state = {
      scrollAnim,
      offsetAnim,
      clampedScroll: Animated.diffClamp(
        Animated.add(
          scrollAnim.interpolate({
            inputRange: [0, 1],
            outputRange: [0, 1],
            extrapolateLeft: 'clamp',
          }),
          offsetAnim
        ),
        0,
        LIST_HEADER_HEIGHT
      ),
    };
  }

  componentDidMount() {
    this.props.fetchData();
  }

  componentDidUpdate() {
    // https://github.com/facebook/react-native/issues/1878
    if (
      Platform.OS === 'ios' &&
      this.props.requests.length <= 20 &&
      !this.props.isRefreshing &&
      !(this.props.isPending && this.props.requests.length !== 0)
    ) {
      this.listRef.getNode().scrollToOffset({
        offset: -LIST_HEADER_HEIGHT,
        animated: false,
      });
    }
  }

  _fetchData = () => {
    if (!this.props.isPending) {
      const timeDif = Date.now() - this.props.firstPageFetchedAt;
      // Prevent fetching data twice on initalizition because the `onEndReachEd` event fires with an empy list
      // value in mili seconds
      if (timeDif > 1000) {
        this.props.fetchData();
      }
    }
  };

  _refreshData = () => {
    this.props.refreshData();
  };

  _renderNumberOfResultHeader = () => {
    const nResultsText =
      this.props.nResults !== -1 ? `${this.props.nResults} REQUESTS` : null;
    return (
      <Text style={styles.nResults}>
        {nResultsText}
      </Text>
    );
  };

  _renderPendingActivity = () => {
    if (!this.props.isPending) return null;

    return (
      <View
        style={{
          paddingVertical: 20,
        }}
      >
        <ActivityIndicator animating size="large" color={primaryColorLight} />
      </View>
    );
  };

  _renderItem = ({ item, index }) => {
    // fix because that it's complicated with the status. see utils/index.js for more information.
    const realStatus = mapToRealStatus(item.status, item.resolution);
    const imagePath = realStatus;
    const statusName = statusFile.find(getItemById(realStatus)).name;

    const lastContact = item.last_message || item.first_message;
    const timeAgo = moment(lastContact).fromNow();
    let subtitle = `${statusName}, ${timeAgo}`;

    if (item.public_body) {
      const startToSlice = '/api/v1/publicbody/'.length;
      const endSlice = item.public_body.length - 1;
      const publicBodyId = item.public_body.slice(startToSlice, endSlice);
      const publicBodyObject = publicBodyFile[publicBodyId];
      const publicBodyName = publicBodyObject.publicBodyName;
      const jurisdictionName = publicBodyObject.jurisdictionName;

      subtitle += `\n${publicBodyName} (${jurisdictionName})`;
    }

    return (
      <ListItem
        key={item.id}
        title={item.title}
        titleNumberOfLines={3}
        subtitle={subtitle}
        subtitleNumberOfLines={3}
        avatar={{
          uri: imagePath,
        }}
        chevronColor={primaryColor}
        onPress={() => this.props.navigateToDetails({ request: item })}
        // avatarStyle={{ marginTop: 20 }}
        // TODO: Not possible right now, come back later to check if they have fixed it.
        // avatarStyle={{ overlayContainerStyle: { backgroundColor: 'white' } }}
        containerStyle={{
          alignItems: 'center',
          justifyContent: 'center',
          borderBottomWidth: 0,
        }}
      />
    );
  };

  renderSeparator = () => {
    return (
      <View
        style={{
          height: 0.5,
          backgroundColor: greyLight,
        }}
      />
    );
  };

  render() {
    const { clampedScroll } = this.state;

    const headerTranslate = clampedScroll.interpolate({
      inputRange: [0, LIST_HEADER_HEIGHT],
      outputRange: [0, -LIST_HEADER_HEIGHT],
      extrapolate: 'clamp',
    });

    const headerOpacity = clampedScroll.interpolate({
      inputRange: [0, LIST_HEADER_HEIGHT],
      outputRange: [1, 0],
      extrapolate: 'clamp',
    });

    const hasError = this.props.error !== '';
    if (hasError) {
      const errorText = `Sorry, there has been an error with the message '${this
        .props.error}'`;
      Alert.alert(
        'Error',
        errorText,
        [{ text: 'OK', onPress: this.props.clearError }],
        { cancelable: false }
      );
    }

    const androidContainerStyle =
      Platform.OS === 'android' ? { paddingTop: LIST_HEADER_HEIGHT } : null;

    return (
      <View style={{ backgroundColor: 'white', height: '100%' }}>
        <AnimatedFlatList
          refreshControl={
            <RefreshControl
              refreshing={this.props.isRefreshing}
              onRefresh={this._refreshData}
              progressViewOffset={LIST_HEADER_HEIGHT}
              tintColor={primaryColorLight}
              progressBackgroundColor={primaryColorLight}
            />
          } // progresViewOffset for anodroid
          contentInset={{ top: LIST_HEADER_HEIGHT }} // iOS
          data={this.props.requests}
          extraData={this.props.isPending}
          contentContainerStyle={androidContainerStyle}
          renderItem={this._renderItem}
          onEndReached={this._fetchData}
          onEndReachedThreshold={0.5}
          ListFooterComponent={this._renderPendingActivity}
          ListHeaderComponent={this._renderNumberOfResultHeader}
          ItemSeparatorComponent={this.renderSeparator}
          ref={ref => (this.listRef = ref)}
          onScroll={Animated.event(
            [{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } }],
            { useNativeDriver: true }
          )}
          scrollEventThrottle={10} // iOS only, between onScroll calls are at least 500ms
          style={{ backgroundColor: 'white' }} // this fixes a bug with not appearing activity spinner
        />
        <Animated.View
          style={[
            styles.header,
            { transform: [{ translateY: headerTranslate }] },
          ]}
        >
          <Animated.View style={[styles.title, { opacity: headerOpacity }]}>
            <FoiRequestsListHeader />
          </Animated.View>
        </Animated.View>
      </View>
    );
  }
}

FoiRequestsListScreen.navigationOptions = {
  title: 'Requests',
  headerRight: <FoiRequestsListFilterButton />,
};

const mapStateToProps = state => {
  return {
    ...state.foiRequests,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    clearError: () => dispatch(foiRequestsErrorClearAction()),
    fetchData: () => dispatch(foiRequestsFetchData()),
    refreshData: () => dispatch(foiRequestsRefreshData()),
    navigateToDetails: params =>
      dispatch(NavigationActions.navigate({ routeName: 'Details', params })),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(
  FoiRequestsListScreen
);

const styles = StyleSheet.create({
  header: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    height: LIST_HEADER_HEIGHT,
  },
  nResults: {
    textAlign: 'center',
    color: greyDark,
    fontWeight: '400',
    fontSize: 12,
    marginTop: 10,
    marginBottom: 5,
  },
});
<commit_msg>fix not appearing seperators<commit_after>import React from 'react';
import {
  FlatList,
  Text,
  View,
  StyleSheet,
  ActivityIndicator,
  Animated,
  RefreshControl,
  Alert,
  Platform,
} from 'react-native';
import { connect } from 'react-redux';
import { NavigationActions } from 'react-navigation';
import { ListItem } from 'react-native-elements';
import moment from 'moment';

import {
  foiRequestsFetchData,
  foiRequestsRefreshData,
  foiRequestsErrorClearAction,
} from '../../actions/foiRequests';
import publicBodyFile from '../../../scraper/public_bodies/public_bodies_cleaned.json';
import jurisdictionFile from '../../data/jurisdiction.json';
import statusFile from '../../data/status.json';
import FoiRequestsListFilterButton from './FoiRequestsListFilterButton';
import { getItemById, mapToRealStatus } from '../../utils';
import FoiRequestsListHeader from './FoiRequestsListHeader';
import {
  primaryColor,
  primaryColorLight,
  greyDark,
  greyLight,
} from '../../styles/colors';

const LIST_HEADER_HEIGHT = 64;

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);

class FoiRequestsListScreen extends React.Component {
  constructor(props) {
    super(props);

    const scrollAnim = new Animated.Value(0);
    const offsetAnim = new Animated.Value(0);

    this.state = {
      scrollAnim,
      offsetAnim,
      clampedScroll: Animated.diffClamp(
        Animated.add(
          scrollAnim.interpolate({
            inputRange: [0, 1],
            outputRange: [0, 1],
            extrapolateLeft: 'clamp',
          }),
          offsetAnim
        ),
        0,
        LIST_HEADER_HEIGHT
      ),
    };
  }

  componentDidMount() {
    this.props.fetchData();
  }

  componentDidUpdate() {
    // https://github.com/facebook/react-native/issues/1878
    if (
      Platform.OS === 'ios' &&
      this.props.requests.length <= 20 &&
      !this.props.isRefreshing &&
      !(this.props.isPending && this.props.requests.length !== 0)
    ) {
      this.listRef.getNode().scrollToOffset({
        offset: -LIST_HEADER_HEIGHT,
        animated: false,
      });
    }
  }

  _fetchData = () => {
    if (!this.props.isPending) {
      const timeDif = Date.now() - this.props.firstPageFetchedAt;
      // Prevent fetching data twice on initalizition because the `onEndReachEd` event fires with an empy list
      // value in mili seconds
      if (timeDif > 1000) {
        this.props.fetchData();
      }
    }
  };

  _refreshData = () => {
    this.props.refreshData();
  };

  _renderNumberOfResultHeader = () => {
    const nResultsText =
      this.props.nResults !== -1 ? `${this.props.nResults} REQUESTS` : null;
    return (
      <Text style={styles.nResults}>
        {nResultsText}
      </Text>
    );
  };

  _renderPendingActivity = () => {
    if (!this.props.isPending) return null;

    return (
      <View
        style={{
          paddingVertical: 20,
        }}
      >
        <ActivityIndicator animating size="large" color={primaryColorLight} />
      </View>
    );
  };

  _renderItem = ({ item, index }) => {
    // fix because that it's complicated with the status. see utils/index.js for more information.
    const realStatus = mapToRealStatus(item.status, item.resolution);
    const imagePath = realStatus;
    const statusName = statusFile.find(getItemById(realStatus)).name;

    const lastContact = item.last_message || item.first_message;
    const timeAgo = moment(lastContact).fromNow();
    let subtitle = `${statusName}, ${timeAgo}`;

    if (item.public_body) {
      const startToSlice = '/api/v1/publicbody/'.length;
      const endSlice = item.public_body.length - 1;
      const publicBodyId = item.public_body.slice(startToSlice, endSlice);
      const publicBodyObject = publicBodyFile[publicBodyId];
      const publicBodyName = publicBodyObject.publicBodyName;
      const jurisdictionName = publicBodyObject.jurisdictionName;

      subtitle += `\n${publicBodyName} (${jurisdictionName})`;
    }

    return (
      <ListItem
        key={item.id}
        title={item.title}
        titleNumberOfLines={3}
        subtitle={subtitle}
        subtitleNumberOfLines={3}
        avatar={{
          uri: imagePath,
        }}
        chevronColor={primaryColor}
        onPress={() => this.props.navigateToDetails({ request: item })}
        // avatarStyle={{ marginTop: 20 }}
        // TODO: Not possible right now, come back later to check if they have fixed it.
        // avatarStyle={{ overlayContainerStyle: { backgroundColor: 'white' } }}
        containerStyle={{
          alignItems: 'center',
          justifyContent: 'center',
          borderBottomWidth: 0,
        }}
      />
    );
  };

  renderSeparator = () => {
    return (
      <View
        style={{
          height: 1,
          backgroundColor: greyLight,
        }}
      />
    );
  };

  render() {
    const { clampedScroll } = this.state;

    const headerTranslate = clampedScroll.interpolate({
      inputRange: [0, LIST_HEADER_HEIGHT],
      outputRange: [0, -LIST_HEADER_HEIGHT],
      extrapolate: 'clamp',
    });

    const headerOpacity = clampedScroll.interpolate({
      inputRange: [0, LIST_HEADER_HEIGHT],
      outputRange: [1, 0],
      extrapolate: 'clamp',
    });

    const hasError = this.props.error !== '';
    if (hasError) {
      const errorText = `Sorry, there has been an error with the message '${this
        .props.error}'`;
      Alert.alert(
        'Error',
        errorText,
        [{ text: 'OK', onPress: this.props.clearError }],
        { cancelable: false }
      );
    }

    const androidContainerStyle =
      Platform.OS === 'android' ? { paddingTop: LIST_HEADER_HEIGHT } : null;

    return (
      <View style={{ backgroundColor: 'white', height: '100%' }}>
        <AnimatedFlatList
          refreshControl={
            <RefreshControl
              refreshing={this.props.isRefreshing}
              onRefresh={this._refreshData}
              progressViewOffset={LIST_HEADER_HEIGHT}
              tintColor={primaryColorLight}
              progressBackgroundColor={primaryColorLight}
            />
          } // progresViewOffset for anodroid
          contentInset={{ top: LIST_HEADER_HEIGHT }} // iOS
          data={this.props.requests}
          extraData={this.props.isPending}
          contentContainerStyle={androidContainerStyle}
          renderItem={this._renderItem}
          onEndReached={this._fetchData}
          onEndReachedThreshold={0.5}
          ListFooterComponent={this._renderPendingActivity}
          ListHeaderComponent={this._renderNumberOfResultHeader}
          ItemSeparatorComponent={this.renderSeparator}
          ref={ref => (this.listRef = ref)}
          onScroll={Animated.event(
            [{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } }],
            { useNativeDriver: true }
          )}
          scrollEventThrottle={10} // iOS only, between onScroll calls are at least 500ms
          style={{ backgroundColor: 'white' }} // this fixes a bug with not appearing activity spinner
        />
        <Animated.View
          style={[
            styles.header,
            { transform: [{ translateY: headerTranslate }] },
          ]}
        >
          <Animated.View style={[styles.title, { opacity: headerOpacity }]}>
            <FoiRequestsListHeader />
          </Animated.View>
        </Animated.View>
      </View>
    );
  }
}

FoiRequestsListScreen.navigationOptions = {
  title: 'Requests',
  headerRight: <FoiRequestsListFilterButton />,
};

const mapStateToProps = state => {
  return {
    ...state.foiRequests,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    clearError: () => dispatch(foiRequestsErrorClearAction()),
    fetchData: () => dispatch(foiRequestsFetchData()),
    refreshData: () => dispatch(foiRequestsRefreshData()),
    navigateToDetails: params =>
      dispatch(NavigationActions.navigate({ routeName: 'Details', params })),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(
  FoiRequestsListScreen
);

const styles = StyleSheet.create({
  header: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    height: LIST_HEADER_HEIGHT,
  },
  nResults: {
    textAlign: 'center',
    color: greyDark,
    fontWeight: '400',
    fontSize: 12,
    marginTop: 10,
    marginBottom: 5,
  },
});
<|endoftext|>


<commit_before>'use strict'
const log = require('debug')
var debug = log('netcat:client')
const Netcat = require('./Netcat')
const portScan = require('./portScan')
const tcpClient = require('./tcpClient')
const udpClient = require('./udpClient')

function noop () {}

class Client extends Netcat {
  constructor (opts) {
    opts = opts || {}
    super(opts)

    debug = opts.verbose ? function () {
      var args = Array.prototype.slice.call(arguments)
      process.stderr.write(args.join(' ') + '\n')
    } : debug
    this.debug = debug
    this._address = opts.address || '127.0.0.1'
    this._interval = opts.interval || false
    this._q = opts.q || null
  }

  interval (secs) {
    debug('setting interval to', secs, 'seconds')
    if (!Number.isInteger(secs)) throw Error('Please provide a valid number')
    this._interval = Math.round(secs)
    return this
  }

  i (s) { /* alias for interval */
    return this.interval(s)
  }

  init () {
    if (this._protocol === 'tcp') throw Error('Useless call to init() in TCP mode, use connect() instead')
    if (!this._port) throw Error('Port is mandatory in UDP')
    udpClient.call(this, debug)
    return this
  }

  connect (cb) {
    if (this._protocol !== 'tcp') {
      throw Error('Useless call to connect() in UDP mode, use init() instead')
    }
    tcpClient.call(this, debug, cb)
    return this
  }

  retry (ms) {
    if (this._protocol !== 'tcp') throw Error('Cannot use retry() in UDP')
    if (!Number.isInteger(ms)) throw Error('Retry should be a positive integer!')
    this._retry = ms
    return this /* chainable method */
  }

  stream () {
    debug('Returning stream reference')
    return this.client /* return stream */
  }

  pipe (outStream) {
    if (this.client) {
      debug('Piping incoming data from socket to the given outStream')
      this.client.pipe(outStream, { end: !this._retry }) /* HACK: kept open when retrying */
    } else {
      debug('Client is NOT defined, please call pipe after connect()!')
    }
    return this
  }

  send (data, cb) {
    var cbOrHost = (this._protocol === 'tcp') ? (cb || noop) : (cb || '255.255.255.255')

    if (data && !Buffer.isBuffer(data)) {
      data = Buffer.from(data)
    }
    var self = this
    if (!this.client) return this
    if (this._interval) { /* delay */
      setTimeout(function () {
        self.client.write(data, cbOrHost)
        debug('Client sent data', data, ((this._protocol === 'udp') ? 'to ' + cbOrHost : ''))
      }, self._interval * 1000)
    } else {
      debug('host', cbOrHost)
      self.client.write(data, cbOrHost)
      debug('Client sent data', data, ((this._protocol === 'udp') ? 'to ' + cbOrHost : ''))
    }
    return this
  }

  end (d) {
    if (this._protocol === 'udp') throw Error('end() method is tcp only')
    var self = this
    if (this._interval) {
      setTimeout(function () { /* delay */
        if (self.client) self.client.end(d) /* send data + EOF */
      }, self._interval * 1000)
    } else {
      self.client.end(d) /* send data + EOF */
    }
    return this
  }

  close (cb) {
    debug('client: closing socket.')
    if (typeof cb === 'function') this.on('close', cb)
    if (this._protocol === 'udp') {
      try {
        if (this.client) this.client.close(cb)
      } catch (e) {
        debug('client already closed')
      }
      return this
    }
    return this.end()
  }

  scan (portsInterval, cb) {
    /* Port Scanner */
    portScan(this, portsInterval, cb)
    return this
  }
}

module.exports = Client

if (!module.parent) {
  const nc2 = new Client()
  nc2.unixSocket('/var/run/docker.sock')
  .enc('utf8')
  .on('data', function (res) {
    console.log(res)
  })
  .connect()
  .send('GET /images/json HTTP/1.0\r\n\r\n')
}
<commit_msg>debug improv<commit_after>'use strict'
const log = require('debug')
var debug = log('netcat:client')
const Netcat = require('./Netcat')
const portScan = require('./portScan')
const tcpClient = require('./tcpClient')
const udpClient = require('./udpClient')

function noop () {}

class Client extends Netcat {
  constructor (opts) {
    opts = opts || {}
    super(opts)

    debug = opts.verbose ? function () {
      var args = Array.prototype.slice.call(arguments)
      process.stderr.write(args.join(' ') + '\n')
    } : debug
    this.debug = debug
    this._address = opts.address || '127.0.0.1'
    this._interval = opts.interval || false
    this._q = opts.q || null
  }

  interval (secs) {
    debug('setting interval to', secs, 'seconds')
    if (!Number.isInteger(secs)) throw Error('Please provide a valid number')
    this._interval = Math.round(secs)
    return this
  }

  i (s) { /* alias for interval */
    return this.interval(s)
  }

  init () {
    if (this._protocol === 'tcp') throw Error('Useless call to init() in TCP mode, use connect() instead')
    if (!this._port) throw Error('Port is mandatory in UDP')
    udpClient.call(this, debug)
    return this
  }

  connect (cb) {
    if (this._protocol !== 'tcp') {
      throw Error('Useless call to connect() in UDP mode, use init() instead')
    }
    tcpClient.call(this, debug, cb)
    return this
  }

  retry (ms) {
    if (this._protocol !== 'tcp') throw Error('Cannot use retry() in UDP')
    if (!Number.isInteger(ms)) throw Error('Retry should be a positive integer!')
    this._retry = ms
    return this /* chainable method */
  }

  stream () {
    debug('Returning stream reference')
    return this.client /* return stream */
  }

  pipe (outStream) {
    if (this.client) {
      debug('Piping incoming data from socket to the given outStream')
      this.client.pipe(outStream, { end: !this._retry }) /* HACK: kept open when retrying */
    } else {
      debug('Client is NOT defined, please call pipe after connect()!')
    }
    return this
  }

  send (data, cb) {
    var cbOrHost = (this._protocol === 'tcp') ? (cb || noop) : (cb || null)

    if (data && !Buffer.isBuffer(data)) {
      data = Buffer.from(data)
    }
    var self = this
    if (!this.client) return this
    if (this._interval) { /* delay */
      setTimeout(function () {
        self.client.write(data, cbOrHost)
        debug('Client sent data', data, ((self._protocol === 'udp') ? 'to ' + (cbOrHost || self._destination) : ''))
      }, self._interval * 1000)
    } else {
      self.client.write(data, cbOrHost)
      debug('Client sent data', data, ((self._protocol === 'udp') ? 'to ' + (cbOrHost || self._destination) : ''))
    }
    return this
  }

  end (d) {
    if (this._protocol === 'udp') throw Error('end() method is tcp only')
    var self = this
    if (this._interval) {
      setTimeout(function () { /* delay */
        if (self.client) self.client.end(d) /* send data + EOF */
      }, self._interval * 1000)
    } else {
      self.client.end(d) /* send data + EOF */
    }
    return this
  }

  close (cb) {
    debug('client: closing socket.')
    if (typeof cb === 'function') this.on('close', cb)
    if (this._protocol === 'udp') {
      try {
        if (this.client) this.client.close(cb)
      } catch (e) {
        debug('client already closed')
      }
      return this
    }
    return this.end()
  }

  scan (portsInterval, cb) {
    /* Port Scanner */
    portScan(this, portsInterval, cb)
    return this
  }
}

module.exports = Client

if (!module.parent) {
  const nc2 = new Client()
  nc2.unixSocket('/var/run/docker.sock')
  .enc('utf8')
  .on('data', function (res) {
    console.log(res)
  })
  .connect()
  .send('GET /images/json HTTP/1.0\r\n\r\n')
}
<|endoftext|>