Work towards C SDK (#11). Frenchie is still broken.
[progcomp10.git] / src / link / pexpect / examples / cgishell.cgi
1 #!/usr/bin/python
2 ##!/usr/bin/env python
3 """CGI shell server
4
5 This exposes a shell terminal on a web page.
6 It uses AJAX to send keys and receive screen updates.
7 The client web browser needs nothing but CSS and Javascript.
8
9     --hostname : sets the remote host name to open an ssh connection to.
10     --username : sets the user name to login with
11     --password : (optional) sets the password to login with
12     --port     : set the local port for the server to listen on
13     --watch    : show the virtual screen after each client request
14
15 This project is probably not the most security concious thing I've ever built.
16 This should be considered an experimental tool -- at best.
17 """
18 import sys,os
19 sys.path.insert (0,os.getcwd()) # let local modules precede any installed modules
20 import socket, random, string, traceback, cgi, time, getopt, getpass, threading, resource, signal
21 import pxssh, pexpect, ANSI
22
23 def exit_with_usage(exit_code=1):
24     print globals()['__doc__']
25     os._exit(exit_code)
26
27 def client (command, host='localhost', port=-1):
28     """This sends a request to the server and returns the response.
29     If port <= 0 then host is assumed to be the filename of a Unix domain socket.
30     If port > 0 then host is an inet hostname.
31     """
32     if port <= 0:
33         s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
34         s.connect(host)
35     else:
36         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
37         s.connect((host, port))
38     s.send(command)
39     data = s.recv (2500)
40     s.close()
41     return data
42
43 def server (hostname, username, password, socket_filename='/tmp/server_sock', daemon_mode = True, verbose=False):
44     """This starts and services requests from a client.
45         If daemon_mode is True then this forks off a separate daemon process and returns the daemon's pid.
46         If daemon_mode is False then this does not return until the server is done.
47     """
48     if daemon_mode:
49         mypid_name = '/tmp/%d.pid' % os.getpid()
50         daemon_pid = daemonize(daemon_pid_filename=mypid_name)
51         time.sleep(1)
52         if daemon_pid != 0:
53             os.unlink(mypid_name)
54             return daemon_pid
55
56     virtual_screen = ANSI.ANSI (24,80) 
57     child = pxssh.pxssh()
58     try:
59         child.login (hostname, username, password, login_naked=True)
60     except:
61         return   
62     if verbose: print 'login OK'
63     virtual_screen.write (child.before)
64     virtual_screen.write (child.after)
65
66     if os.path.exists(socket_filename): os.remove(socket_filename)
67     s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
68     s.bind(socket_filename)
69     os.chmod(socket_filename, 0777)
70     if verbose: print 'Listen'
71     s.listen(1)
72
73     r = roller (endless_poll, (child, child.PROMPT, virtual_screen))
74     r.start()
75     if verbose: print "started screen-poll-updater in background thread"
76     sys.stdout.flush()
77     try:
78         while True:
79             conn, addr = s.accept()
80             if verbose: print 'Connected by', addr
81             data = conn.recv(1024)
82             request = data.split(' ', 1)
83             if len(request)>1:
84                 cmd = request[0].strip()
85                 arg = request[1].strip()
86             else:
87                 cmd = request[0].strip()
88                 arg = ''
89
90             if cmd == 'exit':
91                 r.cancel()
92                 break
93             elif cmd == 'sendline':
94                 child.sendline (arg)
95                 time.sleep(0.1)
96                 shell_window = str(virtual_screen)
97             elif cmd == 'send' or cmd=='xsend':
98                 if cmd=='xsend':
99                     arg = arg.decode("hex")
100                 child.send (arg)
101                 time.sleep(0.1)
102                 shell_window = str(virtual_screen)
103             elif cmd == 'cursor':
104                 shell_window = '%x,%x' % (virtual_screen.cur_r, virtual_screen.cur_c)
105             elif cmd == 'refresh':
106                 shell_window = str(virtual_screen)
107             elif cmd == 'hash':
108                 shell_window = str(hash(str(virtual_screen)))
109
110             response = []
111             response.append (shell_window)
112             if verbose: print '\n'.join(response)
113             sent = conn.send('\n'.join(response))
114             if sent < len (response):
115                 if verbose: print "Sent is too short. Some data was cut off."
116             conn.close()
117     except e:
118         pass
119     r.cancel()
120     if verbose: print "cleaning up socket"
121     s.close()
122     if os.path.exists(socket_filename): os.remove(socket_filename)
123     if verbose: print "server done!"
124
125 class roller (threading.Thread):
126     """This class continuously loops a function in a thread.
127         This is basically a thin layer around Thread with a
128         while loop and a cancel.
129     """
130     def __init__(self, function, args=[], kwargs={}):
131         threading.Thread.__init__(self)
132         self.function = function
133         self.args = args
134         self.kwargs = kwargs
135         self.finished = threading.Event()
136     def cancel(self):
137         """Stop the roller."""
138         self.finished.set()
139     def run(self):
140         while not self.finished.isSet():
141             self.function(*self.args, **self.kwargs)
142
143 def endless_poll (child, prompt, screen, refresh_timeout=0.1):
144     """This keeps the screen updated with the output of the child.
145         This will be run in a separate thread. See roller class.
146     """
147     #child.logfile_read = screen
148     try:
149         s = child.read_nonblocking(4000, 0.1)
150         screen.write(s)
151     except:
152         pass
153
154 def daemonize (stdin=None, stdout=None, stderr=None, daemon_pid_filename=None):
155     """This runs the current process in the background as a daemon.
156     The arguments stdin, stdout, stderr allow you to set the filename that the daemon reads and writes to.
157     If they are set to None then all stdio for the daemon will be directed to /dev/null.
158     If daemon_pid_filename is set then the pid of the daemon will be written to it as plain text
159     and the pid will be returned. If daemon_pid_filename is None then this will return None.
160     """
161     UMASK = 0
162     WORKINGDIR = "/"
163     MAXFD = 1024
164
165     # The stdio file descriptors are redirected to /dev/null by default.
166     if hasattr(os, "devnull"):
167         DEVNULL = os.devnull
168     else:
169         DEVNULL = "/dev/null"
170     if stdin is None: stdin = DEVNULL
171     if stdout is None: stdout = DEVNULL
172     if stderr is None: stderr = DEVNULL
173
174     try:
175         pid = os.fork()
176     except OSError, e:
177         raise Exception, "%s [%d]" % (e.strerror, e.errno)
178
179     if pid != 0:   # The first child.
180         os.waitpid(pid,0)
181         if daemon_pid_filename is not None:
182             daemon_pid = int(file(daemon_pid_filename,'r').read())
183             return daemon_pid
184         else:
185             return None
186
187     # first child
188     os.setsid()
189     signal.signal(signal.SIGHUP, signal.SIG_IGN)
190
191     try:
192         pid = os.fork() # fork second child
193     except OSError, e:
194         raise Exception, "%s [%d]" % (e.strerror, e.errno)
195
196     if pid != 0:
197         if daemon_pid_filename is not None:
198             file(daemon_pid_filename,'w').write(str(pid))
199         os._exit(0) # exit parent (the first child) of the second child.
200
201     # second child
202     os.chdir(WORKINGDIR)
203     os.umask(UMASK)
204
205     maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
206     if maxfd == resource.RLIM_INFINITY:
207         maxfd = MAXFD
208   
209     # close all file descriptors
210     for fd in xrange(0, maxfd):
211         try:
212             os.close(fd)
213         except OSError:   # fd wasn't open to begin with (ignored)
214             pass
215
216     os.open (DEVNULL, os.O_RDWR)  # standard input
217
218     # redirect standard file descriptors
219     si = open(stdin, 'r')
220     so = open(stdout, 'a+')
221     se = open(stderr, 'a+', 0)
222     os.dup2(si.fileno(), sys.stdin.fileno())
223     os.dup2(so.fileno(), sys.stdout.fileno())
224     os.dup2(se.fileno(), sys.stderr.fileno())
225
226     return 0
227
228 def client_cgi ():
229     """This handles the request if this script was called as a cgi.
230     """
231     sys.stderr = sys.stdout
232     ajax_mode = False
233     TITLE="Shell"
234     SHELL_OUTPUT=""
235     SID="NOT"
236     print "Content-type: text/html;charset=utf-8\r\n"
237     try:
238         form = cgi.FieldStorage()
239         if form.has_key('ajax'):
240             ajax_mode = True
241             ajax_cmd = form['ajax'].value
242             SID=form['sid'].value
243             if ajax_cmd == 'send':
244                 command = 'xsend'
245                 arg = form['arg'].value.encode('hex')
246                 result = client (command + ' ' + arg, '/tmp/'+SID)
247                 print result
248             elif ajax_cmd == 'refresh':
249                 command = 'refresh'
250                 result = client (command, '/tmp/'+SID)
251                 print result
252             elif ajax_cmd == 'cursor':
253                 command = 'cursor'
254                 result = client (command, '/tmp/'+SID)
255                 print result
256             elif ajax_cmd == 'exit':
257                 command = 'exit'
258                 result = client (command, '/tmp/'+SID)
259                 print result
260             elif ajax_cmd == 'hash':
261                 command = 'hash'
262                 result = client (command, '/tmp/'+SID)
263                 print result
264         elif not form.has_key('sid'):
265             SID=random_sid()
266             print LOGIN_HTML % locals();
267         else:
268             SID=form['sid'].value
269             if form.has_key('start_server'):
270                 USERNAME = form['username'].value
271                 PASSWORD = form['password'].value
272                 dpid = server ('127.0.0.1', USERNAME, PASSWORD, '/tmp/'+SID)
273                 SHELL_OUTPUT="daemon pid: " + str(dpid)
274             else:
275                 if form.has_key('cli'):
276                     command = 'sendline ' + form['cli'].value
277                 else:
278                     command = 'sendline'
279                 SHELL_OUTPUT = client (command, '/tmp/'+SID)
280             print CGISH_HTML % locals()
281     except:
282         tb_dump = traceback.format_exc()
283         if ajax_mode:
284             print str(tb_dump)
285         else:
286             SHELL_OUTPUT=str(tb_dump)
287             print CGISH_HTML % locals()
288
289 def server_cli():
290     """This is the command line interface to starting the server.
291     This handles things if the script was not called as a CGI
292     (if you run it from the command line).
293     """
294     try:
295         optlist, args = getopt.getopt(sys.argv[1:], 'h?d', ['help','h','?', 'hostname=', 'username=', 'password=', 'port=', 'watch'])
296     except Exception, e:
297         print str(e)
298         exit_with_usage()
299
300     command_line_options = dict(optlist)
301     options = dict(optlist)
302     # There are a million ways to cry for help. These are but a few of them.
303     if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]:
304         exit_with_usage(0)
305   
306     hostname = "127.0.0.1"
307     #port = 1664
308     username = os.getenv('USER')
309     password = ""
310     daemon_mode = False
311     if '-d' in options:
312         daemon_mode = True
313     if '--watch' in options:
314         watch_mode = True
315     else:
316         watch_mode = False
317     if '--hostname' in options:
318         hostname = options['--hostname']
319     if '--port' in options:
320         port = int(options['--port'])
321     if '--username' in options:
322         username = options['--username']
323     if '--password' in options:
324         password = options['--password']
325     else:
326         password = getpass.getpass('password: ')
327    
328     server (hostname, username, password, '/tmp/mysock', daemon_mode)
329
330 def random_sid ():
331     a=random.randint(0,65535)
332     b=random.randint(0,65535)
333     return '%04x%04x.sid' % (a,b)
334
335 def parse_host_connect_string (hcs):
336     """This parses a host connection string in the form
337     username:password@hostname:port. All fields are options expcet hostname. A
338     dictionary is returned with all four keys. Keys that were not included are
339     set to empty strings ''. Note that if your password has the '@' character
340     then you must backslash escape it.
341     """
342     if '@' in hcs:
343         p = re.compile (r'(?P<username>[^@:]*)(:?)(?P<password>.*)(?!\\)@(?P<hostname>[^:]*):?(?P<port>[0-9]*)')
344     else:
345         p = re.compile (r'(?P<username>)(?P<password>)(?P<hostname>[^:]*):?(?P<port>[0-9]*)')
346     m = p.search (hcs)
347     d = m.groupdict()
348     d['password'] = d['password'].replace('\\@','@')
349     return d
350      
351 def pretty_box (s, rows=24, cols=80):
352     """This puts an ASCII text box around the given string.
353     """
354     top_bot = '+' + '-'*cols + '+\n'
355     return top_bot + '\n'.join(['|'+line+'|' for line in s.split('\n')]) + '\n' + top_bot
356
357 def main ():
358     if os.getenv('REQUEST_METHOD') is None:
359         server_cli()
360     else:
361         client_cgi()
362
363 # It's mostly HTML and Javascript from here on out.
364 CGISH_HTML="""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
365 <html>
366 <head>
367 <title>%(TITLE)s %(SID)s</title>
368 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
369 <style type=text/css>
370 a {color: #9f9; text-decoration: none}
371 a:hover {color: #0f0}
372 hr {color: #0f0}
373 html,body,textarea,input,form
374 {
375 font-family: "Courier New", Courier, mono; 
376 font-size: 8pt; 
377 color: #0c0;
378 background-color: #020;
379 margin:0;
380 padding:0;
381 border:0;
382 }
383 input { background-color: #010; }
384 textarea {
385 border-width:1;
386 border-style:solid;
387 border-color:#0c0;
388 padding:3;
389 margin:3;
390 }
391 </style>
392
393 <script language="JavaScript">
394 function focus_first()
395 {if (document.forms.length > 0)
396 {var TForm = document.forms[0];
397 for (i=0;i<TForm.length;i++){
398 if ((TForm.elements[i].type=="text")||
399 (TForm.elements[i].type=="textarea")||
400 (TForm.elements[i].type.toString().charAt(0)=="s"))
401 {document.forms[0].elements[i].focus();break;}}}}
402
403 // JavaScript Virtual Keyboard
404 // If you like this code then buy me a sandwich.
405 // Noah Spurrier <[email protected]>
406 var flag_shift=0;
407 var flag_shiftlock=0;
408 var flag_ctrl=0;
409 var ButtonOnColor="#ee0";
410
411 function init ()
412 {
413     // hack to set quote key to show both single quote and double quote
414     document.form['quote'].value = "'" + '  "';
415     //refresh_screen();
416     poll();
417     document.form["cli"].focus();
418 }
419 function get_password ()
420 {
421     var username = prompt("username?","");
422     var password = prompt("password?","");
423     start_server (username, password);
424 }
425 function multibrowser_ajax ()
426 {
427     var xmlHttp = false;
428 /*@cc_on @*/
429 /*@if (@_jscript_version >= 5)
430     try
431     {
432         xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
433     }
434     catch (e)
435     {
436         try
437         {
438             xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
439         }
440         catch (e2)
441         {
442               xmlHttp = false;
443         }
444     }
445 @end @*/
446
447     if (!xmlHttp && typeof XMLHttpRequest != 'undefined')
448     {
449         xmlHttp = new XMLHttpRequest();
450     }
451     return xmlHttp;
452 }
453 function load_url_to_screen(url)
454
455     xmlhttp = multibrowser_ajax();
456     //window.XMLHttpRequest?new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP");
457     xmlhttp.onreadystatechange = update_virtual_screen;
458     xmlhttp.open("GET", url);
459     xmlhttp.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
460     xmlhttp.send(null);
461 }
462 function update_virtual_screen()
463 {
464     if ((xmlhttp.readyState == 4) && (xmlhttp.status == 200))
465     {
466         var screen_text = xmlhttp.responseText;
467         document.form["screen_text"].value = screen_text;
468         //var json_data = json_parse(xmlhttp.responseText);
469     }
470 }
471 function poll()
472 {
473     refresh_screen();
474     timerID  = setTimeout("poll()", 2000);
475     // clearTimeout(timerID);
476 }
477 //function start_server (username, password)
478 //{
479 //    load_url_to_screen('cgishell.cgi?ajax=serverstart&username=' + escape(username) + '&password=' + escape(password);
480 //}
481 function refresh_screen()
482 {
483     load_url_to_screen('cgishell.cgi?ajax=refresh&sid=%(SID)s');
484 }
485 function query_hash()
486 {
487     load_url_to_screen('cgishell.cgi?ajax=hash&sid=%(SID)s');
488 }
489 function query_cursor()
490 {
491     load_url_to_screen('cgishell.cgi?ajax=cursor&sid=%(SID)s');
492 }
493 function exit_server()
494 {
495     load_url_to_screen('cgishell.cgi?ajax=exit&sid=%(SID)s');
496 }
497 function type_key (chars)
498 {
499     var ch = '?';
500     if (flag_shiftlock || flag_shift)
501     {
502         ch = chars.substr(1,1);
503     }
504     else if (flag_ctrl)
505     {
506         ch = chars.substr(2,1);
507     }
508     else
509     {
510         ch = chars.substr(0,1);
511     }
512     load_url_to_screen('cgishell.cgi?ajax=send&sid=%(SID)s&arg=' + escape(ch));
513     if (flag_shift || flag_ctrl)
514     {
515         flag_shift = 0;
516         flag_ctrl = 0;
517     }
518     update_button_colors();
519 }
520
521 function key_shiftlock()
522 {
523     flag_ctrl = 0;
524     flag_shift = 0;
525     if (flag_shiftlock)
526     {
527         flag_shiftlock = 0;
528     }
529     else
530     {
531         flag_shiftlock = 1;
532     }
533     update_button_colors();
534 }
535
536 function key_shift()
537 {
538     if (flag_shift)
539     {
540         flag_shift = 0;
541     }
542     else
543     {
544         flag_ctrl = 0;
545         flag_shiftlock = 0;
546         flag_shift = 1;
547     }
548     update_button_colors(); 
549 }
550 function key_ctrl ()
551 {
552     if (flag_ctrl)
553     {
554         flag_ctrl = 0;
555     }
556     else
557     {
558         flag_ctrl = 1;
559         flag_shiftlock = 0;
560         flag_shift = 0;
561     }
562     
563     update_button_colors();
564 }
565 function update_button_colors ()
566 {
567     if (flag_ctrl)
568     {
569         document.form['Ctrl'].style.backgroundColor = ButtonOnColor;
570         document.form['Ctrl2'].style.backgroundColor = ButtonOnColor;
571     }
572     else
573     {
574         document.form['Ctrl'].style.backgroundColor = document.form.style.backgroundColor;
575         document.form['Ctrl2'].style.backgroundColor = document.form.style.backgroundColor;
576     }
577     if (flag_shift)
578     {
579         document.form['Shift'].style.backgroundColor = ButtonOnColor;
580         document.form['Shift2'].style.backgroundColor = ButtonOnColor;
581     }
582     else
583     {
584         document.form['Shift'].style.backgroundColor = document.form.style.backgroundColor;
585         document.form['Shift2'].style.backgroundColor = document.form.style.backgroundColor;
586     }
587     if (flag_shiftlock)
588     {
589         document.form['ShiftLock'].style.backgroundColor = ButtonOnColor;
590     }
591     else
592     {
593         document.form['ShiftLock'].style.backgroundColor = document.form.style.backgroundColor;
594     }
595     
596 }
597 function keyHandler(e)
598 {
599     var pressedKey;
600     if (document.all)    { e = window.event; }
601     if (document.layers) { pressedKey = e.which; }
602     if (document.all)    { pressedKey = e.keyCode; }
603     pressedCharacter = String.fromCharCode(pressedKey);
604     type_key(pressedCharacter+pressedCharacter+pressedCharacter);
605     alert(pressedCharacter);
606 //    alert(' Character = ' + pressedCharacter + ' [Decimal value = ' + pressedKey + ']');
607 }
608 //document.onkeypress = keyHandler;
609 //if (document.layers)
610 //    document.captureEvents(Event.KEYPRESS);
611 //http://sniptools.com/jskeys
612 //document.onkeyup = KeyCheck;       
613 function KeyCheck(e)
614 {
615     var KeyID = (window.event) ? event.keyCode : e.keyCode;
616     type_key(String.fromCharCode(KeyID));
617     e.cancelBubble = true;
618     window.event.cancelBubble = true;
619 }
620 </script>
621
622 </head>
623
624 <body onload="init()">
625 <form id="form" name="form" action="/cgi-bin/cgishell.cgi" method="POST">
626 <input name="sid" value="%(SID)s" type="hidden">
627 <textarea name="screen_text" cols="81" rows="25">%(SHELL_OUTPUT)s</textarea>
628 <hr noshade="1">
629 &nbsp;<input name="cli" id="cli" type="text" size="80"><br>
630 <table border="0" align="left">
631 <tr>
632 <td width="86%%" align="center">    
633     <input name="submit" type="submit" value="Submit">
634     <input name="refresh" type="button" value="REFRESH" onclick="refresh_screen()">
635     <input name="refresh" type="button" value="CURSOR" onclick="query_cursor()">
636     <input name="hash" type="button" value="HASH" onclick="query_hash()">
637     <input name="exit" type="button" value="EXIT" onclick="exit_server()">
638     <br>
639     <input type="button" value="Esc" onclick="type_key('\\x1b\\x1b')" />
640     <input type="button" value="` ~" onclick="type_key('`~')" />
641     <input type="button" value="1!" onclick="type_key('1!')" />
642     <input type="button" value="2@" onclick="type_key('2@\\x00')" />
643     <input type="button" value="3#" onclick="type_key('3#')" />
644     <input type="button" value="4$" onclick="type_key('4$')" />
645     <input type="button" value="5%%" onclick="type_key('5%%')" />
646     <input type="button" value="6^" onclick="type_key('6^\\x1E')" />
647     <input type="button" value="7&" onclick="type_key('7&')" />
648     <input type="button" value="8*" onclick="type_key('8*')" />
649     <input type="button" value="9(" onclick="type_key('9(')" />
650     <input type="button" value="0)" onclick="type_key('0)')" />
651     <input type="button" value="-_" onclick="type_key('-_\\x1F')" />
652     <input type="button" value="=+" onclick="type_key('=+')" />
653     <input type="button" value="BkSp" onclick="type_key('\\x08\\x08\\x08')" />
654     <br>
655     <input type="button" value="Tab" onclick="type_key('\\t\\t')" />
656     <input type="button" value="Q" onclick="type_key('qQ\\x11')" />
657     <input type="button" value="W" onclick="type_key('wW\\x17')" />
658     <input type="button" value="E" onclick="type_key('eE\\x05')" />
659     <input type="button" value="R" onclick="type_key('rR\\x12')" />
660     <input type="button" value="T" onclick="type_key('tT\\x14')" />
661     <input type="button" value="Y" onclick="type_key('yY\\x19')" />
662     <input type="button" value="U" onclick="type_key('uU\\x15')" />
663     <input type="button" value="I" onclick="type_key('iI\\x09')" />
664     <input type="button" value="O" onclick="type_key('oO\\x0F')" />
665     <input type="button" value="P" onclick="type_key('pP\\x10')" />
666     <input type="button" value="[ {" onclick="type_key('[{\\x1b')" />
667     <input type="button" value="] }" onclick="type_key(']}\\x1d')" />
668     <input type="button" value="\\ |" onclick="type_key('\\\\|\\x1c')" />
669     <br>
670     <input type="button" id="Ctrl" value="Ctrl" onclick="key_ctrl()" />
671     <input type="button" value="A" onclick="type_key('aA\\x01')" />
672     <input type="button" value="S" onclick="type_key('sS\\x13')" />
673     <input type="button" value="D" onclick="type_key('dD\\x04')" />
674     <input type="button" value="F" onclick="type_key('fF\\x06')" />
675     <input type="button" value="G" onclick="type_key('gG\\x07')" />
676     <input type="button" value="H" onclick="type_key('hH\\x08')" />
677     <input type="button" value="J" onclick="type_key('jJ\\x0A')" />
678     <input type="button" value="K" onclick="type_key('kK\\x0B')" />
679     <input type="button" value="L" onclick="type_key('lL\\x0C')" />
680     <input type="button" value="; :" onclick="type_key(';:')" />
681     <input type="button" id="quote" value="'" onclick="type_key('\\x27\\x22')" />
682     <input type="button" value="Enter" onclick="type_key('\\n\\n')" />
683     <br>
684     <input type="button" id="ShiftLock" value="Caps Lock" onclick="key_shiftlock()" />
685     <input type="button" id="Shift" value="Shift" onclick="key_shift()"  />
686     <input type="button" value="Z" onclick="type_key('zZ\\x1A')" />
687     <input type="button" value="X" onclick="type_key('xX\\x18')" />
688     <input type="button" value="C" onclick="type_key('cC\\x03')" />
689     <input type="button" value="V" onclick="type_key('vV\\x16')" />
690     <input type="button" value="B" onclick="type_key('bB\\x02')" />
691     <input type="button" value="N" onclick="type_key('nN\\x0E')" />
692     <input type="button" value="M" onclick="type_key('mM\\x0D')" />
693     <input type="button" value=", <" onclick="type_key(',<')" />
694     <input type="button" value=". >" onclick="type_key('.>')" />
695     <input type="button" value="/ ?" onclick="type_key('/?')" />
696     <input type="button" id="Shift2" value="Shift" onclick="key_shift()" />
697     <input type="button" id="Ctrl2" value="Ctrl" onclick="key_ctrl()" />
698     <br>
699     <input type="button" value="        FINAL FRONTIER        " onclick="type_key('  ')" />
700 </td>
701 </tr>
702 </table>  
703 </form>
704 </body>
705 </html>
706 """
707
708 LOGIN_HTML="""<html>
709 <head>
710 <title>Shell Login</title>
711 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
712 <style type=text/css>
713 a {color: #9f9; text-decoration: none}
714 a:hover {color: #0f0}
715 hr {color: #0f0}
716 html,body,textarea,input,form
717 {
718 font-family: "Courier New", Courier, mono;
719 font-size: 8pt;
720 color: #0c0;
721 background-color: #020;
722 margin:3;
723 padding:0;
724 border:0;
725 }
726 input { background-color: #010; }
727 input,textarea {
728 border-width:1;
729 border-style:solid;
730 border-color:#0c0;
731 padding:3;
732 margin:3;
733 }
734 </style>
735 <script language="JavaScript">
736 function init ()
737 {
738     document.login_form["username"].focus();
739 }
740 </script>
741 </head>
742 <body onload="init()">
743 <form name="login_form" method="POST">
744 <input name="start_server" value="1" type="hidden">
745 <input name="sid" value="%(SID)s" type="hidden">
746 username: <input name="username" type="text" size="30"><br>
747 password: <input name="password" type="password" size="30"><br>
748 <input name="submit" type="submit" value="enter">
749 </form>
750 <br>
751 </body>
752 </html>
753 """
754
755 if __name__ == "__main__":
756     try:
757         main()
758     except Exception, e:
759         print str(e)
760         tb_dump = traceback.format_exc()
761         print str(tb_dump)
762

UCC git Repository :: git.ucc.asn.au