#! /usr/bin/python import cgi, os, string, time form = cgi.FieldStorage() def txtfilter(s): l = filter(lambda c: c in "!$*,-.0123456789:@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}", s) return string.join(l, '') def identity(s): return s def fieldlist(name, filter=txtfilter): result = [] if form.has_key(name): field = form[name] if type(field) is type([]): for f in field: result.append(filter(f.value)) else: result.append(filter(field.value)) return result def goodmatch(addr1, addr2): l1 = string.split(addr1, '.') if len(l1) >= 4: del l1[-1] l2 = string.split(addr2, '.') if len(l2) >= 4: del l2[-1] return l1 == l2 class Entry: Notice = '' FIELDS = [('server', 128), ('desc', 128), ('icon', 64), ('orig', 64), ('dele1', 64), ('dele2', 64)] def __init__(self, f): self.pos = f.tell() for fname, flen in self.FIELDS: s = f.read(flen) if not s: self.filled = 0 break setattr(self, fname, string.rstrip(s)) else: self.filled = 1 def __nonzero__(self): return self.filled def write(self, f): if not getattr(self, 'icon', ''): try: import random lst = os.listdir('../htdocs/images') except: pass else: lst = filter(lambda x: x[-4:]=='.png' and 'A'<=x[0]<='Z', lst) if lst: self.icon = random.choice(lst) f.seek(self.pos) for fname, flen in self.FIELDS: data = getattr(self, fname, '')[:flen] f.write(data + ' '*(flen-len(data))) def main(): DATABASE = 'servers' SIZEMAX = 65536 Serv = 0 Orig = 1 Dele1 = 2 REMOTE_ADDR = os.environ['REMOTE_ADDR'] REMOTE_ID = REMOTE_ADDR + time.strftime('@%d%m', time.localtime(time.time())) OPS = {} for srv in fieldlist('d'): OPS[srv] = 'd' for srv in fieldlist('a'): OPS[srv] = 'a' desc = (fieldlist('desc', identity) or [''])[0] f = open(DATABASE, 'r+b') freelist = [] published = [] while 1: e = Entry(f) if not e: break if e.server: if OPS.get(e.server) == 'a': validdesc = desc and goodmatch(REMOTE_ADDR, e.orig) if e.dele1 or e.dele2 or (validdesc and desc != e.desc): e.dele1 = e.dele2 = '' # re-enable server if validdesc: e.desc = desc e.write(f) del OPS[e.server] elif OPS.get(e.server) == 'd': if goodmatch(REMOTE_ADDR, e.orig) or ( REMOTE_ID != e.dele1 and REMOTE_ID != e.dele2): if goodmatch(REMOTE_ADDR, e.orig): e.server = '' # remove server elif e.dele1 == '': e.dele1 = REMOTE_ID elif e.dele2 == '': e.dele2 = REMOTE_ID else: e.server = '' # remove server e.write(f) Entry.Notice = 'd' if e.server: published.append((e.pos, e)) else: freelist.append(e) for srv, action in OPS.items(): if action == 'a': if freelist: e = freelist[-1] del freelist[-1] else: f.seek(0, 2) e = Entry(f) if e.pos >= SIZEMAX: raise Exception("Sorry, server database too big") hostname = string.split(srv, ':')[0] if '.' not in hostname: Entry.Notice = 'Server hostname "%s" incomplete.' % hostname else: import socket try: result = socket.gethostbyaddr(hostname) except socket.error, e: Entry.Notice = ('%s: %s' % (hostname, e)) else: if result[0] == 'projects.sourceforge.net': # ???? Entry.Notice = ('Server hostname "%s" does not exist.' % hostname) else: e.server = srv e.icon = '' e.desc = desc e.orig = REMOTE_ADDR e.dele1 = e.dele2 = '' e.write(f) published.append((e.pos, e)) Entry.Notice = 'a' f.close() published.sort() return map(lambda (pos, e): e, published) def publish_list(serverlist): url = (fieldlist('url', identity) or ['http://127.0.0.1:8000'])[0] query = [] for s in fieldlist('redirected'): query.append('redirected=' + s) for s in serverlist: query.append('s=' + txtfilter(s.server)) url = url + '?' + string.join(query, '&') for s in fieldlist('frag'): url = url + '#' + s print 'Content-Type: text/html' print 'Location:', url print print '' print 'Please click here to continue.' % url print '' def publish_default(serverlist): import htmlentitydefs text_to_html = {} for key, value in htmlentitydefs.entitydefs.items(): text_to_html[value] = '&' + key + ';' for i in range(32): text_to_html[chr(i)] = '?' def htmlquote(s, getter=text_to_html.get): lst = [] for c in s: lst.append(getter(c, c)) return string.join(lst, '') REMOTE_ADDR = os.environ['REMOTE_ADDR'] f = open('started.html', 'r') header, row, footer = string.split(f.read(), '\\') f.close() import sys print 'Content-Type: text/html' print sys.stdout.write(header % "List of registered Internet Servers") counter = 0 for s in serverlist: if s.icon: s1 = '' % s.icon else: s1 = '' lst = string.split(txtfilter(s.server), ':') hostname, port, udpport, httpport = (lst+['?','?','?','?'])[:4] s2 = '%s' % hostname try: int(httpport) except ValueError: pass else: s2 = '%s:%s' % (hostname, httpport, s2, port) s2 = '' + s2 + '' if s.desc: s2 = s2 + '   playing  %s' % htmlquote(s.desc) if goodmatch(REMOTE_ADDR, s.orig): s2 = s2 + '      (if this server is dead, click here to remove it)' % s.server sys.stdout.write(row % (s1, ('#C0D0D0', '#E0D0A8')[counter&1], s2)) counter = counter + 1 if not serverlist: sys.stdout.write(row % ('', '#FFFFFF', 'There is no registered server at the moment.')) sys.stdout.write(footer % "If your browser understands Java, you can click on a server to join the game. Note however that you will still need to install the whole Python client to benefit from the background musics, as this feature is missing from the Java client.

This list might contain already-dead servers; such 'zombies' disappear after some time.") def publish_raw(serverlist): print 'Content-Type: text/plain' print print 'Raw list produced for', os.environ['REMOTE_ADDR'] print for s in serverlist: print repr((s.server, s.desc, s.icon, s.orig)) def publish_img(serverlist): import sys print 'Content-Type: image/png' print f = open('sfbub.png', 'rb') sys.stdout.write(f.read()) f.close() def publish_register(serverlist): if Entry.Notice == 'a': banner = 'The game server is now registered to SourceForge.' elif Entry.Notice == 'd': banner = ('Server unregistered ' 'from SourceForge.') elif Entry.Notice == '': if fieldlist('a'): banner = "The game server is already registered to SourceForge." elif fieldlist('d'): banner = 'The game server was already absent from SourceForge.' else: publish_default(serverlist) return else: # errors banner = ('%s

' % Entry.Notice + 'If you are behind a firewall or NAT device (e.g. ADSL routers) you can still make your server reachable but it requires manual configuration. (Instructions not available yet -- sorry)') f = open('started.html', 'r') header, row, footer = string.split(f.read(), '\\') f.close() import sys print 'Content-Type: text/html' print sys.stdout.write(header % banner) sys.stdout.write(footer % 'Press Back to come back to the main page.') try: slist = main() cmd = (fieldlist('cmd') or ['?'])[0] publish = globals().get('publish_'+cmd, publish_default) publish(slist) except: import traceback, sys print "Content-Type: text/plain" print print "ERROR REPORT" print traceback.print_exc(file=sys.stdout)