#!/usr/bin/python import random, time, os, popen2, fcntl, crypt from select import select PORT = 5900#+random.randint(0,9) TUNNELS_COMMAND = "/opt/sun-jdk-1.5.0.06/bin/java Tunnels" print PORT PROMPT = "? " hasher = 'th' def md5hash( _hash, s ): return crypt.crypt( _hash, s ) # number of clients to start worrying about # idles, etc. PRUNE_THRESHOLD = 10 IDLE_TIME = 900 # open passwords passwords = {} passwd = open("server_files/passwd") for pw in passwd.readlines(): _hash, name, flags = pw.split(':') passwords[name] = _hash BANNER = open("server_files/banner").read() HELP = open("server_files/help").read() ABOUT = open("server_files/about").read() NEW_USER = open("server_files/newbie").read() NEW_PASS = open("server_files/newpw").read() README = open("README").read() INACTIVE_WARN = open("server_files/inactive").read() def validate(name, password): if not passwords.has_key(name): return False if passwords[name] != md5hash(hasher, password): return False return True #u32 def write_pw(): os.system("cp server_files/passwd server_files/passwd-old") f = open("server_files/passwd", "w") for key, value in passwords.items(): f.write("%s:%s:0\n"%(value, key)) f.close() # Make user name allowable # Thwart attempts to mess up terminals with /033... # since it seems inevitable that someday usernames will be displayed on a # web site as well, we also thwart attempts to include html tags (< and >). def filter(name, cap=16): if not name: return "[NULL]" name = list(name.replace(' ', '__')) newname = [] for letter in name: if ord(letter) >= 32 and letter != '<' and letter != '>': newname.append(letter) if newname[0].isspace(): name[0] = '_' if newname[-1].isspace(): name[-1] = '_' return ''.join(newname)[0:cap] # master list. clients = [] class Client: # List to hold the game watchers. listeners = None # Logged in? logged_in = False last_action = 0 exit_code = 0 # user name login = "" what_doing = "Menu" playing = False # Connection and address of client conn, addr = None, None pipe = None def __init__(self, c, a): self.conn = c self.addr = a self.last_action = time.time() clients.append(self) self.listeners = [] def finalize(self): for listener in self.listeners: listener.close() if (self.exit_code == -1): self.conn.close() clients.remove(self) else: self.what_doing = "Waiting" def watch_game(watcher, target): watcher.what_doing = "Watching "+target.login watcher.playing = False target.listeners.append(watcher) watcher.conn.send("\n\nWATCHING %s\n\n"%target.login); target.conn.send("\n\033[46;31m%s is watching your game.\033[0m\n" %watcher.login) while target.playing: message = watcher.conn.recv(80) if (message): target.conn.send("\n\033[46;30m%s: \033[1m%s\033[0m\n" %(watcher.login, filter(message.strip(), 80))) def play_game(client): client.playing = True client.what_doing = "Playing" if not client.pipe: client.pipe = popen2.Popen3(TUNNELS_COMMAND) client.pipe.tochild.write( str(client.addr)+" User: "+client.login+"\n") client.pipe.tochild.flush() for listener in client.listeners: listener.setblocking(0) ins, outs = client.pipe.tochild, client.pipe.fromchild client.conn.setblocking(0) fl = fcntl.fcntl(ins, fcntl.F_GETFL) fcntl.fcntl(outs, fcntl.F_SETFL, fl | os.O_NDELAY) fl = fcntl.fcntl(outs, fcntl.F_GETFL) fcntl.fcntl(outs, fcntl.F_SETFL, fl | os.O_NDELAY) while 1: client.exit_code = client.pipe.poll() if (client.exit_code != -1): break client.last_action = time.time() try: readable, writable, e = select([client.conn, outs], [], []) except: print "Thread for %s (%s) pipe broken.\n"%(client.login, client.addr) break client.exit_code = client.pipe.poll() if (client.exit_code != -1): break for item in readable: if item is client.conn: try: data = item.recv(1024) except: print "Broken client 141" break try: ins.write(data) ins.flush() except: print "FAIL 146" break for listener in client.listeners: try: listener.conn.send(data) except: listeners.remove(listener) print "Bumped %s %s\n"%(listener.login, listener.what_doing) else: while 1: data = [] try: # get it all now so it doesn't hang data.append(item.read(1024)) except: break data = ''.join(data) try: client.conn.send(data) except: client.exit_code = -1 print "client disconnect" break for listener in client.listeners: try: listener.conn.send(data) except: listeners.remove(listener) print "Bumped %s %s\n"%(listener.login, listener.what_doing) client.exit_code = client.pipe.poll() print "Tunnels exited" data = os.popen("tail -n 6 scores.txt").read() client.conn.send(data + "\nGoodbye.\n") os.system("python sortscore.py 100") client.playing = False client.what_doing = "Nothing" client.pipe = None for cl in clients: if (client.login == cl.login and not cl is client): cl.exit_code = 0 cl.finalize() for listener in client.listeners: listener.conn.send("\nAlright, this show's over!\n") listeners = [] #client.pipe.close() client.finalize() def menu (clients, client): while 1: if (client.exit_code == -1): return client.conn.setblocking(1) client.last_action = time.time() client.conn.send(PROMPT) msg = client.conn.recv(80) if msg: ch = msg[0].lower() else: ch = '' if (ch == '?'): client.conn.send(HELP%client.login) elif (ch == 'n'): if (client.logged_in): client.conn.send("Already logged in.\n") continue client.conn.send(NEW_USER) client.conn.send(PROMPT) username = filter(client.conn.recv(18)) if (len(username) < 1): client.conn.send("Your username is too short.\n") continue if passwords.has_key(username): client.conn.send("The username %s is taken.\n" % username) continue client.conn.send(NEW_PASS) client.conn.send(PROMPT) pw = filter(client.conn.recv(18)) if (len(pw) < 5): client.conn.send("Your password is too short!\n") continue client.conn.send("Your username and password: %s, %s\n" % (username, pw)) client.conn.send("Thank you for registering.\n") passwords[username] = md5hash(hasher, pw) write_pw() elif (ch == 'q'): client.conn.send("Farewell, %s\n"%client.login) return elif (ch == 'l'): if (client.logged_in): client.conn.send("Already logged in.\n") continue client.conn.send("Username? "); username = filter(client.conn.recv(18)) client.conn.send("Password? "); pw = filter(client.conn.recv(18)) if validate(username, pw): client.login = username client.logged_in = True client.conn.send("Welcome %s!\n"%client.login) for cl in clients: if cl.login == client.login and not cl is client: if cl.pipe: client.pipe = cl.pipe client.conn.send( "You Have a game already in progress.\n" ) cl.finalize() else: client.conn.send("Wrong username/password.\n") time.sleep(1) elif (ch == 'r'): client.conn.send(README) elif (ch == 'p'): if not client.logged_in: client.conn.send("You have to log in first.\n") continue play_game(client) elif (ch == ' '): for target in clients: if target.what_doing != "Menu": continue target.conn.send( "\n\033[36m%s: \033[1m%s\033[0m\n" %(client.login, filter(msg.strip(), 80))) elif (ch == 'c'): if not client.logged_in: client.conn.send("You have to log in first.\n") continue client.conn.send(NEW_PASS) client.conn.send(PROMPT) pw = filter(client.conn.recv(18)) if (len(pw) < 5): client.conn.send("Your password is too short!\n") continue client.conn.send("Your username and password: %s, %s\n" % (client.login, pw)) passwords[client.login] = md5hash(hasher, pw) write_pw() elif (ch == 'a'): client.conn.send(ABOUT) elif (ch == 'w'): if not client.logged_in: client.conn.send("You have to log in first.\n") continue client.conn.send("Watch who's game? (Username): ") username = filter(client.conn.recv(18)) target = None for cl in clients: if cl.login == username: target = cl break if not target: client.conn.send("That person is not online.\n") continue if not target.playing: client.conn.send("That person isn't playing.\n") continue watch_game(client, cl) client.finalize() return elif (ch == 'v'): client.conn.send(" -- Currently Online: --\n\n") for cl in clients: client.conn.send(" %18s %26s\t%s\n" % (cl.login, cl.addr[0], cl.what_doing)) else: client.conn.send("Unrecognized command.\n") client.conn.send(HELP%client.login) def server(clients, client): client.conn.send(BANNER%time.ctime()) client.conn.send(HELP%client.login) # menu menu(clients, client) client.finalize() from socket import * import thread sock = socket(AF_INET, SOCK_STREAM) sock.bind(('', PORT)) sock.listen(5) def idle_watcher(clients): while 1: time.sleep(5) if len(clients) >= PRUNE_THRESHOLD: for cl in clients: idle = time.time() - cl.last_action print cl.login, idle if idle > IDLE_TIME and not cl.playing: cl.conn.send( "Disconnected for inactivity.\n") print "Disconnected ", cl.login, cl.addr print " Idle for: ", idle try: cl.finalize() except: pass elif idle > IDLE_TIME/2: cl.conn.send( INACTIVE_WARN %((IDLE_TIME-idle)/60)) thread.start_new_thread(idle_watcher, (clients,)) while 1: c, a = sock.accept() cln = Client(c, a) thread.start_new_thread(server, (clients, cln))