#! /usr/bin/env python3 import cgitb; cgitb.enable() import cgi import os import fcntl import re import sys import pointscli import teams cat_re = re.compile(r'^[a-z]+$') points_re = re.compile(r'^[0-9]+$') def dbg(*vals): print('Content-type: text/plain\n\n') print(*vals) points_by_cat = {} points_by_team = {} try: for line in open('puzzler.dat'): line = line.strip() cat, team, pts = line.split('\t') pts = int(pts) points_by_cat[cat] = max(points_by_cat.get(cat, 0), pts) points_by_team.setdefault((team, cat), set()).add(pts) except IOError: pass f = cgi.FieldStorage() cat = f.getfirst('c') points = f.getfirst('p') team = f.getfirst('t') passwd = f.getfirst('w') key = f.getfirst('k') verboten = ['key', 'index.html'] def start_html(title): print('''Content-type: text/html %s

%s

''' % (title, title)) def end_html(): print('') def safe_join(*args): safe = [] for a in args: if not a: return None else: a = a.replace('..', '') a = a.replace('/', '') safe.append(a) ret = '/'.join(safe) if os.path.exists(ret): return ret def dump_file(fn): f = open(fn, 'rb') while True: d = f.read(4096) if not d: break sys.stdout.buffer.write(d) def show_cats(): start_html('Categories') print('') end_html() def show_puzzles(cat, cat_dir): start_html('Open in %s' % cat) opened = points_by_cat.get(cat, 0) puzzles = sorted([int(v) for v in os.listdir(cat_dir)]) if puzzles: print('') else: print('

None (someone is slacking)

') end_html() def show_puzzle(cat, points, points_dir): # Show puzzle in cat for points start_html('%s for %s' % (cat, points)) fn = os.path.join(points_dir, 'index.html') if os.path.exists(fn): print('
') dump_file(fn) print('
') print('') print('
') print('' % cat) print('' % points) print('Team:
') print('Password:
') print('Key:
') print('') print('
') end_html() def win(cat, team, points): start_html('Winner!') points = int(points) pointscli.submit(cat, team, points) end_html() f = open('puzzler.dat', 'a') fctnl.lockf(f, LOCK_EX) f.write('%s\t%s\t%d\n' % (cat, team, points)) def main(): cat_dir = safe_join('puzzles', cat) points_dir = safe_join('puzzles', cat, points) if not cat_dir: # Show categories show_cats() elif not points_dir: # Show available puzzles in category show_puzzles(cat, cat_dir) elif not (team and passwd and key): fn = f.getfirst('f') if fn in verboten: fn = None fn = safe_join('puzzles', cat, points, fn) if fn: # Provide a file from this directory print('Content-type: application/octet-stream') print() dump_file(fn) else: show_puzzle(cat, points, points_dir) else: thekey = open('%s/key' % points_dir).read().strip() if not teams.chkpasswd(team, passwd): start_html('Wrong password') end_html() elif key != thekey: show_puzzle(cat, points, points_dir) elif points_by_team.get((team, cat)): start_html('Greedy greedy') end_html() else: win(cat, team, points) main()