Installer.js

'use strict';

const _readline_    = require('readline');
const _path_        = require('path');
const _fs_          = require('fs');
const _stream_      = require('stream');
const _got_         = require("got");

const _https_       = require('https');

const {URL}         = require("url");
const {promisify}   = require('util');

//const _pipeline_ = promisify(_stream_.pipeline);


const StatusMessage = require('./StatusMessage');
const Configuration = require('./Configuration');
const Workspace = require('./Workspace');
const DxcWorkspace = require('./DexcaliburWorkspace');

const Logger        = require('./Logger')();


const GET_VERSION = {
    javaVersion: "java --version",
    fridaVersion: "frida --version",
    nodeVersion: "node --version"
};




/**
 * This class is the install helper
 */
class Installer
{
    constructor( pContext=null){
        this.consoleIO = _readline_.createInterface({
            input: process.stdin,
            output: process.stdout
        });    
        
        this.context = pContext;

        this.os = null;
      

        this.wsReady = false;

        this.status = null;

        this.taskList = [];
        this.aborted = false;
    }

    

    /**
     * To download remote file
     * 
     * @param {URL} pRemoteURL Remote URL
     */
    static download(pRemoteURL, pLocalPath, pOptions, pCallbacks){


        _stream_.pipeline(
            _got_.stream(pRemoteURL, pOptions),
            _fs_.createWriteStream(pLocalPath, {
                flags: 'w+',
                mode: 0o777,
                encoding: 'binary' // binary
            }),
            (err)=>{
                if(pCallbacks.onSuccess != null)
                        pCallbacks.onSuccess(err);
            }
        );
    }

    static verifyWorkspacePath( pPath){
        if( _fs_.existsSync(pPath) == true)
            return null;
        else
            return "Folder not found.";
    }

    /**
     * 
     * Callbacks :
     *  - onSuccess
     *  - onError
     *  - onPreDownload
     *  - onPostDownload
     *  - onDownloadingError
     * 
     * 
     * @param {*} pName 
     * @param {*} pRemoteURL 
     * @param {*} pLocalPath 
     * @param {*} pCallbacks 
     * @param {*} pOS 
     */
    addTask(pName, pRemoteURL, pLocalPath, pCallbacks, pOptions = {}){
        this.taskList.push({
            name: pName,
            url: pRemoteURL,
            target: pLocalPath,
            callbacks: pCallbacks,
            system: pOptions.system != null ? pOptions.system : null,
            options: pOptions
        });
    }

    addSimpleTask(pName, pCallbacks, pOptions={}){
        this.taskList.push({
            name: pName,
            url: null,
            callbacks: pCallbacks,
            system: pOptions.system != null ? pOptions.system : null,
            options: pOptions
        });
    }

    runTask( pTaskOffset, pStep){
        let self = this;
        let task = this.taskList[pTaskOffset];

        // download
        if(task.url !== null){
            this.status = new StatusMessage( this.status.progress, `Downloading ${task.name} from ${task.url} ...`);
            Installer.download( task.url, task.target, task.options, {
                onSuccess: function(vData){
                    self.status.progress += pStep;
                    if(task.callbacks.onPostDownload != null)
                        task.callbacks.onPostDownload(task, pStep, vData)

                    self.nextTask( pTaskOffset+1, pStep);
                },
                onError: function(vErr){
                    self.aborted = true;
                    if(task.callbacks.onDownloadingError !== null)
                        task.callbacks.onDownloadingError(task, pStep, vErr)
                }
            });
        }else{
            self.status.progress += pStep;         
            this.status = new StatusMessage( this.progress, `Running : ${task.name}  ...`);
            task.callbacks.onSuccess(this);

            self.nextTask( pTaskOffset+1, pStep);
        }

        // after downlaod

    }

    nextTask( pTaskOffset, pStep){
        if(this.aborted == false){
            if(this.taskList[pTaskOffset] !== undefined)
                this.runTask( pTaskOffset, pStep);
            else{
                this.status = StatusMessage.newSuccess(`Success.`);
            }
        }else{
            this.status = StatusMessage.newError( this.status.progress, `An error occured. Stopped :( `);
        }
    }

    run(){
        let step = 10; //Math.round(100/(this.taskList.length*2));

        this.status = new StatusMessage( 10, `Starting ...`);
        this.runTask( 0, step);

        return true;
    }

    resetStatus(){
        this.status = null;
    }

    getStatus(){
        return this.status;
    }

    /*
    initStatus(){
        this.status = {
            msg: null,
            progress: 0
        };
    }


    getStatusProgress(){
        return this.status.progress;
    }

    getStatusMessage(){
        return this.status.msg;
    }
    */

    checkFridaVersion(){
        let v = process.execSync('frida --version');
        if(/[0-9]{1,2}(\.[0-9a-f]+)/.test(v)){
            return true;
        }else{
            return false;
        }
    }
}

module.exports = {
    Installer: Installer,
    StatusMessage: StatusMessage
}