diff --git a/misc/embedded.html b/misc/embedded.html new file mode 100644 index 0000000..ebdbc8c --- /dev/null +++ b/misc/embedded.html @@ -0,0 +1,192 @@ + + + + + + + + +

+ Embedded Shell In A Box example page. +

+ +

Controls:

+
+ + + + + +
+ +

Session status: ???

+ + + + + +

Terminal output:

+

+
+	
+
+
+
diff --git a/shellinabox/shell_in_a_box.jspp b/shellinabox/shell_in_a_box.jspp
index c7b61cb..952decc 100644
--- a/shellinabox/shell_in_a_box.jspp
+++ b/shellinabox/shell_in_a_box.jspp
@@ -109,12 +109,15 @@ function ShellInABox(url, container) {
   this.pendingKeys  = '';
   this.keysInFlight = false;
   this.connected    = false;
+  this.replayOutput = 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);
@@ -192,6 +195,9 @@ ShellInABox.prototype.onReadyStateChange = function(request) {
       this.connected = true;
       var response   = eval('(' + request.responseText + ')');
       if (response.data) {
+        if (this.replayOutput) {
+          this.messageReplay('output', response.data);
+        }
         this.vt100(response.data);
       }
 
@@ -360,6 +366,87 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
 
 };
 
+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));
+    }
+  }
+
+};
+
+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.
+    if (decoded.data === 'enable') {
+      this.replayOutput = true;
+    } else if (decoded.data === 'disable') {
+      this.replayOutput = false;
+    } else if (decoded.data === 'toggle') {
+      this.replayOutput = !this.replayOutput;
+    }
+    break;
+  case 'session':
+    // Replay with session status.
+    this.messageReplay('session', this.session ? 'alive' : 'closed');
+    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 version " + VERSION +
         "\nCopyright 2008-2010 by Markus Gutschke\n" +
diff --git a/shellinabox/shellinaboxd.c b/shellinabox/shellinaboxd.c
index edd5455..c6f150e 100644
--- a/shellinabox/shellinaboxd.c
+++ b/shellinabox/shellinaboxd.c
@@ -103,14 +103,15 @@
 static int            port;
 static int            portMin;
 static int            portMax;
-static int            localhostOnly = 0;
-static int            noBeep        = 0;
-static int            numericHosts  = 0;
-static int            enableSSL     = 1;
-static int            enableSSLMenu = 1;
-static int            linkifyURLs   = 1;
+static int            localhostOnly   = 0;
+static int            noBeep          = 0;
+static int            numericHosts    = 0;
+static int            enableSSL       = 1;
+static int            enableSSLMenu   = 1;
+static char           *messagesOrigin = NULL;
+static int            linkifyURLs     = 1;
 static char           *certificateDir;
-static int            certificateFd = -1;
+static int            certificateFd   = -1;
 static HashMap        *externalFiles;
 static Server         *cgiServer;
 static char           *cgiSessionKey;
@@ -677,11 +678,16 @@ static int shellInABoxHttpHandler(HttpConnection *http, void *arg,
                                          "disableSSLMenu    = %s;\n"
                                          "suppressAllAudio  = %s;\n"
                                          "linkifyURLs       = %d;\n"
-                                         "userCSSList       = %s;\n\n",
+                                         "userCSSList       = %s;\n"
+                                         "serverMessagesOrigin = %s%s%s;\n\n",
                                          enableSSL      ? "true" : "false",
                                          !enableSSLMenu ? "true" : "false",
                                          noBeep         ? "true" : "false",
-                                         linkifyURLs, userCSSString);
+                                         linkifyURLs,
+                                         userCSSString,
+                                         messagesOrigin ? "'" : "",
+                                         messagesOrigin ? messagesOrigin : "false",
+                                         messagesOrigin ? "'" : "");
     free(userCSSString);
     int stateVarsLength   = strlen(stateVars);
     int contentLength     = stateVarsLength +
@@ -773,6 +779,7 @@ static void usage(void) {
           "      --localhost-only        only listen on 127.0.0.1\n"
           "      --no-beep               suppress all audio output\n"
           "  -n, --numeric               do not resolve hostnames\n"
+          "  -m, --messages-origin=ORIGIN allow iframe message passing from origin\n"
           "      --pidfile=PIDFILE       publish pid of daemon process\n"
           "  -p, --port=PORT             select a port (default: %d)\n"
           "  -s, --service=SERVICE       define one or more services\n"
@@ -862,7 +869,7 @@ static void parseArgs(int argc, char * const argv[]) {
   check(cssStyleSheet      = strdup(stylesStart));
 
   for (;;) {
-    static const char optstring[] = "+hb::c:df:g:np:s:tqu:v";
+    static const char optstring[] = "+hb::c:df:g:nm:p:s:tqu:v";
     static struct option options[] = {
       { "help",             0, 0, 'h' },
       { "background",       2, 0, 'b' },
@@ -877,6 +884,7 @@ static void parseArgs(int argc, char * const argv[]) {
       { "localhost-only",   0, 0,  0  },
       { "no-beep",          0, 0,  0  },
       { "numeric",          0, 0, 'n' },
+      { "messages-origin",  1, 0, 'm' },
       { "pidfile",          1, 0,  0  },
       { "port",             1, 0, 'p' },
       { "service",          1, 0, 's' },
@@ -1054,6 +1062,15 @@ static void parseArgs(int argc, char * const argv[]) {
     } else if (!idx--) {
       // Numeric
       numericHosts         = 1;
+    } else if (!idx--) {
+      // Messages origin
+      if (messagesOrigin) {
+        fatal("Duplicated \"--messages-origin\" option.");
+      }
+      if (!optarg || !*optarg) {
+        fatal("Option \"--messages-origin\" expects an argument.");
+      }
+      check(messagesOrigin = strdup(optarg));
     } else if (!idx--) {
       // Pidfile
       if (cgi) {
@@ -1346,6 +1363,7 @@ int main(int argc, char * const argv[]) {
   free(services);
   free(certificateDir);
   free(cgiSessionKey);
+  free(messagesOrigin);
   if (pidfile) {
     // As a convenience, remove the pidfile, if it is still the version that
     // we wrote. In general, pidfiles are not expected to be incredibly