Index: CMTManagement/p3.py
===================================================================
--- CMTManagement/p3.py	(revision 421)
+++ CMTManagement/p3.py	(revision 421)
@@ -0,0 +1,209 @@
+# $Id: p3.py,v 1.2 2003/11/18 19:04:03 phr Exp phr $
+
+# Simple p3 encryption "algorithm": it's just SHA used as a stream
+# cipher in output feedback mode.  
+
+# Author: Paul Rubin, Fort GNOX Cryptography, <phr-crypto at nightsong.com>.
+# Algorithmic advice from David Wagner, Richard Parker, Bryan
+# Olson, and Paul Crowley on sci.crypt is gratefully acknowledged.
+
+# Copyright 2002,2003 by Paul Rubin
+# Copying license: same as Python 2.3 license
+
+# Please include this revision number in any bug reports: $Revision: 1.2 $.
+
+from string import join
+from array import array
+import sha
+import getpass
+from time import time
+
+
+class CryptError(Exception): pass
+def _hash(str): return sha.new(str).digest()
+
+_ivlen = 16
+_maclen = 8
+_state = _hash(`time()`)
+
+try:
+    import os
+    _pid = `os.getpid()`
+except ImportError, AttributeError:
+    _pid = ''
+
+def _expand_key(key, clen):
+    blocks = (clen+19)/20
+    xkey=[]
+    seed=key
+    for i in xrange(blocks):
+        seed=sha.new(key+seed).digest()
+        xkey.append(seed)
+    j = join(xkey,'')
+    return array ('L', j)
+
+def p3_encrypt(plain,key):
+    global _state
+    H = _hash
+
+    # change _state BEFORE using it to compute nonce, in case there's
+    # a thread switch between computing the nonce and folding it into
+    # the state.  This way if two threads compute a nonce from the
+    # same data, they won't both get the same nonce.  (There's still
+    # a small danger of a duplicate nonce--see below).
+    _state = 'X'+_state
+
+    # Attempt to make nlist unique for each call, so we can get a
+    # unique nonce.  It might be good to include a process ID or
+    # something, but I don't know if that's portable between OS's.
+    # Since is based partly on both the key and plaintext, in the
+    # worst case (encrypting the same plaintext with the same key in
+    # two separate Python instances at the same time), you might get
+    # identical ciphertexts for the identical plaintexts, which would
+    # be a security failure in some applications.  Be careful.
+    nlist = [`time()`, _pid, _state, `len(plain)`,plain, key]
+    nonce = H(join(nlist,','))[:_ivlen]
+    _state = H('update2'+_state+nonce)
+    k_enc, k_auth = H('enc'+key+nonce), H('auth'+key+nonce)
+    n=len(plain)                        # cipher size not counting IV
+
+    stream = array('L', plain+'0000'[n&3:]) # pad to fill 32-bit words
+    xkey = _expand_key(k_enc, n+4)
+    for i in xrange(len(stream)):
+        stream[i] = stream[i] ^ xkey[i]
+    ct = nonce + stream.tostring()[:n]
+    auth = _hmac(ct, k_auth)
+    return ct + auth[:_maclen]
+
+def p3_decrypt(cipher,key):
+    H = _hash
+    n=len(cipher)-_ivlen-_maclen        # length of ciphertext
+    if n < 0:
+        raise CryptError, "invalid ciphertext"
+    nonce,stream,auth = \
+      cipher[:_ivlen], cipher[_ivlen:-_maclen]+'0000'[n&3:],cipher[-_maclen:]
+    k_enc, k_auth = H('enc'+key+nonce), H('auth'+key+nonce)
+    vauth = _hmac (cipher[:-_maclen], k_auth)[:_maclen]
+    if auth != vauth:
+        raise CryptError, "invalid key or ciphertext"
+
+    stream = array('L', stream)
+    xkey = _expand_key (k_enc, n+4)
+    for i in xrange (len(stream)):
+        stream[i] = stream[i] ^ xkey[i]
+    plain = stream.tostring()[:n]
+    return plain
+
+# RFC 2104 HMAC message authentication code
+# This implementation is faster than Python 2.2's hmac.py, and also works in
+# old Python versions (at least as old as 1.5.2).
+from string import translate
+def _hmac_setup():
+    global _ipad, _opad, _itrans, _otrans
+    _itrans = array('B',[0]*256)
+    _otrans = array('B',[0]*256)    
+    for i in xrange(256):
+        _itrans[i] = i ^ 0x36
+        _otrans[i] = i ^ 0x5c
+    _itrans = _itrans.tostring()
+    _otrans = _otrans.tostring()
+
+    _ipad = '\x36'*64
+    _opad = '\x5c'*64
+
+def _hmac(msg, key):
+    if len(key)>64:
+        key=sha.new(key).digest()
+    ki = (translate(key,_itrans)+_ipad)[:64] # inner
+    ko = (translate(key,_otrans)+_opad)[:64] # outer
+    return sha.new(ko+sha.new(ki+msg).digest()).digest()
+
+#
+# benchmark and unit test
+#
+
+def _time_p3(n=1000,len=20):
+    plain="a"*len
+    t=time()
+    for i in xrange(n):
+        p3_encrypt(plain,"abcdefgh")
+    dt=time()-t
+    print "plain p3:", n,len,dt,"sec =",n*len/dt,"bytes/sec"
+
+def _speed():
+    _time_p3(len=5)
+    _time_p3()
+    _time_p3(len=200)
+    _time_p3(len=2000,n=100)
+
+def _test():
+    e=p3_encrypt
+    d=p3_decrypt
+
+    plain="test plaintext"
+    key = "test key"
+    c1 = e(plain,key)
+    c2 = e(plain,key)
+    assert c1!=c2
+    assert d(c2,key)==plain
+    assert d(c1,key)==plain
+    c3 = c2[:20]+chr(1+ord(c2[20]))+c2[21:] # change one ciphertext character
+
+    try:
+        print d(c3,key)         # should throw exception
+        print "auth verification failure"
+    except CryptError:
+        pass
+
+    try:
+        print d(c2,'wrong key')         # should throw exception
+        print "test failure"
+    except CryptError:
+        pass
+
+
+def encrypt(instring,key=None):
+  """Encrypt the input string
+  
+  Encrypts the input string or the string contained in the file
+  with name <instring>. Uses encryption key <key>
+  """
+  
+  try:
+    if os.path.exists(instring):
+      instring = open(instring,'r').read().strip() 
+  except:
+    pass     
+
+  if key is None:
+    key = getpass.getuser()
+  return 'cry'+p3_encrypt(instring,key)+'pto'  
+
+def decrypt(instring,key=None):
+  """Decrypt the input string
+  
+  Decrypts the input string or the string contained in the file
+  with name <instring>. Uses encryption key <key>. If the input
+  string is not encrypted, just returns it.
+  """
+  
+  try:
+    if os.path.exists(instring):
+      instring = open(instring,'r').read().strip()
+  except:
+    pass    
+  
+  ins = instring.strip()
+  #if re.search('^cry.*pto$',ins):
+  if ins[:3]+ins[-3:] == 'crypto': 
+    if key is None:
+      key = getpass.getuser()
+    ins = ins[3:-3]
+    return p3_decrypt(ins,key) 
+  else:
+    return instring    
+
+
+_hmac_setup()
+_test()
+# _speed()                                # uncomment to run speed test
Index: CMTManagement/utils/bd_client.py
===================================================================
--- CMTManagement/utils/bd_client.py	(revision 421)
+++ CMTManagement/utils/bd_client.py	(revision 421)
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+import socket
+import sys
+
+HOST = 'localhost'    # The remote host
+PORT = 1968              # The same port as used by the server
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.connect((HOST, PORT))
+s.send(sys.argv[1])
+data = s.recv(1024)
+s.close()
+print data
+
Index: CMTManagement/utils/bd_client_web.py
===================================================================
--- CMTManagement/utils/bd_client_web.py	(revision 421)
+++ CMTManagement/utils/bd_client_web.py	(revision 421)
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# #sys.path.insert (0,"/var/www/cgi-bin")
+
+import sys
+import os
+import commands
+import cgi
+import traceback
+import string
+import re
+import socket
+
+ROOTPATH="/tmp"
+CGISH_HTML="""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>Untitled Document</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+<style type=text/css>
+body 
+{
+	font-family: "Courier New", Courier, mono; 
+	font-size: 10pt; 
+	color: #00cc00;
+	background-color: #002000; 
+}
+.headline {font-size: 18pt}
+a {color: #99ff99; text-decoration: none}
+a:hover {color: #00FF00}
+hr {color: #00ff00}
+.cursor {color:#002000;background-color:#00cc00}
+form {
+	font-family: "Courier New", Courier, mono;
+	color: #00CC00;
+	background-color: #003300;
+}
+input {
+	font-family: "Courier New", Courier, mono;
+	color: #00CC00;
+	background-color: #003300;
+	padding: 3px;
+	border: 0;
+}
+textarea {
+	font-family: "Courier New", Courier, mono;
+	color: #00CC00;
+	background-color: #003300;
+}
+</style>
+<script language="JavaScript">
+function firstFocus()
+{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;}}}}
+</script>
+</head>
+
+<body onLoad="firstFocus()">
+<pre>%(SHELL_OUTPUT)s</pre>
+<form action="http://www.chocho.org/cgi-bin/bd_client_web.py" method="POST">
+<input name="command" type="text" size="80"><br>
+<hr noshade="1">
+<input name="submit" type="submit" value="Enter">
+<input name="ctrl_c" type="submit" value="CTRL-C">
+<input name="ctrl_d" type="submit" value="CTRL-D">
+<input name="ctrl_z" type="submit" value="CTRL-Z">
+<input name="esc" type="submit" value="ESC">
+<input name="refresh" type="submit" value="REFRESH">
+
+</form>
+
+</body>
+</html>
+"""
+def page (result = ''):
+	"""Return the main form"""
+	return CGISH_HTML % {'SHELL_OUTPUT':result}
+
+def bd_client (command, host='localhost', port = 1666):
+	HOST = 'localhost'    # The remote host
+	PORT = 1666              # The same port as used by the server
+	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+	s.connect((HOST, PORT))
+	s.send(command)
+        data = s.recv (1920)
+	s.close()
+	return data
+
+	#fout = file ('/tmp/log2','w')
+	#fout.write (command)
+	#fout.write ('\n')
+#	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+#	s.connect((host, port))
+#	s.send(command)
+#	data = s.recv(1024)
+	#fout.write (data)
+	#fout.write ('\n')
+#	s.close()
+	#fout.close()
+#	return data
+
+#def link (matchobject):
+#	"""Used in re.sub calls to replace a matched object with an HTML link."""
+#	path = matchobject.group(0)
+#	l = "<a href=\"http://63.199.26.227/cgi-bin/ls.py?root=%s&path=%s\">%s</a>" % \
+#		(ROOTPATH+"/"+path, ROOTPATH+"/"+path, path)
+#	return l
+
+def escape_shell_meta_chars(s):
+         """Escape shell meta characters. This is done for security."""
+         s = string.replace(s, "\\", "\\\\")
+         s = string.replace(s, "`", "\\`")
+         s = string.replace(s, " ", "\\ ",)
+         s = string.replace(s, "&", "\\&",)
+         s = string.replace(s, ";", "\\;",)
+         s = string.replace(s, "\"", "\\\"",)
+         s = string.replace(s, "\'", "\\'",)
+         s = string.replace(s, "|", "\\|",)
+         s = string.replace(s, "*", "\\*",)
+         s = string.replace(s, "<", "\\<",)
+         s = string.replace(s, ">", "\\>",)
+         return s
+
+sys.path.insert (0,"/usr/local/apache/cgi-bin")
+sys.stderr = sys.stdout
+
+print "Content-type: text/html"
+print
+
+try:
+	form = cgi.FieldStorage()
+	if form.has_key("command"):
+		command = form["command"].value
+		result = bd_client (command)
+		print page(result)
+	else:
+		print page()
+
+except:
+	print "\n\n<pre>"
+	traceback.print_exc()
+	print "</pre>"
+
+
Index: CMTManagement/utils/bd_serv.py
===================================================================
--- CMTManagement/utils/bd_serv.py	(revision 421)
+++ CMTManagement/utils/bd_serv.py	(revision 421)
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+import socket, pexpect, ANSI
+import time, sys, os
+
+# Clearly having the password on the command line is not a good idea, but
+# then this entire enterprise is probably not the most security concious thing
+# I've ever built. This should be considered an experimental tool.
+#    USER = sys.argv[1]
+#    PASSWORD = sys.argv[2]
+#    PORT = sys.argv[3]
+
+def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
+    '''This forks the current process into a daemon.
+    Almost none of this is necessary (or advisable) if your daemon 
+    is being started by inetd. In that case, stdin, stdout and stderr are 
+    all set up for you to refer to the network connection, and the fork()s 
+    and session manipulation should not be done (to avoid confusing inetd). 
+    Only the chdir() and umask() steps remain as useful. 
+
+    References:
+        UNIX Programming FAQ
+        1.7 How do I get my program to act like a daemon?
+        http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
+
+        Advanced Programming in the Unix Environment
+        W. Richard Stevens, 1992, Addison-Wesley, ISBN 0-201-56317-7.
+
+    The stdin, stdout, and stderr arguments are file names that
+    will be opened and be used to replace the standard file descriptors
+    in sys.stdin, sys.stdout, and sys.stderr.
+    These arguments are optional and default to /dev/null.
+    Note that stderr is opened unbuffered, so
+    if it shares a file with stdout then interleaved output
+    may not appear in the order that you expect.
+    '''
+
+    # Do first fork.
+    try: 
+        pid = os.fork() 
+        if pid > 0:
+            sys.exit(0)   # Exit first parent.
+    except OSError, e: 
+        sys.stderr.write ("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror) )
+        sys.exit(1)
+
+    # Decouple from parent environment.
+    os.chdir("/") 
+    os.umask(0) 
+    os.setsid() 
+
+    # Do second fork.
+    try: 
+        pid = os.fork() 
+        if pid > 0:
+            sys.exit(0)   # Exit second parent.
+    except OSError, e: 
+        sys.stderr.write ("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror) )
+        sys.exit(1)
+
+    # Now I am a daemon!
+    
+    # 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())
+
+    # I now return as the daemon
+    return 0
+
+def main ():
+    USER = sys.argv[1]
+    PASSWORD = sys.argv[2]
+    PORT = int(sys.argv[3])
+    
+    #daemonize ()
+    #daemonize('/dev/null','/tmp/daemon.log','/tmp/daemon.log')
+
+    sys.stdout.write ('Daemon started with pid %d\n' % os.getpid() )
+
+    vs = ANSI.ANSI (24,80)
+    p = pexpect.spawn ('ssh %(USER)s@localhost'%locals(), timeout=9)
+    p.expect ('assword')
+    p.sendline (PASSWORD)
+    time.sleep (0.2)
+    #p.sendline ('stty -echo')
+    #time.sleep (0.2)
+    p.sendline ('export PS1="HAON "')
+    time.sleep (0.2)
+    p.expect (pexpect.TIMEOUT)
+    print p.before
+    vs.process (p.before)
+    HOST = '' # Symbolic name meaning the local host
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.bind((HOST, PORT))
+    print 'Listen'
+    s.listen(1)
+    print 'Accept'
+    while 1:
+        conn, addr = s.accept()
+        print 'Connected by', addr
+        data = conn.recv(1024)
+        print data
+
+        if data == 'exit':
+            p.sendline (exit)
+            break
+        if not data == "pass":
+            p.sendline (data)
+            time.sleep (0.1)
+            p.expect (['HAON ',pexpect.TIMEOUT])
+            #response = p.before
+            sh_response = p.before.replace ('\r', '')
+            vs.process_list (sh_response)
+        response = str (vs)
+        print response
+        sent = conn.send(response)
+        if sent < len (response):
+            print "Sent is too short"
+
+
+if __name__ == "__main__":
+#    daemonize('/dev/null','/tmp/daemon.log','/tmp/daemon.log')
+    main()
+
+
+
Index: CMTManagement/utils/chess.py
===================================================================
--- CMTManagement/utils/chess.py	(revision 421)
+++ CMTManagement/utils/chess.py	(revision 421)
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+'''This demonstrates controlling a screen oriented application (curses).
+It starts two instances of gnuchess and then pits them against each other.
+'''
+import pexpect
+import string
+import ANSI
+
+REGEX_MOVE = '(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)'
+REGEX_MOVE_PART = '(?:[0-9]|\x1b\[C)(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)'
+
+class Chess:
+
+	def __init__(self, engine = "/usr/local/bin/gnuchess -a -h 1"):
+		self.child = pexpect.spawn (engine)
+                self.term = ANSI.ANSI ()
+             
+		self.child.expect ('Chess')
+		if self.child.after != 'Chess':
+			raise IOError, 'incompatible chess program'
+                self.term.process_list (self.before)
+                self.term.process_list (self.after)
+		self.last_computer_move = ''
+        def read_until_cursor (self, r,c)
+            while 1:
+                self.child.read(1, 60)
+                self.term.process (c)
+                if self.term.cur_r == r and self.term.cur_c == c:
+                    return 1
+
+	def do_first_move (self, move):
+		self.child.expect ('Your move is')
+		self.child.sendline (move)
+                self.term.process_list (self.before)
+                self.term.process_list (self.after)
+		return move
+
+	def do_move (self, move):
+                read_until_cursor (19,60)
+		#self.child.expect ('\[19;60H')
+		self.child.sendline (move)
+		print 'do_move' move
+		return move
+
+	def get_first_computer_move (self):
+		self.child.expect ('My move is')
+		self.child.expect (REGEX_MOVE)
+#		print '', self.child.after
+		return self.child.after
+	
+	def get_computer_move (self):
+		print 'Here'
+		i = self.child.expect (['\[17;59H', '\[17;58H'])
+		print i
+		if i == 0:
+			self.child.expect (REGEX_MOVE)
+			if len(self.child.after) < 4:
+				self.child.after = self.child.after + self.last_computer_move[3]
+		if i == 1:
+			self.child.expect (REGEX_MOVE_PART)
+			self.child.after = self.last_computer_move[0] + self.child.after
+		print '', self.child.after
+		self.last_computer_move = self.child.after
+		return self.child.after
+
+	def switch (self):
+		self.child.sendline ('switch')
+
+	def set_depth (self, depth):
+		self.child.sendline ('depth')
+		self.child.expect ('depth=')
+		self.child.sendline ('%d' % depth)
+
+	def quit(self):
+		self.child.sendline ('quit')
+import sys, os
+print 'Starting...'
+white = Chess()
+white.child.echo = 1
+white.child.expect ('Your move is')
+white.set_depth(2)
+white.switch()
+
+move_white = white.get_first_computer_move()
+print 'first move white:', move_white
+
+white.do_move ('e7e5')
+move_white = white.get_computer_move()
+print 'move white:', move_white
+white.do_move ('f8c5')
+move_white = white.get_computer_move()
+print 'move white:', move_white
+white.do_move ('b8a6')
+move_white = white.get_computer_move()
+print 'move white:', move_white
+
+sys.exit(1)
+
+
+
+black = Chess()
+white = Chess()
+white.child.expect ('Your move is')
+white.switch()
+
+move_white = white.get_first_computer_move()
+print 'first move white:', move_white
+
+black.do_first_move (move_white)
+move_black = black.get_first_computer_move()
+print 'first move black:', move_black
+
+white.do_move (move_black)
+
+done = 0
+while not done:
+    move_white = white.get_computer_move()
+    print 'move white:', move_white
+
+    black.do_move (move_white)
+    move_black = black.get_computer_move()
+    print 'move black:', move_black
+   
+    white.do_move (move_black)
+    print 'tail of loop'
+
+g.quit()
+
+
Index: CMTManagement/utils/chess2.py
===================================================================
--- CMTManagement/utils/chess2.py	(revision 421)
+++ CMTManagement/utils/chess2.py	(revision 421)
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+'''This demonstrates controlling a screen oriented application (curses).
+It starts two instances of gnuchess and then pits them against each other.
+'''
+import pexpect
+import string
+import ANSI
+import sys, os, time
+
+class Chess:
+
+        def __init__(self, engine = "/usr/local/bin/gnuchess -a -h 1"):
+                self.child = pexpect.spawn (engine)
+                self.term = ANSI.ANSI ()
+             
+                #self.child.expect ('Chess')
+                #if self.child.after != 'Chess':
+                #        raise IOError, 'incompatible chess program'
+                #self.term.process_list (self.child.before)
+                #self.term.process_list (self.child.after)
+
+                self.last_computer_move = ''
+
+        def read_until_cursor (self, r,c, e=0):
+            '''Eventually something like this should move into the screen class or
+            a subclass. Maybe a combination of pexpect and screen...
+            '''
+            fout = open ('log','a')
+            while self.term.cur_r != r or self.term.cur_c != c:
+                try:
+                    k = self.child.read(1, 10)
+                except Exception, e:
+                    print 'EXCEPTION, (r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c)
+                    sys.stdout.flush()
+                self.term.process (k)
+                fout.write ('(r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c))
+                fout.flush()
+                if e:
+                    sys.stdout.write (k)
+                    sys.stdout.flush()
+                if self.term.cur_r == r and self.term.cur_c == c:
+                    fout.close()
+                    return 1
+            print 'DIDNT EVEN HIT.'
+            fout.close()
+            return 1
+
+        def expect_region (self):
+            '''This is another method that would be moved into the
+            screen class.
+            '''
+            pass
+        def do_scan (self):
+            fout = open ('log','a')
+            while 1:
+                c = self.child.read(1,10)
+                self.term.process (c)
+                fout.write ('(r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c))
+                fout.flush()
+                sys.stdout.write (c)
+                sys.stdout.flush()
+
+        def do_move (self, move, e = 0):
+                time.sleep(1)
+                self.read_until_cursor (19,60, e)
+                self.child.sendline (move)
+
+        def wait (self, color):
+            while 1:
+                r = self.term.get_region (14,50,14,60)[0]
+                r = r.strip()
+                if r == color:
+                    return
+                time.sleep (1)
+
+        def parse_computer_move (self, s):
+                i = s.find ('is: ')
+                cm = s[i+3:i+9]        
+                return cm
+        def get_computer_move (self, e = 0):
+                time.sleep(1)
+                self.read_until_cursor (19,60, e)
+                time.sleep(1)
+                r = self.term.get_region (17,50,17,62)[0]
+                cm = self.parse_computer_move (r)
+                return cm
+
+        def switch (self):
+                print 'switching'
+                self.child.sendline ('switch')
+
+        def set_depth (self, depth):
+                self.child.sendline ('depth')
+                self.child.expect ('depth=')
+                self.child.sendline ('%d' % depth)
+
+        def quit(self):
+                self.child.sendline ('quit')
+
+def LOG (s):
+    print s
+    sys.stdout.flush ()
+    fout = open ('moves.log', 'a')
+    fout.write (s + '\n')
+    fout.close()
+
+print 'Starting...'
+
+black = Chess()
+white = Chess()
+white.read_until_cursor (19,60,1)
+white.switch()
+
+done = 0
+while not done:
+    white.wait ('Black')
+    move_white = white.get_computer_move(1)
+    LOG ( 'move white:'+ move_white )
+
+    black.do_move (move_white)
+    black.wait ('White')
+    move_black = black.get_computer_move()
+    LOG ( 'move black:'+ move_black )
+   
+    white.do_move (move_black, 1)
+
+g.quit()
+
+
Index: CMTManagement/utils/chess3.py
===================================================================
--- CMTManagement/utils/chess3.py	(revision 421)
+++ CMTManagement/utils/chess3.py	(revision 421)
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+'''This demonstrates controlling a screen oriented application (curses).
+It starts two instances of gnuchess and then pits them against each other.
+'''
+import pexpect
+import string
+import ANSI
+
+REGEX_MOVE = '(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)'
+REGEX_MOVE_PART = '(?:[0-9]|\x1b\[C)(?:[a-z]|\x1b\[C)(?:[0-9]|\x1b\[C)'
+
+class Chess:
+
+	def __init__(self, engine = "/usr/local/bin/gnuchess -a -h 1"):
+		self.child = pexpect.spawn (engine)
+                self.term = ANSI.ANSI ()
+             
+#		self.child.expect ('Chess')
+	#	if self.child.after != 'Chess':
+	#		raise IOError, 'incompatible chess program'
+        #        self.term.process_list (self.before)
+        #        self.term.process_list (self.after)
+		self.last_computer_move = ''
+        def read_until_cursor (self, r,c):
+            fout = open ('log','a')
+            while 1:
+                k = self.child.read(1, 10)
+                self.term.process (k)
+                fout.write ('(r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c))
+                fout.flush()
+                if self.term.cur_r == r and self.term.cur_c == c:
+                    fout.close()
+                    return 1
+                sys.stdout.write (k)
+                sys.stdout.flush()
+
+	def do_scan (self):
+            fout = open ('log','a')
+            while 1:
+                c = self.child.read(1,10)
+                self.term.process (c)
+                fout.write ('(r,c):(%d,%d)\n' %(self.term.cur_r, self.term.cur_c))
+                fout.flush()
+                sys.stdout.write (c)
+                sys.stdout.flush()
+
+	def do_move (self, move):
+                self.read_until_cursor (19,60)
+		self.child.sendline (move)
+		return move
+	
+	def get_computer_move (self):
+		print 'Here'
+		i = self.child.expect (['\[17;59H', '\[17;58H'])
+		print i
+		if i == 0:
+			self.child.expect (REGEX_MOVE)
+			if len(self.child.after) < 4:
+				self.child.after = self.child.after + self.last_computer_move[3]
+		if i == 1:
+			self.child.expect (REGEX_MOVE_PART)
+			self.child.after = self.last_computer_move[0] + self.child.after
+		print '', self.child.after
+		self.last_computer_move = self.child.after
+		return self.child.after
+
+	def switch (self):
+		self.child.sendline ('switch')
+
+	def set_depth (self, depth):
+		self.child.sendline ('depth')
+		self.child.expect ('depth=')
+		self.child.sendline ('%d' % depth)
+
+	def quit(self):
+		self.child.sendline ('quit')
+import sys, os
+print 'Starting...'
+white = Chess()
+white.do_move('b2b4')
+white.read_until_cursor (19,60)
+c1 = white.term.get_abs(17,58)
+c2 = white.term.get_abs(17,59)
+c3 = white.term.get_abs(17,60)
+c4 = white.term.get_abs(17,61)
+fout = open ('log','a')
+fout.write ('Computer:%s%s%s%s\n' %(c1,c2,c3,c4))
+fout.close()
+white.do_move('c2c4')
+white.read_until_cursor (19,60)
+c1 = white.term.get_abs(17,58)
+c2 = white.term.get_abs(17,59)
+c3 = white.term.get_abs(17,60)
+c4 = white.term.get_abs(17,61)
+fout = open ('log','a')
+fout.write ('Computer:%s%s%s%s\n' %(c1,c2,c3,c4))
+fout.close()
+white.do_scan ()
+
+#white.do_move ('b8a6')
+#move_white = white.get_computer_move()
+#print 'move white:', move_white
+
+sys.exit(1)
+
+
+
+black = Chess()
+white = Chess()
+white.child.expect ('Your move is')
+white.switch()
+
+move_white = white.get_first_computer_move()
+print 'first move white:', move_white
+
+black.do_first_move (move_white)
+move_black = black.get_first_computer_move()
+print 'first move black:', move_black
+
+white.do_move (move_black)
+
+done = 0
+while not done:
+    move_white = white.get_computer_move()
+    print 'move white:', move_white
+
+    black.do_move (move_white)
+    move_black = black.get_computer_move()
+    print 'move black:', move_black
+   
+    white.do_move (move_black)
+    print 'tail of loop'
+
+g.quit()
+
+
Index: CMTManagement/utils/fix_cvs_files.py
===================================================================
--- CMTManagement/utils/fix_cvs_files.py	(revision 421)
+++ CMTManagement/utils/fix_cvs_files.py	(revision 421)
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+"""This is for cleaning up binary files improperly added to CVS.
+This script scans the given path to find binary files; then
+check with CVS to see if the sticky options are set to -kb;
+finally if sticky options are not -kb then use 'cvs admin' to
+set the -kb option. It ignores CVS directories, symbolic links,
+and files not known under CVS control (cvs status is 'Unknown').
+
+Run this on a CHECKED OUT module sandbox, not on the repository itself.
+After if fixes the sticky options on any files you should manually do
+a 'cvs commit' to accept the changes. Then be sure to have all users do
+a 'cvs up -A' to update the Sticky Option status.
+
+Noah Spurrier
+20030426
+"""
+import os, sys, time
+import pexpect
+
+VERBOSE = 1
+
+def is_binary (filename):
+	"""Assume that any file with a character where 
+	the 8th bit is set is binary.
+	"""
+	fin = open(filename, 'rb')
+	wholething = fin.read()
+	fin.close()
+	for c in wholething:
+		if ord(c) & 0x80:
+			return 1
+	return 0
+
+def is_kb_sticky (filename):
+	"""This checks if 'cvs status' reports '-kb' for Sticky options.
+	    If the Sticky Option status is '-ks' then this returns 1.
+	    If the status is 'Unknown' then it returns 1.
+            Otherwise 0 is returned.
+	"""
+	try:
+		s = pexpect.spawn ('cvs status %s' % filename)
+		i = s.expect (['Sticky Options:\s*(.*)\r\n',  'Status: Unknown'])
+		if i==1 and VERBOSE:
+			print 'File not part of CVS repository:', filename
+			return 1 # Pretend it's OK.
+		if s.match.group(1) == '-kb':
+			return 1
+		s = None
+	except:
+		print 'Something went wrong trying to run external cvs command.'
+		print '    cvs status %s' % filename
+		print 'The cvs command returned:'
+		print s.before
+	return 0
+
+def cvs_admin_kb (filename):
+	"""This uses 'cvs admin' to set the '-kb' sticky option.
+	"""
+	s = pexpect.run ('cvs admin -kb %s' % filename)
+	# There is a timing issue. If I run 'cvs admin' too quickly
+	# cvs sometimes has trouble obtaining the directory lock.
+	time.sleep(1)
+	
+def walk_and_clean_cvs_binaries (arg, dirname, names):
+	"""This contains the logic for processing files.
+	This is the os.path.walk callback.
+	This skips dirnames that end in CVS.
+	"""
+	if len(dirname)>3 and dirname[-3:]=='CVS':
+		return
+	for n in names:
+		fullpath = os.path.join (dirname, n)
+		if os.path.isdir(fullpath) or os.path.islink(fullpath):
+			continue
+		if is_binary(fullpath):
+			if not is_kb_sticky (fullpath):
+				if VERBOSE: print fullpath
+				cvs_admin_kb (fullpath)
+
+def main ():
+	if len(sys.argv) == 1:
+		root = '.'
+	else:
+		root = sys.argv[1]
+	os.path.walk (root, walk_and_clean_cvs_binaries, None)
+
+if __name__ == '__main__':
+	main ()
+
Index: CMTManagement/utils/ftp.py
===================================================================
--- CMTManagement/utils/ftp.py	(revision 421)
+++ CMTManagement/utils/ftp.py	(revision 421)
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+"""This demonstrates an FTP "bookmark".
+This connects to an ftp site; does a few ftp stuff; and then gives the user
+interactive control over the session. In this case the "bookmark" is to a
+directory on the OpenBSD ftp server. It puts you in the i386 packages
+directory. You can easily modify this for other sites.
+"""
+import pexpect
+import sys
+
+child = pexpect.spawn('/usr/bin/ftp ftp.openbsd.org')
+child.expect('Name .*: ')
+child.sendline('anonymous')
+child.expect('Password:')
+child.sendline('pexpect@sourceforge.net')
+child.expect('ftp> ')
+child.sendline('cd /pub/OpenBSD/3.1/packages/i386')
+child.expect('ftp> ')
+child.sendline('bin')
+child.expect('ftp> ')
+child.sendline('prompt')
+child.expect('ftp> ')
+child.sendline('pwd')
+child.expect('ftp> ')
+print("Escape character is '^]'.\n")
+sys.stdout.write (child.after)
+sys.stdout.flush()
+child.interact() # Escape character defaults to ^]
+# At this point this script blocks until the user presses the escape character
+# or until the child exits. The human user and the child should be talking
+# to each other now.
+
+# At this point the script is running again.
+print 'Left interact mode.'
+
+#
+# The rest is not strictly necessary. This just demonstrates a few functions.
+# This makes sure the child is dead; although it would be killed when Python exits.
+#
+if child.isalive():
+    child.sendline('bye') # Try to ask ftp child to exit.
+    child.kill(1) # Then try to force it.
+# Print the final state of the child. Normally isalive() should be FALSE.
+print
+if child.isalive():
+    print 'Child did not exit gracefully.'
+else:
+    print 'Child exited gracefully.'
+
+
Index: CMTManagement/utils/passmass.py
===================================================================
--- CMTManagement/utils/passmass.py	(revision 421)
+++ CMTManagement/utils/passmass.py	(revision 421)
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+'''Change passwords on the named machines.
+    passmass host1 host2 host3 . . .
+Note that login shell prompt on remote machine must end in # or $.
+'''
+
+import pexpect
+import sys, getpass
+
+USAGE = '''passmass host1 host2 host3 . . .'''
+SHELL_PROMPT = '[#\$] '
+
+def login(host, user, password):
+    child = pexpect.spawn('ssh %s@%s'%(user, host))
+    child.expect('password:')
+    child.sendline(password)
+    i = child.expect(['Permission denied', SHELL_PROMPT, 'Terminal type'])
+    if i == 0:
+        print 'Permission denied on host:', host
+        return None
+    elif i == 2:
+        child.sendline('vt100')
+        i = child.expect('[#\$] ')
+    return child
+
+def change_password(child, user, oldpassword, newpassword):
+    child.sendline('passwd %s'%user)
+    i = child.expect(['Old [Pp]assword', 'New [Pp]assword'])
+    # Root does not require old password, so it gets to bypass the next step.
+    if i == 0:
+        child.sendline(oldpassword)
+        child.expect('New [Pp]assword')
+    child.sendline(newpassword)
+    i = child.expect(['New [Pp]assword', 'Retype', 'Re-enter'])
+    if i == 0:
+        print 'Host did not like new password. Here is what it said...'
+        print child.before
+	child.send (chr(3)) # Ctrl-C
+        child.sendline('') # This should tell remote passwd command to quit.
+        return
+    child.sendline(newpassword)
+
+def main():
+    if len(sys.argv) <= 1:
+        print USAGE
+        return 1
+
+    user = raw_input('Username: ')
+    password = getpass.getpass('Current Password: ')
+    newpassword = getpass.getpass('New Password: ')
+    newpasswordconfirm = getpass.getpass('Confirm New Password: ')
+    if newpassword != newpasswordconfirm:
+        print 'New Passwords do not match.'
+        return 1
+
+    for host in sys.argv[1:]:
+        child = login(host, user, password)
+        if child == None:
+            print 'Could not login to host:', host
+            continue
+        print 'Changing password on host:', host
+        change_password(child, user, password, newpassword)
+        child.expect(SHELL_PROMPT)
+        child.sendline('exit')
+
+if __name__ == '__main__':
+    try:
+        main()
+    except pexpect.ExceptionPexpect, e:
+        print str(e)
+
+
Index: CMTManagement/utils/ssh_session.py
===================================================================
--- CMTManagement/utils/ssh_session.py	(revision 421)
+++ CMTManagement/utils/ssh_session.py	(revision 421)
@@ -0,0 +1,104 @@
+#
+# Eric S. Raymond
+#
+# Greatly modified by Nigel W. Moriarty
+# April 2003
+#
+from pexpect import *
+import os, sys
+import getpass
+import time, string
+    
+class ssh_session:
+    "Session with extra state including the password to be used."
+    def __init__(self, user, host, password=None, verbose=0):
+        self.user = user
+        self.host = host
+        self.verbose = verbose
+        self.password = password
+        self.keys = [
+            'authenticity',
+            ' password: ',
+            '@@@@@@@@@@@@',
+            'Command not found.',
+            EOF,
+            ]
+        #self.f = open('ssh.out','w+')
+        
+    def __del__(self):
+        pass
+        #self.f.close()
+            
+    def __repr__(self):
+        outl = 'class :'+self.__class__.__name__
+        for attr in self.__dict__:
+            if attr == 'password':
+                outl += '\n\t'+attr+' : '+'*'*len(self.password)
+            else:
+                outl += '\n\t'+attr+' : '+str(getattr(self, attr))
+        return outl
+
+    def __exec(self, command):
+        "Execute a command on the remote host.    Return the output."     
+        child = spawn(command, timeout=5000)
+        #child.timeout = 5000
+        if self.verbose:
+            print "-> " + command + "\n"
+            #sys.stderr.write("-> " + command + "\n")
+        seen = child.expect(self.keys)
+        #self.f.write(str(child.before) + str(child.after)+'\n')
+        if seen == 0:
+            child.sendline('yes')
+            seen = child.expect(self.keys)
+        if seen == 1:
+            if not self.password: 
+                self.password = getpass.getpass(self.user+'@'+self.host+"'s password:")
+            child.sendline(self.password)
+            child.readline()
+            for line in child:
+                print string.replace(line, "\n", "")    
+            time.sleep(5)
+            # Added to allow the background running of remote process
+            if not child.isalive():
+                seen = child.expect(self.keys)
+        if seen == 2: 
+            lines = child.readlines()
+            #self.f.write(lines)
+        if self.verbose:
+            pass
+            print "<- " + child.before +"|\n"
+        try:
+            pass
+            #self.f.write(str(child.before) + str(child.after)+'\n')
+        except:
+            pass
+        return child.before
+
+    def ssh(self, command):
+        return self.__exec("ssh -l %s %s \"%s\"" \
+                                             % (self.user,self.host,command))
+
+    def scp(self, src, dst, option=None):
+        if option:
+            return self.__exec("scp -"+option+" %s %s@%s:%s" \
+                                             % (src, self.user, self.host, dst))
+                               
+        return self.__exec("scp  %s %s@%s:%s" \
+                                             % (src, self.user, self.host, dst))
+    
+    def scpget(self, src, dst, option=None):
+        if option:
+            return self.__exec("scp -"+option+" %s@%s:%s %s " \
+                                             % (self.user, self.host, src, dst))
+                               
+        return self.__exec("scp  %s@%s:%s %s " \
+                                             % (self.user, self.host, src, dst))
+    
+                                             
+    def exists(self, file):
+        "Retrieve file permissions of specified remote file."
+        seen = self.ssh("/bin/ls -ld %s" % file)
+        if string.find(seen, "No such file") > -1:
+            return None # File doesn't exist
+        else:
+            return seen.split()[0] # Return permission field of listing.
Index: CMTManagement/utils/ssh_tunnel.py
===================================================================
--- CMTManagement/utils/ssh_tunnel.py	(revision 421)
+++ CMTManagement/utils/ssh_tunnel.py	(revision 421)
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+"""This starts an SSH tunnel to a given host.
+If the SSH process ever dies then this script will detect that and restart it.
+I use this under Cygwin to keep open encrypted tunnels to
+port 110 (POP3) and port 25 (SMTP). I set my mail client to talk to
+localhost and I keep this script running in the background.
+"""
+import pexpect
+import getpass
+import time
+
+#tunnel_command = 'ssh -C -N  -L 25:%(host)s:25 -L 110:%(host)s:110 %(user)s@%(host)s'
+tunnel_command = 'ssh -C -n -L 25:%(host)s:25 -L 110:%(host)s:110 %(user)s@%(host)s -f nothing.sh'
+nothing_script = """#!/bin/sh
+while true; do sleep 53; done
+"""
+host = raw_input('Hostname: ')
+user = raw_input('Username: ')
+X = getpass.getpass('Password: ')
+
+def start_tunnel ():
+    ssh_tunnel = pexpect.spawn (tunnel_command % globals())
+    try:
+        ssh_tunnel.expect ('password:')
+    except Exception, e:
+        print str(e)
+        print ssh_tunnel.before
+        print ssh_tunnel.after
+    time.sleep (0.1)
+    ssh_tunnel.sendline (X)
+    time.sleep (60) # Cygwin is slow to update process status.
+    ssh_tunnel.expect (pexpect.EOF)
+
+def main ():
+    while 1:
+        ps = pexpect.spawn ('ps')
+        time.sleep (1)
+        index = ps.expect (['/usr/bin/ssh', pexpect.EOF, pexpect.TIMEOUT])
+        if index == 2:
+            print 'TIMEOUT in ps command...'
+            print ps.before
+            print ps.after
+        if index == 1:
+            print time.asctime(),
+            print 'restarting tunnel'
+            start_tunnel ()
+            time.sleep (60)
+	    print 'tunnel OK'
+        else:
+            # print 'tunnel OK'
+            time.sleep (60)
+
+if __name__ == '__main__':
+    main ()
+
+
Index: CMTManagement/utils/sshls.py
===================================================================
--- CMTManagement/utils/sshls.py	(revision 421)
+++ CMTManagement/utils/sshls.py	(revision 421)
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+'''This runs "ls -l" on a remote host using SSH.
+    At the prompts enter hostname, user, and password.
+'''
+import pexpect
+import getpass
+
+host = raw_input('Hostname: ')
+user = raw_input('User: ')
+password = getpass.getpass('Password: ')
+
+child = pexpect.spawn("ssh -l %s %s /bin/ls -l"%(user, host))
+
+child.expect('password:')
+child.sendline(password)
+
+child.expect(pexpect.EOF)
+
+print child.before
+
Index: CMTManagement/utils/uptime.py
===================================================================
--- CMTManagement/utils/uptime.py	(revision 421)
+++ CMTManagement/utils/uptime.py	(revision 421)
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+"""This displays uptime information using uptime.
+This is redundant perhaps, but it demonstrates expecting for a
+regular expression that uses subgroups.
+"""
+
+import pexpect
+import re
+
+# There are many different styles of uptime results.
+# I try to parse them all. Yeee!
+#
+# [x86] Linux 2.4 (Redhat 7.3)
+#  2:06pm  up 63 days, 18 min,  3 users,  load average: 0.32, 0.08, 0.02
+# [x86] Linux 2.4.18-14 (Redhat 8.0)
+#  3:07pm  up 29 min,  1 user,  load average: 2.44, 2.51, 1.57
+# [PPC - G4] MacOS X 10.1 SERVER Edition
+# 2:11PM  up 3 days, 13:50, 3 users, load averages: 0.01, 0.00, 0.00
+# [Sparc - R220] Sun Solaris (8)
+#  2:13pm  up 22 min(s),  1 user,  load average: 0.02, 0.01, 0.01
+# [x86] Linux 2.4.18-14 (Redhat 8)
+# 11:36pm  up 4 days, 17:58,  1 user,  load average: 0.03, 0.01, 0.00
+# AIX jwdir 2 5 0001DBFA4C00
+#  09:43AM   up  23:27,  1 user,  load average: 0.49, 0.32, 0.23
+
+# This parses uptime output into the major groups using
+# regex group matching.
+p = pexpect.spawn ('uptime')
+p.expect('up\s+(.*?),\s+([0-9]+) users?,\s+load averages?: ([0-9]+\.[0-9][0-9]), ([0-9]+\.[0-9][0-9]), ([0-9]+\.[0-9][0-9])')
+match = p.match
+duration, users, av1, av5, av15 = match.groups()
+
+# The duration is a little harder to parse because of all the different
+# styles of uptime. I'm sure there is a way to do this all at once with
+# one single regex, but I bet it would be hard to read and maintain.
+# If anyone wants to send me a version using a single regex I'd be
+# happy to see it.
+days = '0'
+if 'day' in duration:
+    match = re.search('([0-9]+)\s+day',duration)
+    days = match.group(1)
+hours = '0:0'
+if ':' in duration:
+    match = re.search('([0-9]+:[0-9]+)',duration)
+    hours = match.group(1)
+mins = '0'
+if 'min' in duration:
+    match = re.search('([0-9]+)\s+min',duration)
+    mins = match.group(1)
+
+# Print the parsed fields in CSV format.
+print 'days, hours, minutes, users, cpu avg 1 min, cpu avg 5 min, cpu avg 15 min'
+print '%s, %s, %s, %s, %s, %s, %s' % (days, hours, mins, users, av1, av5, av15)
+
