[Ovirt-devel] [PATCH server] updated anyterm/ovirt integration

Mohammed Morsi mmorsi at redhat.com
Mon Jul 20 20:07:50 UTC 2009


 host static anyterm content locally,
 url parameterize anyterm rows/cols/general param
 update spec/makefile

 Ideally I wanted and tried hard to put all this into a
 seperate ovirt-server--anyterm subpackage, but we cannot
 'reopen' the ovirt server virtual host defined in
 ovirt-server.conf to add the neccessary rewrite rules. it would
 be nice to figure out a way to do this
---
 Makefile.am            |    1 +
 anyterm/anyterm.css    |  132 ++++++++
 anyterm/anyterm.html   |   72 +++++
 anyterm/anyterm.js     |  799 ++++++++++++++++++++++++++++++++++++++++++++++++
 anyterm/copy.gif       |  Bin 0 -> 911 bytes
 anyterm/copy.png       |  Bin 0 -> 232 bytes
 anyterm/paste.gif      |  Bin 0 -> 148 bytes
 anyterm/paste.png      |  Bin 0 -> 225 bytes
 conf/ovirt-server.conf |   13 +-
 ovirt-server.spec.in   |    7 +
 scripts/ovirt-vm2node  |   13 +-
 11 files changed, 1021 insertions(+), 16 deletions(-)
 create mode 100644 anyterm/anyterm.css
 create mode 100644 anyterm/anyterm.html
 create mode 100644 anyterm/anyterm.js
 create mode 100644 anyterm/copy.gif
 create mode 100644 anyterm/copy.png
 create mode 100644 anyterm/paste.gif
 create mode 100644 anyterm/paste.png

diff --git a/Makefile.am b/Makefile.am
index f115c8f..a143663 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,7 @@ EXTRA_DIST =			\
   ovirt-server.spec.in	\
   scripts				\
   conf					\
+  anyterm               \
   src					\
   installer

