Your IP : 3.18.225.203


Current Path : /usr/share/doc/shellinabox/
Upload File :
Current File : //usr/share/doc/shellinabox/shell_in_a_box.js

// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator.
// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// In addition to these license terms, the author grants the following
// additional rights:
//
// If you modify this program, or any covered work, by linking or
// combining it with the OpenSSL project's OpenSSL library (or a
// modified version of that library), containing parts covered by the
// terms of the OpenSSL or SSLeay licenses, the author
// grants you additional permission to convey the resulting work.
// Corresponding Source for a non-source form of such a combination
// shall include the source code for the parts of OpenSSL used as well
// as that of the covered work.
//
// You may at your option choose to remove this additional permission from
// the work, or from any part of it.
//
// It is possible to build this program in a way that it loads OpenSSL
// libraries at run-time. If doing so, the following notices are required
// by the OpenSSL and SSLeay licenses:
//
// This product includes software developed by the OpenSSL Project
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
//
// This product includes cryptographic software written by Eric Young
// (eay@cryptsoft.com)
//
//
// The most up-to-date version of this program is always available from
// http://shellinabox.com
//
//
// Notes:
//
// The author believes that for the purposes of this license, you meet the
// requirements for publishing the source code, if your web server publishes
// the source in unmodified form (i.e. with licensing information, comments,
// formatting, and identifier names intact). If there are technical reasons
// that require you to make changes to the source code when serving the
// JavaScript (e.g to remove pre-processor directives from the source), these
// changes should be done in a reversible fashion.
//
// The author does not consider websites that reference this script in
// unmodified form, and web servers that serve this script in unmodified form
// to be derived works. As such, they are believed to be outside of the
// scope of this license and not subject to the rights or restrictions of the
// GNU General Public License.
//
// If in doubt, consult a legal professional familiar with the laws that
// apply in your country.

// #define XHR_UNITIALIZED 0
// #define XHR_OPEN        1
// #define XHR_SENT        2
// #define XHR_RECEIVING   3
// #define XHR_LOADED      4

// IE does not define XMLHttpRequest by default, so we provide a suitable
// wrapper.
if (typeof XMLHttpRequest == 'undefined') {
  XMLHttpRequest = function() {
    try { return new ActiveXObject('Msxml2.XMLHTTP.6.0');} catch (e) { }
    try { return new ActiveXObject('Msxml2.XMLHTTP.3.0');} catch (e) { }
    try { return new ActiveXObject('Msxml2.XMLHTTP');    } catch (e) { }
    try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) { }
    throw new Error('');
  };
}

function extend(subClass, baseClass) {
  function inheritance() { }
  inheritance.prototype          = baseClass.prototype;
  subClass.prototype             = new inheritance();
  subClass.prototype.constructor = subClass;
  subClass.prototype.superClass  = baseClass.prototype;
};

