| #!/usr/bin/python | |
| ##!/usr/bin/env python | |
| """CGI shell server | |
| This exposes a shell terminal on a web page. | |
| It uses AJAX to send keys and receive screen updates. | |
| The client web browser needs nothing but CSS and Javascript. | |
| --hostname : sets the remote host name to open an ssh connection to. | |
| --username : sets the user name to login with | |
| --password : (optional) sets the password to login with | |
| --port : set the local port for the server to listen on | |
| --watch : show the virtual screen after each client request | |
| This project is probably not the most security concious thing I've ever built. | |
| This should be considered an experimental tool -- at best. | |
| """ | |
| import sys,os | |
| sys.path.insert (0,os.getcwd()) # let local modules precede any installed modules | |
| import socket, random, string, traceback, cgi, time, getopt, getpass, threading, resource, signal | |
| import pxssh, pexpect, ANSI | |
| def exit_with_usage(exit_code=1): | |
| print globals()['__doc__'] | |
| os._exit(exit_code) | |
| def client (command, host='localhost', port=-1): | |
| """This sends a request to the server and returns the response. | |
| If port <= 0 then host is assumed to be the filename of a Unix domain socket. | |
| If port > 0 then host is an inet hostname. | |
| """ | |
| if port <= 0: | |
| s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | |
| s.connect(host) | |
| else: | |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| s.connect((host, port)) | |
| s.send(command) | |
| data = s.recv (2500) | |
| s.close() | |
| return data | |
| def server (hostname, username, password, socket_filename='/tmp/server_sock', daemon_mode = True, verbose=False): | |
| """This starts and services requests from a client. | |
| If daemon_mode is True then this forks off a separate daemon process and returns the daemon's pid. | |
| If daemon_mode is False then this does not return until the server is done. | |
| """ | |
| if daemon_mode: | |
| mypid_name = '/tmp/%d.pid' % os.getpid() | |
| daemon_pid = daemonize(daemon_pid_filename=mypid_name) | |
| time.sleep(1) | |
| if daemon_pid != 0: | |
| os.unlink(mypid_name) | |
| return daemon_pid | |
| virtual_screen = ANSI.ANSI (24,80) | |
| child = pxssh.pxssh() | |
| try: | |
| child.login (hostname, username, password, login_naked=True) | |
| except: | |
| return | |
| if verbose: print 'login OK' | |
| virtual_screen.write (child.before) | |
| virtual_screen.write (child.after) | |
| if os.path.exists(socket_filename): os.remove(socket_filename) | |
| s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | |
| s.bind(socket_filename) | |
| os.chmod(socket_filename, 0777) | |
| if verbose: print 'Listen' | |
| s.listen(1) | |
| r = roller (endless_poll, (child, child.PROMPT, virtual_screen)) | |
| r.start() | |
| if verbose: print "started screen-poll-updater in background thread" | |
| sys.stdout.flush() | |
| try: | |
| while True: | |
| conn, addr = s.accept() | |
| if verbose: print 'Connected by', addr | |
| data = conn.recv(1024) | |
| request = data.split(' ', 1) | |
| if len(request)>1: | |
| cmd = request[0].strip() | |
| arg = request[1].strip() | |
| else: | |
| cmd = request[0].strip() | |
| arg = '' | |
| if cmd == 'exit': | |
| r.cancel() | |
| break | |
| elif cmd == 'sendline': | |
| child.sendline (arg) | |
| time.sleep(0.1) | |
| shell_window = str(virtual_screen) | |
| elif cmd == 'send' or cmd=='xsend': | |
| if cmd=='xsend': | |
| arg = arg.decode("hex") | |
| child.send (arg) | |
| time.sleep(0.1) | |
| shell_window = str(virtual_screen) | |
| elif cmd == 'cursor': | |
| shell_window = '%x,%x' % (virtual_screen.cur_r, virtual_screen.cur_c) | |
| elif cmd == 'refresh': | |
| shell_window = str(virtual_screen) | |
| elif cmd == 'hash': | |
| shell_window = str(hash(str(virtual_screen))) | |
| response = [] | |
| response.append (shell_window) | |
| if verbose: print '\n'.join(response) | |
| sent = conn.send('\n'.join(response)) | |
| if sent < len (response): | |
| if verbose: print "Sent is too short. Some data was cut off." | |
| conn.close() | |
| except e: | |
| pass | |
| r.cancel() | |
| if verbose: print "cleaning up socket" | |
| s.close() | |
| if os.path.exists(socket_filename): os.remove(socket_filename) | |
| if verbose: print "server done!" | |
| class roller (threading.Thread): | |
| """This class continuously loops a function in a thread. | |
| This is basically a thin layer around Thread with a | |
| while loop and a cancel. | |
| """ | |
| def __init__(self, function, args=[], kwargs={}): | |
| threading.Thread.__init__(self) | |
| self.function = function | |
| self.args = args | |
| self.kwargs = kwargs | |
| self.finished = threading.Event() | |
| def cancel(self): | |
| """Stop the roller.""" | |
| self.finished.set() | |
| def run(self): | |
| while not self.finished.isSet(): | |
| self.function(*self.args, **self.kwargs) | |
| def endless_poll (child, prompt, screen, refresh_timeout=0.1): | |
| """This keeps the screen updated with the output of the child. | |
| This will be run in a separate thread. See roller class. | |
| """ | |
| #child.logfile_read = screen | |
| try: | |
| s = child.read_nonblocking(4000, 0.1) | |
| screen.write(s) | |
| except: | |
| pass | |
| def daemonize (stdin=None, stdout=None, stderr=None, daemon_pid_filename=None): | |
| """This runs the current process in the background as a daemon. | |
| The arguments stdin, stdout, stderr allow you to set the filename that the daemon reads and writes to. | |
| If they are set to None then all stdio for the daemon will be directed to /dev/null. | |
| If daemon_pid_filename is set then the pid of the daemon will be written to it as plain text | |
| and the pid will be returned. If daemon_pid_filename is None then this will return None. | |
| """ | |
| UMASK = 0 | |
| WORKINGDIR = "/" | |
| MAXFD = 1024 | |
| # The stdio file descriptors are redirected to /dev/null by default. | |
| if hasattr(os, "devnull"): | |
| DEVNULL = os.devnull | |
| else: | |
| DEVNULL = "/dev/null" | |
| if stdin is None: stdin = DEVNULL | |
| if stdout is None: stdout = DEVNULL | |
| if stderr is None: stderr = DEVNULL | |
| try: | |
| pid = os.fork() | |
| except OSError, e: | |
| raise Exception, "%s [%d]" % (e.strerror, e.errno) | |
| if pid != 0: # The first child. | |
| os.waitpid(pid,0) | |
| if daemon_pid_filename is not None: | |
| daemon_pid = int(file(daemon_pid_filename,'r').read()) | |
| return daemon_pid | |
| else: | |
| return None | |
| # first child | |
| os.setsid() | |
| signal.signal(signal.SIGHUP, signal.SIG_IGN) | |
| try: | |
| pid = os.fork() # fork second child | |
| except OSError, e: | |
| raise Exception, "%s [%d]" % (e.strerror, e.errno) | |
| if pid != 0: | |
| if daemon_pid_filename is not None: | |
| file(daemon_pid_filename,'w').write(str(pid)) | |
| os._exit(0) # exit parent (the first child) of the second child. | |
| # second child | |
| os.chdir(WORKINGDIR) | |
| os.umask(UMASK) | |
| maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] | |
| if maxfd == resource.RLIM_INFINITY: | |
| maxfd = MAXFD | |
| # close all file descriptors | |
| for fd in xrange(0, maxfd): | |
| try: | |
| os.close(fd) | |
| except OSError: # fd wasn't open to begin with (ignored) | |
| pass | |
| os.open (DEVNULL, os.O_RDWR) # standard input | |
| # redirect standard file descriptors | |
| si = open(stdin, 'r') | |
| so = open(stdout, 'a+') | |
| se = open(stderr, 'a+', 0) | |
| os.dup2(si.fileno(), sys.stdin.fileno()) | |
| os.dup2(so.fileno(), sys.stdout.fileno()) | |
| os.dup2(se.fileno(), sys.stderr.fileno()) | |
| return 0 | |
| def client_cgi (): | |
| """This handles the request if this script was called as a cgi. | |
| """ | |
| sys.stderr = sys.stdout | |
| ajax_mode = False | |
| TITLE="Shell" | |
| SHELL_OUTPUT="" | |
| SID="NOT" | |
| print "Content-type: text/html;charset=utf-8\r\n" | |
| try: | |
| form = cgi.FieldStorage() | |
| if form.has_key('ajax'): | |
| ajax_mode = True | |
| ajax_cmd = form['ajax'].value | |
| SID=form['sid'].value | |
| if ajax_cmd == 'send': | |
| command = 'xsend' | |
| arg = form['arg'].value.encode('hex') | |
| result = client (command + ' ' + arg, '/tmp/'+SID) | |
| print result | |
| elif ajax_cmd == 'refresh': | |
| command = 'refresh' | |
| result = client (command, '/tmp/'+SID) | |
| print result | |
| elif ajax_cmd == 'cursor': | |
| command = 'cursor' | |
| result = client (command, '/tmp/'+SID) | |
| print result | |
| elif ajax_cmd == 'exit': | |
| command = 'exit' | |
| result = client (command, '/tmp/'+SID) | |
| print result | |
| elif ajax_cmd == 'hash': | |
| command = 'hash' | |
| result = client (command, '/tmp/'+SID) | |
| print result | |
| elif not form.has_key('sid'): | |
| SID=random_sid() | |
| print LOGIN_HTML % locals(); | |
| else: | |
| SID=form['sid'].value | |
| if form.has_key('start_server'): | |
| USERNAME = form['username'].value | |
| PASSWORD = form['password'].value | |
| dpid = server ('127.0.0.1', USERNAME, PASSWORD, '/tmp/'+SID) | |
| SHELL_OUTPUT="daemon pid: " + str(dpid) | |
| else: | |
| if form.has_key('cli'): | |
| command = 'sendline ' + form['cli'].value | |
| else: | |
| command = 'sendline' | |
| SHELL_OUTPUT = client (command, '/tmp/'+SID) | |
| print CGISH_HTML % locals() | |
| except: | |
| tb_dump = traceback.format_exc() | |
| if ajax_mode: | |
| print str(tb_dump) | |
| else: | |
| SHELL_OUTPUT=str(tb_dump) | |
| print CGISH_HTML % locals() | |
| def server_cli(): | |
| """This is the command line interface to starting the server. | |
| This handles things if the script was not called as a CGI | |
| (if you run it from the command line). | |
| """ | |
| try: | |
| optlist, args = getopt.getopt(sys.argv[1:], 'h?d', ['help','h','?', 'hostname=', 'username=', 'password=', 'port=', 'watch']) | |
| except Exception, e: | |
| print str(e) | |
| exit_with_usage() | |
| command_line_options = dict(optlist) | |
| options = dict(optlist) | |
| # There are a million ways to cry for help. These are but a few of them. | |
| if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]: | |
| exit_with_usage(0) | |
| hostname = "127.0.0.1" | |
| #port = 1664 | |
| username = os.getenv('USER') | |
| password = "" | |
| daemon_mode = False | |
| if '-d' in options: | |
| daemon_mode = True | |
| if '--watch' in options: | |
| watch_mode = True | |
| else: | |
| watch_mode = False | |
| if '--hostname' in options: | |
| hostname = options['--hostname'] | |
| if '--port' in options: | |
| port = int(options['--port']) | |
| if '--username' in options: | |
| username = options['--username'] | |
| if '--password' in options: | |
| password = options['--password'] | |
| else: | |
| password = getpass.getpass('password: ') | |
| server (hostname, username, password, '/tmp/mysock', daemon_mode) | |
| def random_sid (): | |
| a=random.randint(0,65535) | |
| b=random.randint(0,65535) | |
| return '%04x%04x.sid' % (a,b) | |
| def parse_host_connect_string (hcs): | |
| """This parses a host connection string in the form | |
| username:password@hostname:port. All fields are options expcet hostname. A | |
| dictionary is returned with all four keys. Keys that were not included are | |
| set to empty strings ''. Note that if your password has the '@' character | |
| then you must backslash escape it. | |
| """ | |
| if '@' in hcs: | |
| p = re.compile (r'(?P<username>[^@:]*)(:?)(?P<password>.*)(?!\\)@(?P<hostname>[^:]*):?(?P<port>[0-9]*)') | |
| else: | |
| p = re.compile (r'(?P<username>)(?P<password>)(?P<hostname>[^:]*):?(?P<port>[0-9]*)') | |
| m = p.search (hcs) | |
| d = m.groupdict() | |
| d['password'] = d['password'].replace('\\@','@') | |
| return d | |
| def pretty_box (s, rows=24, cols=80): | |
| """This puts an ASCII text box around the given string. | |
| """ | |
| top_bot = '+' + '-'*cols + '+\n' | |
| return top_bot + '\n'.join(['|'+line+'|' for line in s.split('\n')]) + '\n' + top_bot | |
| def main (): | |
| if os.getenv('REQUEST_METHOD') is None: | |
| server_cli() | |
| else: | |
| client_cgi() | |
| # It's mostly HTML and Javascript from here on out. | |
| CGISH_HTML="""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | |
| <html> | |
| <head> | |
| <title>%(TITLE)s %(SID)s</title> | |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> | |
| <style type=text/css> | |
| a {color: #9f9; text-decoration: none} | |
| a:hover {color: #0f0} | |
| hr {color: #0f0} | |
| html,body,textarea,input,form | |
| { | |
| font-family: "Courier New", Courier, mono; | |
| font-size: 8pt; | |
| color: #0c0; | |
| background-color: #020; | |
| margin:0; | |
| padding:0; | |
| border:0; | |
| } | |
| input { background-color: #010; } | |
| textarea { | |
| border-width:1; | |
| border-style:solid; | |
| border-color:#0c0; | |
| padding:3; | |
| margin:3; | |
| } | |
| </style> | |
| <script language="JavaScript"> | |
| function focus_first() | |
| {if (document.forms.length > 0) | |
| {var TForm = document.forms[0]; | |
| for (i=0;i<TForm.length;i++){ | |
| if ((TForm.elements[i].type=="text")|| | |
| (TForm.elements[i].type=="textarea")|| | |
| (TForm.elements[i].type.toString().charAt(0)=="s")) | |
| {document.forms[0].elements[i].focus();break;}}}} | |
| // JavaScript Virtual Keyboard | |
| // If you like this code then buy me a sandwich. | |
| // Noah Spurrier <noah@noah.org> | |
| var flag_shift=0; | |
| var flag_shiftlock=0; | |
| var flag_ctrl=0; | |
| var ButtonOnColor="#ee0"; | |
| function init () | |
| { | |
| // hack to set quote key to show both single quote and double quote | |
| document.form['quote'].value = "'" + ' "'; | |
| //refresh_screen(); | |
| poll(); | |
| document.form["cli"].focus(); | |
| } | |
| function get_password () | |
| { | |
| var username = prompt("username?",""); | |
| var password = prompt("password?",""); | |
| start_server (username, password); | |
| } | |
| function multibrowser_ajax () | |
| { | |
| var xmlHttp = false; | |
| /*@cc_on @*/ | |
| /*@if (@_jscript_version >= 5) | |
| try | |
| { | |
| xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); | |
| } | |
| catch (e) | |
| { | |
| try | |
| { | |
| xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); | |
| } | |
| catch (e2) | |
| { | |
| xmlHttp = false; | |
| } | |
| } | |
| @end @*/ | |
| if (!xmlHttp && typeof XMLHttpRequest != 'undefined') | |
| { | |
| xmlHttp = new XMLHttpRequest(); | |
| } | |
| return xmlHttp; | |
| } | |
| function load_url_to_screen(url) | |
| { | |
| xmlhttp = multibrowser_ajax(); | |
| //window.XMLHttpRequest?new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP"); | |
| xmlhttp.onreadystatechange = update_virtual_screen; | |
| xmlhttp.open("GET", url); | |
| xmlhttp.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"); | |
| xmlhttp.send(null); | |
| } | |
| function update_virtual_screen() | |
| { | |
| if ((xmlhttp.readyState == 4) && (xmlhttp.status == 200)) | |
| { | |
| var screen_text = xmlhttp.responseText; | |
| document.form["screen_text"].value = screen_text; | |
| //var json_data = json_parse(xmlhttp.responseText); | |
| } | |
| } | |
| function poll() | |
| { | |
| refresh_screen(); | |
| timerID = setTimeout("poll()", 2000); | |
| // clearTimeout(timerID); | |
| } | |
| //function start_server (username, password) | |
| //{ | |
| // load_url_to_screen('cgishell.cgi?ajax=serverstart&username=' + escape(username) + '&password=' + escape(password); | |
| //} | |
| function refresh_screen() | |
| { | |
| load_url_to_screen('cgishell.cgi?ajax=refresh&sid=%(SID)s'); | |
| } | |
| function query_hash() | |
| { | |
| load_url_to_screen('cgishell.cgi?ajax=hash&sid=%(SID)s'); | |
| } | |
| function query_cursor() | |
| { | |
| load_url_to_screen('cgishell.cgi?ajax=cursor&sid=%(SID)s'); | |
| } | |
| function exit_server() | |
| { | |
| load_url_to_screen('cgishell.cgi?ajax=exit&sid=%(SID)s'); | |
| } | |
| function type_key (chars) | |
| { | |
| var ch = '?'; | |
| if (flag_shiftlock || flag_shift) | |
| { | |
| ch = chars.substr(1,1); | |
| } | |
| else if (flag_ctrl) | |
| { | |
| ch = chars.substr(2,1); | |
| } | |
| else | |
| { | |
| ch = chars.substr(0,1); | |
| } | |
| load_url_to_screen('cgishell.cgi?ajax=send&sid=%(SID)s&arg=' + escape(ch)); | |
| if (flag_shift || flag_ctrl) | |
| { | |
| flag_shift = 0; | |
| flag_ctrl = 0; | |
| } | |
| update_button_colors(); | |
| } | |
| function key_shiftlock() | |
| { | |
| flag_ctrl = 0; | |
| flag_shift = 0; | |
| if (flag_shiftlock) | |
| { | |
| flag_shiftlock = 0; | |
| } | |
| else | |
| { | |
| flag_shiftlock = 1; | |
| } | |
| update_button_colors(); | |
| } | |
| function key_shift() | |
| { | |
| if (flag_shift) | |
| { | |
| flag_shift = 0; | |
| } | |
| else | |
| { | |
| flag_ctrl = 0; | |
| flag_shiftlock = 0; | |
| flag_shift = 1; | |
| } | |
| update_button_colors(); | |
| } | |
| function key_ctrl () | |
| { | |
| if (flag_ctrl) | |
| { | |
| flag_ctrl = 0; | |
| } | |
| else | |
| { | |
| flag_ctrl = 1; | |
| flag_shiftlock = 0; | |
| flag_shift = 0; | |
| } | |
| update_button_colors(); | |
| } | |
| function update_button_colors () | |
| { | |
| if (flag_ctrl) | |
| { | |
| document.form['Ctrl'].style.backgroundColor = ButtonOnColor; | |
| document.form['Ctrl2'].style.backgroundColor = ButtonOnColor; | |
| } | |
| else | |
| { | |
| document.form['Ctrl'].style.backgroundColor = document.form.style.backgroundColor; | |
| document.form['Ctrl2'].style.backgroundColor = document.form.style.backgroundColor; | |
| } | |
| if (flag_shift) | |
| { | |
| document.form['Shift'].style.backgroundColor = ButtonOnColor; | |
| document.form['Shift2'].style.backgroundColor = ButtonOnColor; | |
| } | |
| else | |
| { | |
| document.form['Shift'].style.backgroundColor = document.form.style.backgroundColor; | |
| document.form['Shift2'].style.backgroundColor = document.form.style.backgroundColor; | |
| } | |
| if (flag_shiftlock) | |
| { | |
| document.form['ShiftLock'].style.backgroundColor = ButtonOnColor; | |
| } | |
| else | |
| { | |
| document.form['ShiftLock'].style.backgroundColor = document.form.style.backgroundColor; | |
| } | |
| } | |
| function keyHandler(e) | |
| { | |
| var pressedKey; | |
| if (document.all) { e = window.event; } | |
| if (document.layers) { pressedKey = e.which; } | |
| if (document.all) { pressedKey = e.keyCode; } | |
| pressedCharacter = String.fromCharCode(pressedKey); | |
| type_key(pressedCharacter+pressedCharacter+pressedCharacter); | |
| alert(pressedCharacter); | |
| // alert(' Character = ' + pressedCharacter + ' [Decimal value = ' + pressedKey + ']'); | |
| } | |
| //document.onkeypress = keyHandler; | |
| //if (document.layers) | |
| // document.captureEvents(Event.KEYPRESS); | |
| //http://sniptools.com/jskeys | |
| //document.onkeyup = KeyCheck; | |
| function KeyCheck(e) | |
| { | |
| var KeyID = (window.event) ? event.keyCode : e.keyCode; | |
| type_key(String.fromCharCode(KeyID)); | |
| e.cancelBubble = true; | |
| window.event.cancelBubble = true; | |
| } | |
| </script> | |
| </head> | |
| <body onload="init()"> | |
| <form id="form" name="form" action="/cgi-bin/cgishell.cgi" method="POST"> | |
| <input name="sid" value="%(SID)s" type="hidden"> | |
| <textarea name="screen_text" cols="81" rows="25">%(SHELL_OUTPUT)s</textarea> | |
| <hr noshade="1"> | |
| <input name="cli" id="cli" type="text" size="80"><br> | |
| <table border="0" align="left"> | |
| <tr> | |
| <td width="86%%" align="center"> | |
| <input name="submit" type="submit" value="Submit"> | |
| <input name="refresh" type="button" value="REFRESH" onclick="refresh_screen()"> | |
| <input name="refresh" type="button" value="CURSOR" onclick="query_cursor()"> | |
| <input name="hash" type="button" value="HASH" onclick="query_hash()"> | |
| <input name="exit" type="button" value="EXIT" onclick="exit_server()"> | |
| <br> | |
| <input type="button" value="Esc" onclick="type_key('\\x1b\\x1b')" /> | |
| <input type="button" value="` ~" onclick="type_key('`~')" /> | |
| <input type="button" value="1!" onclick="type_key('1!')" /> | |
| <input type="button" value="2@" onclick="type_key('2@\\x00')" /> | |
| <input type="button" value="3#" onclick="type_key('3#')" /> | |
| <input type="button" value="4$" onclick="type_key('4$')" /> | |
| <input type="button" value="5%%" onclick="type_key('5%%')" /> | |
| <input type="button" value="6^" onclick="type_key('6^\\x1E')" /> | |
| <input type="button" value="7&" onclick="type_key('7&')" /> | |
| <input type="button" value="8*" onclick="type_key('8*')" /> | |
| <input type="button" value="9(" onclick="type_key('9(')" /> | |
| <input type="button" value="0)" onclick="type_key('0)')" /> | |
| <input type="button" value="-_" onclick="type_key('-_\\x1F')" /> | |
| <input type="button" value="=+" onclick="type_key('=+')" /> | |
| <input type="button" value="BkSp" onclick="type_key('\\x08\\x08\\x08')" /> | |
| <br> | |
| <input type="button" value="Tab" onclick="type_key('\\t\\t')" /> | |
| <input type="button" value="Q" onclick="type_key('qQ\\x11')" /> | |
| <input type="button" value="W" onclick="type_key('wW\\x17')" /> | |
| <input type="button" value="E" onclick="type_key('eE\\x05')" /> | |
| <input type="button" value="R" onclick="type_key('rR\\x12')" /> | |
| <input type="button" value="T" onclick="type_key('tT\\x14')" /> | |
| <input type="button" value="Y" onclick="type_key('yY\\x19')" /> | |
| <input type="button" value="U" onclick="type_key('uU\\x15')" /> | |
| <input type="button" value="I" onclick="type_key('iI\\x09')" /> | |
| <input type="button" value="O" onclick="type_key('oO\\x0F')" /> | |
| <input type="button" value="P" onclick="type_key('pP\\x10')" /> | |
| <input type="button" value="[ {" onclick="type_key('[{\\x1b')" /> | |
| <input type="button" value="] }" onclick="type_key(']}\\x1d')" /> | |
| <input type="button" value="\\ |" onclick="type_key('\\\\|\\x1c')" /> | |
| <br> | |
| <input type="button" id="Ctrl" value="Ctrl" onclick="key_ctrl()" /> | |
| <input type="button" value="A" onclick="type_key('aA\\x01')" /> | |
| <input type="button" value="S" onclick="type_key('sS\\x13')" /> | |
| <input type="button" value="D" onclick="type_key('dD\\x04')" /> | |
| <input type="button" value="F" onclick="type_key('fF\\x06')" /> | |
| <input type="button" value="G" onclick="type_key('gG\\x07')" /> | |
| <input type="button" value="H" onclick="type_key('hH\\x08')" /> | |
| <input type="button" value="J" onclick="type_key('jJ\\x0A')" /> | |
| <input type="button" value="K" onclick="type_key('kK\\x0B')" /> | |
| <input type="button" value="L" onclick="type_key('lL\\x0C')" /> | |
| <input type="button" value="; :" onclick="type_key(';:')" /> | |
| <input type="button" id="quote" value="'" onclick="type_key('\\x27\\x22')" /> | |
| <input type="button" value="Enter" onclick="type_key('\\n\\n')" /> | |
| <br> | |
| <input type="button" id="ShiftLock" value="Caps Lock" onclick="key_shiftlock()" /> | |
| <input type="button" id="Shift" value="Shift" onclick="key_shift()" /> | |
| <input type="button" value="Z" onclick="type_key('zZ\\x1A')" /> | |
| <input type="button" value="X" onclick="type_key('xX\\x18')" /> | |
| <input type="button" value="C" onclick="type_key('cC\\x03')" /> | |
| <input type="button" value="V" onclick="type_key('vV\\x16')" /> | |
| <input type="button" value="B" onclick="type_key('bB\\x02')" /> | |
| <input type="button" value="N" onclick="type_key('nN\\x0E')" /> | |
| <input type="button" value="M" onclick="type_key('mM\\x0D')" /> | |
| <input type="button" value=", <" onclick="type_key(',<')" /> | |
| <input type="button" value=". >" onclick="type_key('.>')" /> | |
| <input type="button" value="/ ?" onclick="type_key('/?')" /> | |
| <input type="button" id="Shift2" value="Shift" onclick="key_shift()" /> | |
| <input type="button" id="Ctrl2" value="Ctrl" onclick="key_ctrl()" /> | |
| <br> | |
| <input type="button" value=" FINAL FRONTIER " onclick="type_key(' ')" /> | |
| </td> | |
| </tr> | |
| </table> | |
| </form> | |
| </body> | |
| </html> | |
| """ | |
| LOGIN_HTML="""<html> | |
| <head> | |
| <title>Shell Login</title> | |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> | |
| <style type=text/css> | |
| a {color: #9f9; text-decoration: none} | |
| a:hover {color: #0f0} | |
| hr {color: #0f0} | |
| html,body,textarea,input,form | |
| { | |
| font-family: "Courier New", Courier, mono; | |
| font-size: 8pt; | |
| color: #0c0; | |
| background-color: #020; | |
| margin:3; | |
| padding:0; | |
| border:0; | |
| } | |
| input { background-color: #010; } | |
| input,textarea { | |
| border-width:1; | |
| border-style:solid; | |
| border-color:#0c0; | |
| padding:3; | |
| margin:3; | |
| } | |
| </style> | |
| <script language="JavaScript"> | |
| function init () | |
| { | |
| document.login_form["username"].focus(); | |
| } | |
| </script> | |
| </head> | |
| <body onload="init()"> | |
| <form name="login_form" method="POST"> | |
| <input name="start_server" value="1" type="hidden"> | |
| <input name="sid" value="%(SID)s" type="hidden"> | |
| username: <input name="username" type="text" size="30"><br> | |
| password: <input name="password" type="password" size="30"><br> | |
| <input name="submit" type="submit" value="enter"> | |
| </form> | |
| <br> | |
| </body> | |
| </html> | |
| """ | |
| if __name__ == "__main__": | |
| try: | |
| main() | |
| except Exception, e: | |
| print str(e) | |
| tb_dump = traceback.format_exc() | |
| print str(tb_dump) | |