diff --git a/anyterm/anyterm.css b/anyterm/anyterm.css
new file mode 100644
index 0000000..6bfea1f
--- /dev/null
+++ b/anyterm/anyterm.css
@@ -0,0 +1,132 @@
+/* browser/anyterm.css
+   This file is part of Anyterm; see http://anyterm.org/
+   (C) 2005-2008 Philip Endecott
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   any later version.
+
+   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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+/* These are the background colours: */
+.a { background-color: #000000; }  /* black   */
+.b { background-color: #cd0000; }  /* red     */
+.c { background-color: #00cd00; }  /* green   */
+.d { background-color: #cdcd00; }  /* yellow  */
+.e { background-color: #0000cd; }  /* blue    */
+.f { background-color: #cd00cd; }  /* magenta */
+.g { background-color: #00cdcd; }  /* cyan    */
+.h { background-color: #e5e5e5; }  /* white   */
+
+/* These are the foreground colours used when bold mode is NOT enabled.
+   They're the same as the background colours. */
+.i { color: #000000; }  /* black   */
+.j { color: #cd0000; }  /* red     */
+.k { color: #00cd00; }  /* green   */
+.l { color: #cdcd00; }  /* yellow  */
+.m { color: #0000cd; }  /* blue    */
+.n { color: #cd00cd; }  /* magenta */
+.o { color: #00cdcd; }  /* cyan    */
+.p { color: #e5e5e5; }  /* white   */
+
+/* These are the brighter foreground colours used when bold mode IS enabled.
+   The business with !important and .p .z is because the .p default is set
+   on the enclosing term element; I can't see a better way to get the desired
+   behaviour. */
+.z.i        { color: #4d4d4d !important; }  /* black   */
+.z.j        { color: #ff0000 !important; }  /* red     */
+.z.k        { color: #00ff00 !important; }  /* green   */
+.z.l        { color: #ffff00 !important; }  /* yellow  */
+.z.m        { color: #0000ff !important; }  /* blue    */
+.z.n        { color: #ff00ff !important; }  /* magenta */
+.z.o        { color: #00ffff !important; }  /* cyan    */
+.z.p, .p .z { color: #ffffff; }             /* white   */
+
+/* If you want a black-on-white colour scheme like xterm, rather than the
+   default white-on-black, you need to change the lines for black and white
+   above to something like the following:
+   .a { background-color: #ffffff; }
+   .h { background-color: #000000; }
+   .i { color: #e5e5e5; }
+   .p { color: #000000; }
+   .z.i        { color: #ffffff !important; }
+   .z.p, .p .z { color: #000000; }
+*/
+
+/* If the following rule is enabled, bold mode will actually use a bold font.
+   This is not a good idea in general as the bold font will probably be wider
+   than the normal font, messing up the layout, at least for some subset of
+   the character set.  So it's commented out; bold characters will be
+   distinguished only by their brighter colours (above) */
+/* .z { font-weight: bold; } */
+
+/* The cursor.  You can make it blink if you really want to (on some browsers). */
+.cursor {
+  border: 1px solid red;
+  margin: -1px;
+/*text-decoration: blink;*/
+}
+
+
+/* Properties for the page outside the terminal: */
+
+body {
+  background-color: white;
+  /* Don't like the white background?  How about this:
+     background-color: #222222;
+  */
+}
+
+noscript {
+  /* This is for the message that users see if they don't have Javascript
+     enabled.  We want it to be visible whatever the page background colour
+     (above) is set to, so we give it its own background colour. */
+  color: black;
+  background-color: white;
+}
+
+
+/* The remaining definitions determine the appearance of the frame around the
+   terminal, its title bar, and buttons: */
+
+.termframe {
+  float: left;
+  background-color: rgb(63,63,161);
+  padding: 0.4ex;
+}
+
+.termframe p {
+  margin: 0;
+  color: white;
+  font-weight: bold;
+  font-family: sans-serif;
+}
+
+.termframe a {
+  cursor: pointer;
+}
+
+img.button {
+  margin: 0 3px;
+  cursor: pointer;
+  vertical-align: top;
+}
+
+.term {
+  margin: 0.4ex 0 0 0;
+  padding: 1px;
+
+  overflow: auto;
+  overflow-x: visible;
+}
+
diff --git a/anyterm/anyterm.html b/anyterm/anyterm.html
new file mode 100644
index 0000000..bf063de
--- /dev/null
+++ b/anyterm/anyterm.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<!--
+browser/anyterm.html
+This file is part of Anyterm; see http://anyterm.org/
+(C) 2005-2007 Philip Endecott
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+any later version.
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+-->
+
+<html>
+<head>
+<title>Anyterm</title>
+
+<script type="text/javascript" src="anyterm.js">
+</script>
+
+<script type="text/javascript">
+  var rows = get_url_param("rows");
+  var columns = get_url_param("columns");
+  var general_param = get_url_param("param");
+
+  if(rows == ""){
+    rows = 25;
+  }
+
+  if(columns == ""){
+    columns = 80;
+  }
+
+  // To create the terminal, just call create_term.  The paramters are:
+  //  - The id of a <div> element that will become the terminal.
+  //  - The title.  %h and %v expand to the hostname and Anyterm version.
+  //  - The number of rows and columns.
+  //  - An optional parameter which is substituted for %p in the command string.
+  //  - An optional character set.
+  //  - An option number of lines of scrollback (default 0).
+  window.onload=function() {create_term("term","%h (Anyterm%v)",rows,columns,general_param,"",50);};
+
+  // When the user closes the terminal, by default they'll see a blank page.
+  // Generally you'll want to be more friendly than that.  If you set the
+  // variable on_close_goto_url to a URL, they'll be sent to that page after
+  // closing.  You could send them back to your home page, or something.
+  var on_close_goto_url = "";
+
+</script>
+
+<link rel="stylesheet" type="text/css" href="anyterm.css">
+
+</head>
+
+<body>
+
+<noscript>Javascript is essential for this page.  Please use a browser
+that supports Javascript.</noscript>
+
+<div id="term"></div>
+
+</body>
+</html>
diff --git a/anyterm/anyterm.js b/anyterm/anyterm.js
new file mode 100644
index 0000000..fc0aa18
--- /dev/null
+++ b/anyterm/anyterm.js
@@ -0,0 +1,799 @@
+// browser/anyterm.js
+// This file is part of Anyterm; see http://anyterm.org/
+// (C) 2005-2006 Philip Endecott
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// any later version.
+//
+// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+var undefined;
+
+var url_prefix = "";
+
+var frame;
+var term;
+var open=false;
+var session;
+
+var method="POST";
+//var method="GET";
+
+// Random sequence numbers are needed to prevent Opera from caching
+// replies
+
+var is_opera = navigator.userAgent.toLowerCase().indexOf("opera") != -1;
+if (is_opera) {
+  method="GET";
+}
+
+var seqnum_val=Math.round(Math.random()*100000);
+function cachebust() {
+  if (is_opera) {
+    seqnum_val++;
+    return "&x="+seqnum_val;
+  } else {
+    return "";
+  }
+}
+
+
+// Cross-platform creation of XMLHttpRequest object:
+
+function new_XMLHttpRequest() {
+  if (window.XMLHttpRequest) {
+    // For most browsers:
+    return new XMLHttpRequest();
+  } else {
+    // For IE, it's active-X voodoo.
+    // There are different versions in different browsers.
+    // The ones we try are the ones that Sarissa tried.  The disabled ones
+    // apparently also exist, but it seems to work OK without trying them.
+
+    //try{ return new ActiveXObject("MSXML3.XMLHTTP"); }   catch(e){}
+    try{ return new ActiveXObject("Msxml2.XMLHTTP.5.0"); } catch(e){}
+    try{ return new ActiveXObject("Msxml2.XMLHTTP.4.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("Msxml2.XMLHTTP"); }   catch(e){}
+    try{ return new ActiveXObject("Microsoft.XMLHTTP"); }  catch(e){}
+    throw new Error("Could not find an XMLHttpRequest active-X class.")
+  }
+}
+
+
+// Asynchronous and Synchronous XmlHttpRequest wrappers
+
+// AsyncLoader is a class; an instance specifies a callback function.
+// Call load to get something and the callback is invoked with the
+// returned document.
+
+function AsyncLoader(cb) {
+  this.callback = cb;
+  this.load =  function (url,query) {
+    var xmlhttp = new_XMLHttpRequest();
+    var cbk = this.callback;
+    //var timeoutID = window.setTimeout("alert('No response after 20 secs')",20000);
+    xmlhttp.onreadystatechange = function () {
+      if (xmlhttp.readyState==4) {
+	//window.clearTimeout(timeoutID);
+	if (xmlhttp.status==200) {
+	  cbk(xmlhttp.responseText);
+	} else {
+	  alert("Server returned status code "+xmlhttp.status+":\n"+xmlhttp.statusText);
+	  cbk(null);
+	}
+      }
+    }
+    if (method=="GET") {
+      xmlhttp.open(method, url+"?"+query, true);
+      xmlhttp.send(null);
+    } else if (method=="POST") {
+      xmlhttp.open(method, url, true);
+      xmlhttp.setRequestHeader('Content-Type',
+			       'application/x-www-form-urlencoded');
+      xmlhttp.send(query);
+    }
+
+  }
+}
+
+
+// Synchronous loader is a simple function
+
+function sync_load(url,query) {
+  var xmlhttp = new_XMLHttpRequest();
+  if (method=="GET") {
+    xmlhttp.open(method, url+"?"+query, false);
+    xmlhttp.send(null);
+  } else if (method=="POST") {
+    xmlhttp.open(method, url, false);
+    xmlhttp.setRequestHeader('Foo','1234');
+    xmlhttp.setRequestHeader('Content-Type',
+			     'application/x-www-form-urlencoded');
+    xmlhttp.send(query);
+  }
+  if (xmlhttp.status!=200) {
+    alert("Server returned status code "+xmlhttp.status+":\n"+xmlhttp.statusText);
+    return null;
+  }
+  return xmlhttp.responseText;
+}
+
+
+// Process error message from server:
+
+function handle_resp_error(resp) {
+  if (resp.charAt(0)=="E") {
+    var msg = resp.substr(1);
+    alert(msg);
+    return true;
+  }
+  return false;
+}
+
+
+// Receive channel:
+
+var rcv_loader;
+
+var disp="";
+
+
+
+function process_editscript(edscr) {
+
+  var ndisp="";
+
+  var i=0;
+  var dp=0;
+  while (i<edscr.length) {
+    var cmd=edscr.charAt(i);
+    i++;
+    var cp=edscr.indexOf(":",i);
+    var num=Number(edscr.substr(i,cp-i));
+    i=cp+1;
+    //alert("cmd="+cmd+" num="+num);
+    if (cmd=="d") {
+      dp+=num;
+    } else if (cmd=="k") {
+      ndisp+=disp.substr(dp,num);
+      dp+=num;
+    } else if (cmd=="i") {
+      //if (edscr.length<i+num) {
+	//alert("edit script ended early; expecting "+num+" but got only "+edscr.length-cp);
+      //}
+      ndisp+=edscr.substr(i,num);
+      i+=num;
+    }
+  }
+
+  return ndisp;
+}
+
+
+var visible_height_frac = 1;
+
+function display(edscr) {
+
+  //alert(edscr);
+
+  var ndisp;
+  if (edscr=="n") {
+    return;
+  } else if (edscr.charAt(0)=="R") {
+    ndisp = edscr.substr(1);
+  } else {
+    ndisp = process_editscript(edscr);
+  }
+
+  disp=ndisp;
+
+  term.innerHTML=ndisp;
+
+  if (visible_height_frac != 1) {
+    var termheight = visible_height_frac * term.scrollHeight;
+    term.style.height = termheight+"px";
+    term.scrollTop = term.scrollHeight;
+  }
+}
+
+
+function scrollterm(pages) {
+  term.scrollTop += pages * visible_height_frac * term.scrollHeight;
+}
+
+
+var rcv_timeout;
+
+function get() {
+  //alert("get");
+  rcv_loader.load(url_prefix+"anyterm-module","a=rcv&s="+session+cachebust());
+  rcv_timeout = window.setTimeout("alert('no response from server after 60 secs')",60000);
+}
+
+function rcv(resp) {
+  // Called asynchronously when the received document has returned
+  // from the server.
+
+  window.clearTimeout(rcv_timeout);
+
+  if (!open) {
+    return;
+  }
+
+  if (resp=="") {
+    // We seem to get this if the connection to the server fails.
+    alert("Connection to server failed");
+    return;
+  }
+
+  if (handle_resp_error(resp)) {
+    return;
+  }
+
+  display(resp);
+  get();
+}
+
+rcv_loader = new AsyncLoader(rcv);
+
+
+// Transmit channel:
+
+var kb_buf="";
+var send_loader;
+var send_in_progress=false;
+
+function send() {
+  send_in_progress=true;
+  send_loader.load(url_prefix+"anyterm-module",
+                   "a=send&s="+session+cachebust()+"&k="+encodeURIComponent(kb_buf));
+  kb_buf="";
+}
+
+function send_done(resp) {
+  send_in_progress=false;
+  if (handle_resp_error(resp)) {
+    return;
+  }
+  if (kb_buf!="") {
+    send();
+  }
+}
+
+send_loader = new AsyncLoader(send_done);
+
+
+function maybe_send() {
+  if (!send_in_progress && open && kb_buf!="") {
+    send();
+  }
+}
+
+
+function process_key(k) {
+//   alert("key="+k);
+//   return;
+  kb_buf+=k;
+  maybe_send();
+}
+
+
+function esc_seq(s) {
+  return String.fromCharCode(27)+"["+s;
+}
+
+
+function key_ev_stop(ev) {
+  // We want this key event to do absolutely nothing else.
+  ev.cancelBubble=true;
+  if (ev.stopPropagation) ev.stopPropagation();
+  if (ev.preventDefault)  ev.preventDefault();
+  try { ev.keyCode=0; } catch(e){}
+}
+
+function key_ev_supress(ev) {
+  // We want this keydown event to become a keypress event, but nothing else.
+  ev.cancelBubble=true;
+  if (ev.stopPropagation) ev.stopPropagation();
+}
+
+
+// When a key is pressed the browser delivers several events: typically first a keydown
+// event, then a keypress event, then a keyup event.  Ideally we'd just use the keypress
+// event, but there's a problem with that: the browser may not send a keypress event for
+// unusual keys such as function keys, control keys, cursor keys and so on.  The exact
+// behaviour varies between browsers and probably versions of browsers.
+//
+// So to get these keys we need to get the keydown events.  They have a couple of
+// problems.  Firstly, you get these events for things like pressing the shift key.
+// Secondly, unlike keypress events you don't get auto-repeat.
+
+function keypress(ev) {
+  if (!ev) var ev=window.event;
+
+  // Only handle "safe" characters here.  Anything unusual is ignored; it would
+  // have been handled earlier by the keydown function below.
+  if ((ev.ctrlKey && !ev.altKey)  // Ctrl is pressed (but not altgr, which is reported
+                                  // as ctrl+alt in at least some browsers).
+      || (ev.which==0)        // there's no key in the event; maybe a shift key?
+                              // (Mozilla sends which==0 && keyCode==0 when you press
+                              // the 'windows logo' key.)
+      || (ev.keyCode==8)      // backspace
+      || (ev.keyCode==16)) {  // shift; Opera sends this.
+    key_ev_stop(ev);
+    return false;
+  }
+
+  var kc;
+  if (ev.keyCode) kc=ev.keyCode;
+  if (ev.which)   kc=ev.which;
+
+  var k=String.fromCharCode(kc);
+
+  // When a key is pressed with ALT, we send ESC followed by the key's normal
+  // code.  But we don't want to do this when ALT-GR is pressed.
+  if (ev.altKey && !ev.ctrlKey) {
+    k = String.fromCharCode(27)+k;
+  }
+
+//     alert("keypress keyCode="+ev.keyCode+" which="+ev.which+
+//   	" shiftKey="+ev.shiftKey+" ctrlKey="+ev.ctrlKey+" altKey="+ev.altKey);
+
+  process_key(k);
+
+  key_ev_stop(ev);
+  return false;
+}
+
+
+function keydown(ev) {
+  if (!ev) var ev=window.event;
+
+  //  alert("keydown keyCode="+ev.keyCode+" which="+ev.which+
+  // 	" shiftKey="+ev.shiftKey+" ctrlKey="+ev.ctrlKey+" altKey="+ev.altKey);
+
+  var k;
+
+  var kc=ev.keyCode;
+
+  // Handle special keys.  We do this here because IE doesn't send
+  // keypress events for these (or at least some versions of IE don't for
+  // at least many of them).  This is unfortunate as it means that the
+  // cursor keys don't auto-repeat, even in browsers where that would be
+  // possible.  That could be improved.
+
+  // Interpret shift-pageup/down locally
+  if      (ev.shiftKey && kc==33) { scrollterm(-0.5); key_ev_stop(ev); return false; }
+  else if (ev.shiftKey && kc==34) { scrollterm(0.5);  key_ev_stop(ev); return false; }
+
+  else if (kc==33) k=esc_seq("5~");  // PgUp
+  else if (kc==34) k=esc_seq("6~");  // PgDn
+  else if (kc==35) k=esc_seq("4~");  // End
+  else if (kc==36) k=esc_seq("1~");  // Home
+  else if (kc==37) k=esc_seq("D");   // Left
+  else if (kc==38) k=esc_seq("A");   // Up
+  else if (kc==39) k=esc_seq("C");   // Right
+  else if (kc==40) k=esc_seq("B");   // Down
+  else if (kc==45) k=esc_seq("2~");  // Ins
+  else if (kc==46) k=esc_seq("3~");  // Del
+  else if (kc==27) k=String.fromCharCode(27); // Escape
+  else if (kc==9)  k=String.fromCharCode(9);  // Tab
+  else if (kc==8)  k=String.fromCharCode(8);  // Backspace
+  else if (kc==112) k=esc_seq(ev.shiftKey ? "25~" : "[A");  // F1
+  else if (kc==113) k=esc_seq(ev.shiftKey ? "26~" : "[B");  // F2
+  else if (kc==114) k=esc_seq(ev.shiftKey ? "28~" : "[C");  // F3
+  else if (kc==115) k=esc_seq(ev.shiftKey ? "29~" : "[D");  // F4
+  else if (kc==116) k=esc_seq(ev.shiftKey ? "31~" : "[E");  // F5
+  else if (kc==117) k=esc_seq(ev.shiftKey ? "32~" : "17~"); // F6
+  else if (kc==118) k=esc_seq(ev.shiftKey ? "33~" : "18~"); // F7
+  else if (kc==119) k=esc_seq(ev.shiftKey ? "34~" : "19~"); // F8
+  else if (kc==120) k=esc_seq("20~"); // F9
+  else if (kc==121) k=esc_seq("21~"); // F10
+  else if (kc==122) k=esc_seq("23~"); // F11
+  else if (kc==123) k=esc_seq("24~"); // F12
+
+  else {
+
+    // For most keys we'll stop now and let the subsequent keypress event
+    // process the key.  This has the advantage that auto-repeat will work.
+    // But we'll carry on here for control keys.
+    // Note that when altgr is pressed, the event reports ctrl and alt being
+    // pressed because it doesn't have a separate field for altgr.  We'll
+    // handle altgr in the keypress handler.
+    if (!ev.ctrlKey                   // ctrl not pressed
+        || (ev.ctrlKey && ev.altKey)  // altgr pressed
+        || (ev.keyCode==17)) {        // I think that if you press shift-control,
+                                      // you'll get an event with !ctrlKey && keyCode==17.
+      key_ev_supress(ev);
+      return;  // Note that we don't "return false" here, as we want the
+               // keypress handler to be invoked.
+    }
+
+    // OK, so now we're handling a ctrl key combination.
+
+    // There are some assumptions below about whether these symbols are shifted
+    // or not; does this work with different keyboards?
+    if (ev.shiftKey) {
+      if (kc==50) k=String.fromCharCode(0);        // Ctrl-@
+      else if (kc==54) k=String.fromCharCode(30);  // Ctrl-^, doesn't work
+      else if (kc==94) k=String.fromCharCode(30);  // Ctrl-^, doesn't work
+      else if (kc==109) k=String.fromCharCode(31); // Ctrl-_
+      else {
+	key_ev_supress(ev);
+	return;
+      }
+    } else {
+      if (kc>=65 && kc<=90) k=String.fromCharCode(kc-64); // Ctrl-A..Z
+      else if (kc==219) k=String.fromCharCode(27); // Ctrl-[
+      else if (kc==220) k=String.fromCharCode(28); // Ctrl-\   .
+      else if (kc==221) k=String.fromCharCode(29); // Ctrl-]
+      else if (kc==190) k=String.fromCharCode(30); // Since ctrl-^ doesn't work, map
+                                                   // ctrl-. to its code.
+      else if (kc==32)  k=String.fromCharCode(0);  // Ctrl-space sends 0, like ctrl- at .
+      else {
+	key_ev_supress(ev);
+	return;
+      }
+    }
+  }
+
+//   alert("keydown keyCode="+ev.keyCode+" which="+ev.which+
+// 	" shiftKey="+ev.shiftKey+" ctrlKey="+ev.ctrlKey+" altKey="+ev.altKey);
+
+  process_key(k);
+
+  key_ev_stop(ev);
+  return false;
+}
+
+
+// Open, close and initialisation:
+
+function open_term(rows,cols,p,charset,scrollback) {
+  var params = "a=open&rows="+rows+"&cols="+cols;
+  if (p) {
+    params += "&p="+p;
+  }
+  if (charset) {
+    params += "&ch="+charset;
+  }
+  if (scrollback) {
+    if (scrollback>1000) {
+      alert("The maximum scrollback is currently limited to 1000 lines.  "
+           +"Please choose a smaller value and try again.");
+      return;
+    }
+    params += "&sb="+scrollback;
+  }
+  params += cachebust();
+  var resp = sync_load(url_prefix+"anyterm-module",params);
+
+  if (handle_resp_error(resp)) {
+    return;
+  }
+
+  open=true;
+  session=resp;
+}
+
+function close_term() {
+  if (!open) {
+    alert("Connection is not open");
+    return;
+  }
+  open=false;
+  var resp = sync_load(url_prefix+"anyterm-module","a=close&s="+session+cachebust());
+  handle_resp_error(resp);  // If we get an error, we still close everything.
+  document.onkeypress=null;
+  document.onkeydown=null;
+  window.onbeforeunload=null;
+  var e;
+  while (e=frame.firstChild) {
+    frame.removeChild(e);
+  }
+  frame.className="";
+  if (on_close_goto_url) {
+    document.location = on_close_goto_url;
+  }
+}
+
+
+function get_anyterm_version() {
+  var svn_url="$URL: http://svn.anyterm.org/anyterm/tags/releases/1.1/1.1.29/browser/anyterm.js $";
+  var re = /releases\/[0-9]+\.[0-9]+\/([0-9\.]+)/;
+  var match = re.exec(svn_url);
+  if (match) {
+    return match[1];
+  } else {
+    return "";
+  }
+}
+
+function substitute_variables(s) {
+  var version = get_anyterm_version();
+  if (version!="") {
+    version="-"+version;
+  }
+  var hostname=document.location.host;
+  return s.replace(/%v/g,version).replace(/%h/g,hostname);
+}
+
+
+// Copying
+
+function copy_ie_clipboard() {
+  try {
+    window.document.execCommand("copy",false,null);
+  } catch (err) {
+    return undefined;
+  }
+  return 1;
+}
+
+function copy_mozilla_clipboard() {
+  // Thanks to Simon Wissinger for this function.
+
+  try {
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  } catch (err) {
+    return undefined;
+  }
+
+  var sel=window.getSelection();
+  var copytext=sel.toString();
+
+  var str=Components.classes["@mozilla.org/supports-string;1"]
+    .createInstance(Components.interfaces.nsISupportsString);
+  if (!str) return undefined;
+
+  str.data=copytext;
+
+  var trans=Components.classes["@mozilla.org/widget/transferable;1"]
+    .createInstance(Components.interfaces.nsITransferable);
+  if (!trans) return undefined;
+
+  trans.addDataFlavor("text/unicode");
+  trans.setTransferData("text/unicode", str, copytext.length * 2);
+
+  var clipid=Components.interfaces.nsIClipboard;
+
+  var clip=Components.classes["@mozilla.org/widget/clipboard;1"].getService(clipid);
+  if (!clip) return undefined;
+
+  clip.setData(trans, null, clipid.kGlobalClipboard);
+
+  return 1;
+}
+
+function copy_to_clipboard() {
+  var r=copy_ie_clipboard();
+  if (r==undefined) {
+    r=copy_mozilla_clipboard();
+  }
+  if (r==undefined) {
+    alert("Copy seems to be disabled; maybe you need to change your security settings?"
+         +"\n(Copy on the Edit menu will probably work)");
+  }
+}
+
+
+// Pasting
+
+function get_mozilla_clipboard() {
+  // This function is taken from
+  // http://www.nomorepasting.com/paste.php?action=getpaste&pasteID=41974&PHPSESSID=e6565dcf5de07256345e562b97ac9f46
+  // which does not indicate any particular copyright conditions.  It
+  // is a public forum, so one might conclude that it is public
+  // domain.
+
+  // IMHO it's disgraceful that Mozilla makes us use these 30 lines of
+  // undocumented gobledegook to do what IE does, and documents, with
+  // just 'window.clipboardData.getData("Text")'.  What on earth were
+  // they thinking?
+
+  try {
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  } catch (err) {
+    return undefined;
+  }
+
+  var clip = Components.classes["@mozilla.org/widget/clipboard;1"]
+    .createInstance(Components.interfaces.nsIClipboard);
+  if (!clip) {
+    return undefined;
+  }
+
+  var trans = Components.classes["@mozilla.org/widget/transferable;1"]
+    .createInstance(Components.interfaces.nsITransferable);
+  if (!trans) {
+    return undefined;
+  }
+
+  trans.addDataFlavor("text/unicode");
+  clip.getData(trans,clip.kGlobalClipboard);
+
+  var str=new Object();
+  var strLength=new Object();
+
+  try {
+    trans.getTransferData("text/unicode",str,strLength);
+  } catch(err) {
+    // One reason for getting here seems to be that nothing is selected
+    return "";
+  }
+
+  if (str) {
+    str=str.value.QueryInterface(Components.interfaces.nsISupportsString);
+  }
+
+  if (str) {
+    return str.data.substring(0,strLength.value / 2);
+  } else {
+    return "";  // ? is this "clipboard empty" or "cannot access"?
+  }
+}
+
+function get_ie_clipboard() {
+  if (window.clipboardData) {
+    return window.clipboardData.getData("Text");
+  }
+  return undefined;
+}
+
+function get_default_clipboard() {
+  return prompt("Paste into this box and press OK:","");
+}
+
+function paste_from_clipboard() {
+  var p = get_ie_clipboard();
+  if (p==undefined) {
+    p = get_mozilla_clipboard();
+  }
+  if (p==undefined) {
+    p = get_default_clipboard();
+    if (p) {
+      process_key(p);
+    }
+    return;
+  }
+
+  if (p=="") {
+    alert("The clipboard seems to be empty");
+    return;
+  }
+
+  if (confirm('Click OK to "type" the following into the terminal:\n'+p)) {
+    process_key(p);
+  }
+}
+
+
+function create_button(label,fn) {
+  var button=document.createElement("A");
+  var button_t=document.createTextNode("["+label+"] ");
+  button.appendChild(button_t);
+  button.onclick=fn;
+  return button;
+}
+
+function create_img_button(imgfn,label,fn) {
+  var button=document.createElement("A");
+  var button_img=document.createElement("IMG");
+  var class_attr=document.createAttribute("CLASS");
+  class_attr.value="button";
+  button_img.setAttributeNode(class_attr);
+  var src_attr=document.createAttribute("SRC");
+  src_attr.value=imgfn;
+  button_img.setAttributeNode(src_attr);
+  var alt_attr=document.createAttribute("ALT");
+  alt_attr.value="["+label+"] ";
+  button_img.setAttributeNode(alt_attr);
+  var title_attr=document.createAttribute("TITLE");
+  title_attr.value=label;
+  button_img.setAttributeNode(title_attr);
+  button.appendChild(button_img);
+  button.onclick=fn;
+  return button;
+}
+
+function create_term(elem_id,title,rows,cols,p,charset,scrollback) {
+  if (open) {
+    alert("Terminal is already open");
+    return;
+  }
+  title=substitute_variables(title);
+  frame=document.getElementById(elem_id);
+  if (!frame) {
+    alert("There is no element named '"+elem_id+"' in which to build a terminal");
+    return;
+  }
+  frame.className="termframe";
+  var title_p=document.createElement("P");
+  title_p.appendChild(create_img_button("copy.gif","Copy",copy_to_clipboard));
+  title_p.appendChild(create_img_button("paste.gif","Paste",paste_from_clipboard));
+  title_p.appendChild(create_ctrlkey_menu());
+  var title_t=document.createTextNode(" "+title+" ");
+  title_p.appendChild(title_t);
+  title_p.appendChild(create_button("close",close_term));
+  frame.appendChild(title_p);
+  term=document.createElement("PRE");
+  frame.appendChild(term);
+  term.className="term a p";
+  var termbody=document.createTextNode("");
+  term.appendChild(termbody);
+  visible_height_frac=Number(rows)/(Number(rows)+Number(scrollback));
+  if (scrollback>0) {
+    term.style.overflowY="scroll";
+  }
+  document.onhelp = function() { return false; };
+  document.onkeypress=keypress;
+  document.onkeydown=keydown;
+  open_term(rows,cols,p,charset,scrollback);
+  if (open) {
+    window.onbeforeunload=warn_unload;
+    get();
+    maybe_send();
+  }
+}
+
+
+function warn_unload() {
+  if (open) {
+    return "Leaving this page will close the terminal.";
+  }
+}
+
+
+function create_ctrlkey_menu() {
+  var sel=document.createElement("SELECT");
+  create_ctrlkey_menu_entry(sel,"Control keys...",-1);
+  create_ctrlkey_menu_entry(sel,"Ctrl-@",0);
+  for (var code=1; code<27; code++) {
+    var letter=String.fromCharCode(64+code);
+    create_ctrlkey_menu_entry(sel,"Ctrl-"+letter,code);
+  }
+  create_ctrlkey_menu_entry(sel,"Ctrl-[",27);
+  create_ctrlkey_menu_entry(sel,"Ctrl-\\",28);
+  create_ctrlkey_menu_entry(sel,"Ctrl-]",29);
+  create_ctrlkey_menu_entry(sel,"Ctrl-^",30);
+  create_ctrlkey_menu_entry(sel,"Ctrl-_",31);
+  sel.onchange=function() {
+    var code = sel.options[sel.selectedIndex].value;
+    if (code>=0) {
+      process_key(String.fromCharCode(code));
+    }
+  };
+  return sel;
+}
+
+function create_ctrlkey_menu_entry(sel,name,code) {
+  var opt=document.createElement("OPTION");
+  opt.appendChild(document.createTextNode(name));
+  var value_attr=document.createAttribute("VALUE");
+  value_attr.value=code;
+  opt.setAttributeNode(value_attr);
+  sel.appendChild(opt);
+}
+
+function get_url_param( name )
+{
+  var regexS = "[\\?&]"+name+"=([^&#]*)";
+  var regex = new RegExp( regexS );
+  var results = regex.exec( window.location.href );
+  if( results == null )
+    return "";
+  else
+    return results[1];
+}
diff --git a/anyterm/copy.gif b/anyterm/copy.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2ab719e349efd9e003b4b420449c68fecea004ed
GIT binary patch
literal 911
zcmV;A191FDNk%w1VG;lq0QUd at 000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c
z3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM
z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C at 3f?DJd!{Dl021EG#T7
zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}?
zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy
zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy at _IT3cINTwGjTU0q&YUSD5dU|?Wj
zVPRroVq;@tWMpJzWo2e&W at l$-XlQ6 at X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T
za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm at et&;|fPjF3fq{a8f`fyD
zgoK2Jg at uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8 at l$4Z}
zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5(
zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p
zxVX5vxw*Q!y1To(yu7@<y}iD^zQ4b}z`(%4!NJ19!o$PE#KgqK#l^<P#>dCU$jHda
z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1<F+1c9K
z+S}XP+}zyV-QC{a-rwKf;Nall;o;)q;^X7v<mBY#<>lt)=I7_<=;-L_>FMg~>g((4
z?Ck9A?d|UF?(gsK at bK{Q@$vHV^7Hfa^z`)g_4W4l_V at Sq`1ttw`T6?#`uqF){QUg=
z{r&#_{{R2~A^8LW006%LEC2ui01^Ne000PV0DlP_NU-2Q00<K%G<fiz!-xM6GW-Pq
z;zIzL05GhmkmJIF6)i&42r^<qizEq#6zMQ!!;u|7QZ(6;rNo&dZDz!I(d5aFC|O=y
lSyLg(m_$Xo6qppHL61NqJ{8(>D%GVtZCVwI6=}zS06UUJu*?7e

literal 0
HcmV?d00001

diff --git a/anyterm/copy.png b/anyterm/copy.png
new file mode 100644
index 0000000000000000000000000000000000000000..1cdd8c1de2770d63ed9d9ae80ed2e2e44216598b
GIT binary patch
literal 232
zcmeAS at N?(olHy`uVBq!ia0vp^LO?9W!2%@H!&puMDW)WEcNd2L?fqx=19_YU9+AZi
z4BWyX%*Zfnjs#GUy~NYkmHiqsx0s>WCWE6%K%q)c7sn8d^IL;X3La44U`dzWd?qA0
z^GRY4i`f&=I6j>>hOZ{P588M`%F97aO7hyLkIuU$dRG_j|7&_?qQwm*UWHnQqJ>gH
z<tKkHeIu5qvPIN=c1eDVai7ES#}0Q=3l*$SCb;<f$kzP1+hrg*cYidO{=QT56+$+J
a&z*HdEg_w2**~Bo7(8A5T-G at yGywp=dQPqY

literal 0
HcmV?d00001

diff --git a/anyterm/paste.gif b/anyterm/paste.gif
new file mode 100644
index 0000000000000000000000000000000000000000..65ddba8bacc994f6347e950a5f73182c141ca7b8
GIT binary patch
literal 148
zcmZ?wbhEHb6k!l!_{7Wr1aoH1dH($Qe=q>?!9ejR3nK#q3xf`b2U5$x9O<!(>wtlo
z;=CNa at C}<)9TeDPQri}{PKujtmaKF9RnGngwby^0xx7})hfPAF-|hH>HkUTJ)mIcO
pzW$gMas2A!-mb2Q<<mvP@|zR3EUx)ua3JS9Q{1%VD>GRbtO4Q=IkW%(

literal 0
HcmV?d00001

diff --git a/anyterm/paste.png b/anyterm/paste.png
new file mode 100644
index 0000000000000000000000000000000000000000..034debdbf7033e46e9d7d45e239a664a7f92d9fb
GIT binary patch
literal 225
zcmV<703QE|P)<h;3K|Lk000e1NJLTq000yK000&U1^@s6z(KqQ0001}Nkl<ZILpnI
zVG6 at A2!)@cm+*d}hbq0F$8dilG>MCL3v>^Jg7)<@CMgh0YotVxnTP-Ym^o8FP&6ST
z?z0mOp`ORtxR<@a$a*m|yn3VG`WI at _T69(RGMJhM!%u}-y|7sSZ at A5pHlSsO0H8WQ
zBt*2n<+?L8Md(rV^p~a4l;N!HYmSJ%Bb<HUXToRqVKfwX9i!X$PP;Y~3NGGf&vN)r
bxoh|a{{&3{JLUuL00000NkvXXu0mjfLOWi3

literal 0
HcmV?d00001

diff --git a/conf/ovirt-server.conf b/conf/ovirt-server.conf
index 0fb4b94..e4ebd5b 100644
--- a/conf/ovirt-server.conf
+++ b/conf/ovirt-server.conf
@@ -23,15 +23,11 @@ NameVirtualHost AdminNetIpAddress:80

   RewriteEngine On
   RewriteMap vmnodes prg:/usr/bin/ovirt-vm2node
-  RewriteRule ^/terminal/(.*)/(.*\.(js|css|gif))$ http://${vmnodes:anyterm}:81/$2 [P]
-  RewriteRule ^/terminal/(.*)/proxy/anyterm-module$ http://${vmnodes:$1}:81/$1 [P]
-  RewriteRule ^/terminal/(.*)/$ http://${vmnodes:$1}:81/anyterm.html?param=$1 [P,NE]
-
-
+  RewriteRule ^/terminal/(.+)/anyterm-module$ http://${vmnodes:$1}:81/anyterm-module [P]
+  RewriteRule ^/terminal/(.+)/(.*\.(html|js|css|gif))*$ http://127.0.0.1/terminal/$2 [P,NE]

   ProxyPass /ovirt http://AdminNodeFQDN/ovirt retry=3
   ProxyPassReverse /ovirt http://AdminNodeFQDN/ovirt
-
 </VirtualHost>

 <VirtualHost AdminNetIpAddress:80>
@@ -85,3 +81,8 @@ ProxyPassReverse /ovirt/stylesheets !
 ProxyPassReverse /ovirt/errors !

 </VirtualHost>
+
+Alias /terminal /usr/share/ovirt-anyterm
+<Location /terminal>
+  DirectoryIndex anyterm.html
+</Location>
diff --git a/ovirt-server.spec.in b/ovirt-server.spec.in
index 1bf73c7..d762178 100644
--- a/ovirt-server.spec.in
+++ b/ovirt-server.spec.in
@@ -150,6 +150,11 @@ touch %{buildroot}%{_localstatedir}/log/%{name}/db-omatic.log
 %{__cp} -pr %{pbuild}/installer/appliances %{buildroot}/%{acehome}
 %{__cp} -pr %{pbuild}/installer/bin/ovirt-installer %{buildroot}%{_sbindir}

+# setup the anyterm config
+%{__mkdir} -p %{buildroot}%{_datadir}/ovirt-anyterm/
+for f in anyterm/*.{html,css,js,png,gif}; do
+   %{__install} -m644 "$f" %{buildroot}%{_datadir}/ovirt-anyterm/
+done

 %clean
 rm -rf $RPM_BUILD_ROOT
@@ -244,11 +249,13 @@ fi
 %config(noreplace) %{_sysconfdir}/%{name}/development.rb
 %config(noreplace) %{_sysconfdir}/%{name}/production.rb
 %config(noreplace) %{_sysconfdir}/%{name}/test.rb
+%{_datadir}/ovirt-anyterm

 %files installer
 %{_sbindir}/ovirt-installer
 %{acehome}

+
 %changelog
 * Thu May 29 2008 Alan Pevec <apevec at redhat.com> - 0.0.5-0
 - use rubygem-krb5-auth
diff --git a/scripts/ovirt-vm2node b/scripts/ovirt-vm2node
index 1d6104c..01e25bb 100755
--- a/scripts/ovirt-vm2node
+++ b/scripts/ovirt-vm2node
@@ -9,17 +9,10 @@ require 'dutils'
 $stdin.each{ |vmname| # get vm name from stdin
   begin
     vmname.chomp!  # remove the newline
-
-    # specially handle 'anyterm' to just return
-    # first host (for css/js/etc which aren't
-    # vm dependent)
-    if vmname == 'anyterm'
-      puts Host.find(:first, :conditions => "state = 'available'").hostname
-    else
-      puts Vm.find(:first, :conditions => ['description = ?', vmname]).host.hostname
-    end
+    puts Vm.find(:first, :conditions => ['description = ?', vmname]).host.hostname
   rescue Exception => e
-      puts
+    puts "Error looking node with VM: #{ex.class}: #{ex.message}"
+    puts ex.backtrace
   end

   $stdout.flush
--
1.6.0.6




More information about the ovirt-devel mailing list