tuiHoneyPot

front and back end of my TUI honeypot
Log | Files | Refs | README

main.js (9734B)


      1 /**
      2  * The main FakeTerminal class
      3  * @param el
      4  * @param options
      5  * @returns {window.FakeTerminal}
      6  */
      7 window.FakeTerminal.main = function (el, options) {
      8 
      9     /**
     10      * Avoid scope issues by using `base` instead of `this`
     11      * @type {Object}
     12      */
     13     var base = this;
     14     if (!jQuery) {
     15         throw 'FakeTerminal: jQuery required';
     16     } else {
     17         var $ = jQuery;
     18     }
     19 
     20     /**
     21      * jQuery version of the element
     22      * @type {jQuery}
     23      */
     24     base.$el = $(el);
     25 
     26     /**
     27      * The original HTML of the element prior to initialisation (so we can restore the original terminal)
     28      * @type {string}
     29      */
     30     base.originalHtml = '';
     31 
     32     /**
     33      * Any existing items within the element
     34      * @type {Array}
     35      */
     36     base.existingText = [];
     37 
     38     /**
     39      * References to the currently executing command's instance and deferred object
     40      * @type {Object}
     41      */
     42     base.executingCommand = {
     43         'instance': null,
     44         'deferred': null
     45     };
     46 
     47     /**
     48      * Map of keyCodes
     49      * @type {Object}
     50      */
     51     base.keymap = {
     52         ENTER: 13,
     53         UP:    38,
     54         DOWN:  40,
     55         C:     67,
     56         D:     68,
     57         U:     85
     58     };
     59 
     60     /**
     61      * The output service
     62      * @type {window.FakeTerminal.output}
     63      */
     64     base.output = null;
     65 
     66     /**
     67      * The input service
     68      * @type {window.FakeTerminal.input}
     69      */
     70     base.input = null;
     71 
     72     /**
     73      * The filesystem service
     74      * @type {window.FakeTerminal.filesystem}
     75      */
     76     base.filesystem = null;
     77 
     78     /**
     79      * The history service
     80      * @type {window.FakeTerminal.history}
     81      */
     82     base.history = null;
     83 
     84     // --------------------------------------------------------------------------
     85 
     86     /**
     87      * Constructs the FakeTerminal
     88      * @return {void}
     89      */
     90     base.__construct = function () {
     91 
     92         base.$el.trigger('ft:init', [base]);
     93 
     94         //  Merge the options together
     95         base.options = $.extend({}, window.FakeTerminal.defaultOptions, options);
     96 
     97         //  Copy the original markup so we can destroy nicely
     98         base.originalHtml = base.$el.get(0).outerHTML;
     99         base.existingText = base.$el.get(0).innerHTML ? base.$el.get(0).innerHTML.split('\n') : [];
    100 
    101         //  Prepare the element
    102         base.$el
    103             .addClass('faketerminal')
    104             .empty();
    105 
    106         //  Bind listeners
    107         base.bindListeners();
    108 
    109         //  Construct the core classes
    110         base.output     = new window.FakeTerminal.output(base);
    111         base.input      = new window.FakeTerminal.input(base);
    112         base.filesystem = new window.FakeTerminal.filesystem(base);
    113         base.history    = new window.FakeTerminal.history(base);
    114 
    115         /**
    116          * Add the existing content; if there is more than one line of content skip the first and last line
    117          * This is so that we can layout the HTML correctly, i.e contents on a new line
    118          */
    119         for (var i = 0, j = base.existingText.length; i < j; i++) {
    120             if (base.existingText.length > 1 && i === 0) {
    121                 continue;/**/
    122             } else if (base.existingText.length > 1 && i === base.existingText.length - 1) {
    123                 continue;
    124             }
    125             base.output.write($.trim(base.existingText[i]));
    126         }
    127 
    128         //  Focus the input
    129         base.input.focus();
    130 
    131         base.$el.trigger('ft:ready', [base]);
    132 
    133         //  Run the login command, if there is one
    134         if (base.options.login) {
    135             base.exec(base.options.login);
    136         }
    137     };
    138 
    139     // --------------------------------------------------------------------------
    140 
    141     /**
    142      * Binds listeners to the DOM element
    143      * @return {void}
    144      */
    145     base.bindListeners = function () {
    146         base.$el
    147             .on('click', function () {
    148                 base.input.focus();
    149             })
    150             .on('keyup', function (e) {
    151                 if (e.ctrlKey && e.which === base.keymap.C) {
    152                     base.input.ctrlC();
    153                 } else if (e.ctrlKey && e.which === base.keymap.U) {
    154                     base.input.ctrlU();
    155                 }
    156             });
    157     };
    158 
    159     // --------------------------------------------------------------------------
    160 
    161     /**
    162      * Returns a new instance of a command if it exists
    163      * @return {Object}
    164      */
    165     base.findCommand = function (command) {
    166         var cmdInstance;
    167         if (typeof window.FakeTerminal.command[command] === 'function') {
    168             cmdInstance = new window.FakeTerminal.command[command](base);
    169         }
    170         return cmdInstance;
    171     };
    172 
    173     // --------------------------------------------------------------------------
    174 
    175     /**
    176      * Sets the value of the prompt automatically
    177      * @return {String}
    178      */
    179     base.getPrompt = function () {
    180 
    181         var hostname, username, cwd, text;
    182 
    183         //  Determine values
    184         if (typeof base.options.hostname === 'function') {
    185             hostname = base.options.hostname.call();
    186         } else {
    187             hostname = base.options.hostname;
    188         }
    189 
    190         if (typeof base.options.username === 'function') {
    191             username = base.options.username.call();
    192         } else {
    193             username = base.options.username;
    194         }
    195 
    196         if (typeof base.options.cwd === 'function') {
    197             cwd = base.options.cwd.call();
    198         } else {
    199             cwd = base.options.cwd;
    200         }
    201 
    202         //  Ensure the username is lowercase, alpha-numeric
    203         username = username.toLowerCase().replace(/[^a-z0-9]/g, '');
    204 
    205         //  Compile the string
    206         text = base.options.prompt;
    207         text = text.replace(/%hostname%/g, hostname);
    208         text = text.replace(/%username%/g, username);
    209         text = text.replace(/%cwd%/g, cwd);
    210 
    211         return base.colorize(text);
    212     };
    213 
    214     // --------------------------------------------------------------------------
    215 
    216     /**
    217      * Replaces references to <info> etc with spans which can be colourised
    218      * @param {String} line The line to colorize
    219      * @returns {String}
    220      */
    221     base.colorize = function (line) {
    222         line = line.replace(/<([a-zA-Z].+?)>/g, '<span class="color--$1">', line);
    223         line = line.replace(/<\/([a-zA-Z].+)>/g, '</span>', line);
    224         return line;
    225     };
    226 
    227     // --------------------------------------------------------------------------
    228 
    229     /**
    230      * Scroll to the bottom of the terminal window
    231      * @returns {Object} A reference to the class, for chaining
    232      */
    233     base.scrollToBottom = function () {
    234         base.$el.scrollTop(base.$el.get(0).scrollHeight);
    235         return base;
    236     };
    237 
    238     // --------------------------------------------------------------------------
    239 
    240     /**
    241      * Destroys the fake terminal, reverting it back to its previous state
    242      * @return {Object} A reference to the class, for chaining
    243      */
    244     base.destroy = function () {
    245         base.$el.replaceWith($(base.originalHtml));
    246         base.$el.trigger('ft:destroy', [base]);
    247         return base;
    248     };
    249 
    250     // --------------------------------------------------------------------------
    251 
    252     /**
    253      * Executes a command
    254      * @param  {String}  commandString The command to execute
    255      * @param  {Boolean} hidden        Whether the commands are added to the command history
    256      * @return {Object}                A reference to the class, for chaining
    257      */
    258     base.exec = function (commandString, hidden) {
    259 
    260         var deferred, command, userArgs, commandInstance;
    261         deferred = new $.Deferred();
    262 
    263         commandString = $.trim(commandString);
    264 
    265         if (!hidden) {
    266             base.output.write(commandString, true);
    267         }
    268 
    269         //  If the command is empty then no need to proceed.
    270         if (commandString.length === 0) {
    271             deferred.reject();
    272             return deferred.promise();
    273         }
    274 
    275         command         = $.trim(commandString.split(' ').slice(0, 1));
    276         userArgs        = commandString.split(' ').slice(1);
    277         commandInstance = base.findCommand(command);
    278 
    279         if (commandInstance) {
    280 
    281             //  If a command is currently executing, terminate it
    282             if (base.executingCommand.instance) {
    283                 base.executingCommand.instance.terminate();
    284                 base.executingCommand.instance = null;
    285                 base.executingCommand.deferred = null;
    286             }
    287 
    288             /**
    289              * The command has been called, hide the prompt until the command resolves
    290              * or if CTRL+C is encountered.
    291              */
    292             base.input.disable();
    293 
    294             /**
    295              * Call the command's execute function. It is responsible for output and
    296              * resolving the promise once it's complete
    297              */
    298             base.executingCommand.instance = commandInstance;
    299             base.executingCommand.deferred = commandInstance
    300                 .execute.apply(commandInstance, userArgs)
    301                 .done(function () {
    302                     deferred.resolve(arguments);
    303                 })
    304                 .fail(function () {
    305                     deferred.reject(arguments);
    306                 })
    307                 .always(function () {
    308                     base.input.enable().focus();
    309                 });
    310 
    311         } else if (command.length > 0) {
    312             if (!hidden) {
    313                 base.output.write('command not found: "' + command + '"');
    314             }
    315             deferred.reject();
    316         } else {
    317             deferred.reject();
    318         }
    319 
    320         if (!hidden) {
    321             base.history.push(command);
    322         }
    323 
    324         base.$el.trigger('ft:command', [base, command]);
    325 
    326         return deferred.promise();
    327     };
    328 
    329     // --------------------------------------------------------------------------
    330 
    331     // Run constructor
    332     base.__construct();
    333 };