function ShellInABox(url, container) {
  if (url == undefined) {
    this.rooturl    = document.location.href;
    this.url        = document.location.href.replace(/[?#].*/, '');
  } else {
    this.rooturl    = url;
    this.url        = url;
  }
  if (document.location.hash != '') {
    var hash        = decodeURIComponent(document.location.hash).
                      replace(/^#/, '');
    this.nextUrl    = hash.replace(/,.*/, '');
    this.session    = hash.replace(/[^,]*,/, '');
  } else {
    this.nextUrl    = this.url;
    this.session    = null;
  }
  this.pendingKeys  = '';
  this.keysInFlight = false;
  this.connected    = false;
  this.replayOnOutput  = false;
  this.replayOnSession = false;
  this.superClass.constructor.call(this, container);


  // We have to initiate the first XMLHttpRequest from a timer. Otherwise,
  // Chrome never realizes that the page has loaded.
  setTimeout(function(shellInABox) {
               return function() {
                 shellInABox.messageInit();
                 shellInABox.sendRequest();
               };
             }(this), 1);
};
extend(ShellInABox, VT100);

ShellInABox.prototype.sessionClosed = function() {
  try {
    this.connected    = false;
    if (this.session) {
      this.session    = undefined;
      if (this.cursorX > 0) {
        this.vt100('\r\n');
      }
      this.vt100('Session closed.');
    }
    this.showReconnect(true);
    if (this.replayOnSession) {
      this.messageReplay('session', 'closed');
    }
  } catch (e) {
  }
};

ShellInABox.prototype.reconnect = function() {
  this.showReconnect(false);
  if (!this.session) {
    if (document.location.hash != '') {
      // A shellinaboxd daemon launched from a CGI only allows a single
      // session. In order to reconnect, we must reload the frame definition
      // and obtain a new port number. As this is a different origin, we
      // need to get enclosing page to help us.
      parent.location        = this.nextUrl;
    } else {
      if (this.url != this.nextUrl) {
        document.location.replace(this.nextUrl);
      } else {
        this.pendingKeys     = '';
        this.keysInFlight    = false;
        this.reset(true);
        this.sendRequest();
      }
    }
  }
  return false;
};

ShellInABox.prototype.sendRequest = function(request) {
  if (request == undefined) {
    request                  = new XMLHttpRequest();
  }
  request.open('POST', this.url + '?', true);
  request.timeout = 30000; // Don't leave POST pending forever: force 30s timeout to prevent HTTP Proxy thread hijack
  request.setRequestHeader('Cache-Control', 'no-cache');
  request.setRequestHeader('Content-Type',
                           'application/x-www-form-urlencoded; charset=utf-8');
  var content                = 'width=' + this.terminalWidth +
                               '&height=' + this.terminalHeight +
                               (this.session ? '&session=' +
                                encodeURIComponent(this.session) : '&rooturl='+
                                encodeURIComponent(this.rooturl));

  request.onreadystatechange = function(shellInABox) {
    return function() {
             try {
               return shellInABox.onReadyStateChange(request);
             } catch (e) {
               shellInABox.sessionClosed();
             }
           }
    }(this);
  request.send(content);
};

ShellInABox.prototype.onReadyStateChange = function(request) {
  if (request.readyState == 4 /* XHR_LOADED */) {
    if (request.status == 200) {
      this.connected = true;
      var response   = eval('(' + request.responseText + ')');
      if (response.data) {
        if (this.replayOnOutput) {
          this.messageReplay('output', response.data);
        }
        this.vt100(response.data);
      }

      if (!response.session ||
          this.session && this.session != response.session) {
        this.sessionClosed();
      } else {
        if (this.replayOnSession && !this.session && response.session) {
          this.messageReplay('session', 'alive');
        }
        this.session = response.session;
        this.sendRequest(request);
      }
    } else if (request.status == 0) {
      // Time Out or other connection problems: retry after 1s to prevent release CPU before retry
      setTimeout(function(shellInABox) {
        return function() { 
          shellInABox.sendRequest();
        };
      }(this), 1000);
    } else {
      this.sessionClosed();
    }
  }
};

ShellInABox.prototype.sendKeys = function(keys) {
  if (!this.connected) {
    return;
  }
  if (this.keysInFlight || this.session == undefined) {
    this.pendingKeys          += keys;
  } else {
    this.keysInFlight          = true;
    keys                       = this.pendingKeys + keys;
    this.pendingKeys           = '';
    var request                = new XMLHttpRequest();
    request.open('POST', this.url + '?', true);
    request.setRequestHeader('Cache-Control', 'no-cache');
    request.setRequestHeader('Content-Type',
                           'application/x-www-form-urlencoded; charset=utf-8');
    var content                = 'width=' + this.terminalWidth +
                                 '&height=' + this.terminalHeight +
                                 '&session=' +encodeURIComponent(this.session)+
                                 '&keys=' + encodeURIComponent(keys);
    request.onreadystatechange = function(shellInABox) {
      return function() {
               try {
                 return shellInABox.keyPressReadyStateChange(request);
               } catch (e) {
               }
             }
      }(this);
    request.send(content);
  }
};

ShellInABox.prototype.keyPressReadyStateChange = function(request) {
  if (request.readyState == 4 /* XHR_LOADED */) {
    this.keysInFlight = false;
    if (this.pendingKeys) {
      this.sendKeys('');
    }
  }
};

ShellInABox.prototype.keysPressed = function(ch) {
  var hex = '0123456789ABCDEF';
  var s   = '';
  for (var i = 0; i < ch.length; i++) {
    var c = ch.charCodeAt(i);
    if (c < 128) {
      s += hex.charAt(c >> 4) + hex.charAt(c & 0xF);
    } else if (c < 0x800) {
      s += hex.charAt(0xC +  (c >> 10)       ) +
           hex.charAt(       (c >>  6) & 0xF ) +
           hex.charAt(0x8 + ((c >>  4) & 0x3)) +
           hex.charAt(        c        & 0xF );
    } else if (c < 0x10000) {
      s += 'E'                                 +
           hex.charAt(       (c >> 12)       ) +
           hex.charAt(0x8 + ((c >> 10) & 0x3)) +
           hex.charAt(       (c >>  6) & 0xF ) +
           hex.charAt(0x8 + ((c >>  4) & 0x3)) +
           hex.charAt(        c        & 0xF );
    } else if (c < 0x110000) {
      s += 'F'                                 +
           hex.charAt(       (c >> 18)       ) +
           hex.charAt(0x8 + ((c >> 16) & 0x3)) +
           hex.charAt(       (c >> 12) & 0xF ) +
           hex.charAt(0x8 + ((c >> 10) & 0x3)) +
           hex.charAt(       (c >>  6) & 0xF ) +
           hex.charAt(0x8 + ((c >>  4) & 0x3)) +
           hex.charAt(        c        & 0xF );
    }
  }
  this.sendKeys(s);
};

ShellInABox.prototype.resized = function(w, h) {
  // Do not send a resize request until we are fully initialized.
  if (this.session) {
    // sendKeys() always transmits the current terminal size. So, flush all
    // pending keys.
    this.sendKeys('');
  }
};

ShellInABox.prototype.toggleSSL = function() {
  if (document.location.hash != '') {
    if (this.nextUrl.match(/\?plain$/)) {
      this.nextUrl    = this.nextUrl.replace(/\?plain$/, '');
    } else {
      this.nextUrl    = this.nextUrl.replace(/[?#].*/, '') + '?plain';
    }
    if (!this.session) {
      parent.location = this.nextUrl;
    }
  } else {
    this.nextUrl      = this.nextUrl.match(/^https:/)
           ? this.nextUrl.replace(/^https:/, 'http:').replace(/\/*$/, '/plain')
           : this.nextUrl.replace(/^http/, 'https').replace(/\/*plain$/, '');
  }
  if (this.nextUrl.match(/^[:]*:\/\/[^/]*$/)) {
    this.nextUrl     += '/';
  }
  if (this.session && this.nextUrl != this.url) {
    alert('This change will take effect the next time you login.');
  }
};

ShellInABox.prototype.extendContextMenu = function(entries, actions) {
  // Modify the entries and actions in place, adding any locally defined
  // menu entries.
  var oldActions            = [ ];
  for (var i = 0; i < actions.length; i++) {
    oldActions[i]           = actions[i];
  }
  for (var node = entries.firstChild, i = 0, j = 0; node;
       node = node.nextSibling) {
    if (node.tagName == 'LI') {
      actions[i++]          = oldActions[j++];
      if (node.id == "endconfig") {
        node.id             = '';
        if (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL &&
            !(typeof disableSSLMenu != 'undefined' && disableSSLMenu)) {
          // If the server supports both SSL and plain text connections,
          // provide a menu entry to switch between the two.
          var newNode       = document.createElement('li');
          var isSecure;
          if (document.location.hash != '') {
            isSecure        = !this.nextUrl.match(/\?plain$/);
          } else {
            isSecure        =  this.nextUrl.match(/^https:/);
          }
          newNode.innerHTML = (isSecure ? '&#10004; ' : '') + 'Secure';
          if (node.nextSibling) {
            entries.insertBefore(newNode, node.nextSibling);
          } else {
            entries.appendChild(newNode);
          }
          actions[i++]      = this.toggleSSL;
          node              = newNode;
        }
        node.id             = 'endconfig';
      }
    }
  }

};

ShellInABox.prototype.messageInit = function() {

  // Test if server option for iframe message passing was set.
  if (!serverMessagesOrigin) {
    return;
  }

  // Test for browser support of this feature. JSON class functionality is
  // also needed because some older IE browsers, support only string passing
  // and we don't want to use unsafe eval() function in this case.
  if (!window.postMessage || !window.JSON ||
      !window.JSON.parse  || !window.JSON.stringify) {
    return;
  }

  // Install event listener.
  if (window.addEventListener) {
    window.addEventListener('message', function(shellInABox) {
      return function(message) {
        shellInABox.messageReceive(message);
      }
    }(this), false);
  } else {
    // For IE8
    if (window.attachEvent) {
      window.attachEvent('onmessage', function(shellInABox) {
        return function(message) {
          shellInABox.messageReceive(message);
        }
      }(this));
    }
  }

  // After message mechanisms are in place "ready" message is sent to parent
  // window.
  parent.postMessage(JSON.stringify({type : 'ready', data : ''}), '*');
};

ShellInABox.prototype.messageReceive = function (message) {

  // Check for message origin if needed.
  if (serverMessagesOrigin !== "*") {
    if (serverMessagesOrigin !== message.origin) {
      return;
    }
  }

  // Remember replay information.
  if (!this.replaySource || !this.replayOrigin) {
    this.replaySource   = message.source;
    this.replayOrigin   = message.origin;
  }

  // Handle received message.
  var decoded           = JSON.parse(message.data);
  switch (decoded.type) {
  case 'input'  :
    // Input received data to terminal.
    this.keysPressed(decoded.data);
    break;
  case 'output' :
    // Enable, disable or toggle passing terminal output to parent window.
    switch (decoded.data) {
    case 'enable'  : this.replayOnOutput = true;                 break;
    case 'disable' : this.replayOnOutput = false;                break;
    case 'toggle'  : this.replayOnOutput = !this.replayOnOutput; break;
    }
    break;
  case 'session':
    // Replay with session status.
    this.messageReplay('session', this.session ? 'alive' : 'closed');
    break;
  case 'onsessionchange':
    // Enable, disable or toggle passing session status to parent window.
    switch (decoded.data) {
    case 'enable'  : this.replayOnSession = true;                  break;
    case 'disable' : this.replayOnSession = false;                 break;
    case 'toggle'  : this.replayOnSession = !this.replayOnSession; break;
    }
    break;
  case 'reconnect':
    this.reconnect();
    break;
  }
};

ShellInABox.prototype.messageReplay = function(type, data) {
  if (this.replaySource && this.replayOrigin) {
    var encoded = JSON.stringify({ type : type, data : data });
    this.replaySource.postMessage(encoded, this.replayOrigin);
  }
};

ShellInABox.prototype.about = function() {
  alert("Shell In A Box " + "2.20 " +
        "\n\n" +
        "Copyright 2008-2015 by Markus Gutschke. For more information visit\n" +
        "http://shellinabox.com or http://github.com/shellinabox/." +
        (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
         "\n\n" +
         "This product includes software developed by the OpenSSL Project for\n" +
         "use in the OpenSSL Toolkit. (http://www.openssl.org/)" +
         "\n\n" +
         "This product includes cryptographic software written by Eric Young\n" +
         "(eay@cryptsoft.com)" :
         ""));
};

/* vim: set filetype=javascript : */