Mostly cosmetics

This commit is contained in:
Neale Pickett 2009-10-08 16:42:26 -06:00
parent d9d7ddfe53
commit ec202f23f5
11 changed files with 179 additions and 155 deletions

63
ctf.css
View File

@ -30,6 +30,49 @@ h1:first-child:before {
content: "Capture The Flag: "; content: "Capture The Flag: ";
} }
/*** left side bar ***/
#navigation {
position: fixed;
background: #222;
opacity: 0.9;
top: 80px;
left: 0px;
padding: 0;
}
#navigation ul {
list-style: none;
padding: 0;
margin: 0;
}
#navigation li a {
display: block;
height: 25px;
width: 90px;
padding: 5px;
margin: 5px;
background: inherit;
border-right: 4px solid #444;
color: #999;
text-transform: lowercase;
font-size: 0.9em;
}
#navigation li a:hover {
color: #f4f4f4;
background: #333;
border-right: 4px solid #2a2;
}
#navigation li .active {
color: #999;
background: #333;
border-right: 4px solid #444;
}
/**** body ****/ /**** body ****/
a img { a img {
@ -64,6 +107,18 @@ th, td {
vertical-align: top; vertical-align: top;
} }
p {
line-height: 1.4em;
margin-bottom: 20px;
color: #f4f4f4;
}
/**** special cases ****/
.wide {
max-width: inherit;
}
.scoreboard { .scoreboard {
background: #222; background: #222;
} }
@ -72,12 +127,6 @@ th, td {
height: 400px; height: 400px;
} }
p {
line-height: 1.4em;
margin-bottom: 20px;
color: #f4f4f4;
}
.solved { .solved {
text-decoration: line-through; text-decoration: line-through;
} }
@ -93,3 +142,5 @@ table.pollster td {
table.pollster thead { table.pollster thead {
font-weight: bold; font-weight: bold;
} }

View File

@ -28,8 +28,10 @@ if 'home' in os.environ.get('SCRIPT_FILENAME', ''):
} }
else: else:
# An actual installation # An actual installation
config = {'global': config = {
{'data_dir': '/var/lib/ctf', 'global':
{
'data_dir': '/var/lib/ctf',
'base_url': '/', 'base_url': '/',
'css_url': '/ctf.css', 'css_url': '/ctf.css',
'disabled_dir': '/var/lib/ctf/disabled', 'disabled_dir': '/var/lib/ctf/disabled',
@ -37,13 +39,17 @@ else:
'house_team': 'dirtbags', 'house_team': 'dirtbags',
'passwd': '/var/lib/ctf/passwd', 'passwd': '/var/lib/ctf/passwd',
'team_colors': team_colors, 'team_colors': team_colors,
},
'pollster':
{
'poll_interval': 60, 'poll_interval': 60,
'poll_timeout': 0.5, 'poll_timeout': 0.5,
'heartbeat_dir': '/var/lib/pollster', 'heartbeat_dir': '/var/lib/pollster',
'poll_dir': '/var/lib/www', 'results': '/var/lib/pollster/status.html',
}, },
'puzzler': 'puzzler':
{'dir': '/usr/lib/www/puzzler', {
'dir': '/usr/lib/www/puzzler',
'cgi_url': '/puzzler.cgi', 'cgi_url': '/puzzler.cgi',
'base_url': '/puzzler', 'base_url': '/puzzler',
'keys_file': '/usr/lib/ctf/puzzler.keys', 'keys_file': '/usr/lib/ctf/puzzler.keys',
@ -71,22 +77,37 @@ def datafile(filename):
def url(path): def url(path):
return base_url + path return base_url + path
def start_html(title): def start_html(title, hdr='', cls=''):
ret = []
if os.environ.get('GATEWAY_INTERFACE'): if os.environ.get('GATEWAY_INTERFACE'):
print('Content-type: text/html') ret.append('Content-type: text/html')
print() ret.append('')
print('''<?xml version="1.0" encoding="UTF-8"?> ret.append('''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC <!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>%s</title> <title>%(title)s</title>
<link rel="stylesheet" href="%s" type="text/css" /> <link rel="stylesheet" href="%(css)s" type="text/css" />
%(hdr)s
</head> </head>
<body> <body class="%(class)s">
<h1>%s</h1> <h1>%(title)s</h1>
''' % (title, css, title)) <div id="navigation">
<ul>
<li><a href="%(base)s">Home</a></li>
<li><a href="%(base)sintro.html">Intro/Rules</a></li>
<li><a href="%(base)spuzzler.cgi">Puzzles</a></li>
<li><a href="%(base)sscoresboard.cgi">Scoreboard</a></li>
</ul>
</div>
''' % {'title': title,
'css': css,
'hdr': hdr,
'base': base_url,
'class': cls})
return '\n'.join(ret)
def end_html(): def end_html():
print('</body></html>') return '</body></html>'

View File

@ -66,9 +66,10 @@ def start_html(title):
if passwd: if passwd:
c['passwd'] = passwd c['passwd'] = passwd
print(c) print(c)
config.start_html(title) print(config.start_html(title))
end_html = config.end_html def end_html():
print(config.end_html())
def safe_join(*args): def safe_join(*args):
safe = list(args[:1]) safe = list(args[:1])

View File

@ -7,26 +7,7 @@ import string
from . import teams from . import teams
from . import config from . import config
def head(title):
return '''<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Team Registration</title>
<link rel="stylesheet" href="%s" type="text/css" />
</head>
<body>
<h1>%s</h1>
''' % (config.css, title)
def foot():
return '''</body></html>'''
def main(): def main():
print('Content-type: text/html')
print()
f = cgi.FieldStorage() f = cgi.FieldStorage()
team = f.getfirst('team', '') team = f.getfirst('team', '')

View File

@ -15,20 +15,8 @@ def main():
categories = [(cat, s.cat_points(cat)) for cat in s.categories()] categories = [(cat, s.cat_points(cat)) for cat in s.categories()]
print('Content-type: text/html')
print('Refresh: 10') print('Refresh: 10')
print() print(config.start_html('Scoreboard', cls='wide'))
print('''<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Scoreboard</title>
<link rel="stylesheet" href="%sctf.css" type="text/css" />
</head>
<body style="max-width: inherit;">
<h1>Scoreboard</h1>
''' % config.base_url)
print('<table class="scoreboard">') print('<table class="scoreboard">')
print('<tr>') print('<tr>')
print('<th>Overall</th>') print('<th>Overall</th>')
@ -71,9 +59,8 @@ def main():
<p class="histogram"> <p class="histogram">
<img src="histogram.png" alt="scores over time" /> <img src="histogram.png" alt="scores over time" />
</p> </p>
''')
</body> print(config.end_html())
</html>''')
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -24,9 +24,6 @@ def reap():
except OSError: except OSError:
pass pass
def sigchld(signum, frame):
do_reap = True
def main(): def main():
p = optparse.OptionParser() p = optparse.OptionParser()
p.add_option('-p', '--genpass', dest='cat', default=None, p.add_option('-p', '--genpass', dest='cat', default=None,
@ -39,13 +36,11 @@ def main():
pointsrv = pointsd.start() pointsrv = pointsd.start()
flagsrv = flagd.start() flagsrv = flagd.start()
signal.signal(signal.SIGCHLD, sigchld)
s = pointsrv.store s = pointsrv.store
slen = 0 slen = 0
while True: while True:
if do_reap:
reap()
asyncore.loop(timeout=30, use_poll=True, count=1) asyncore.loop(timeout=30, use_poll=True, count=1)
reap()
if len(s) > slen: if len(s) > slen:
slen = len(s) slen = len(s)
chart(s) chart(s)

View File

@ -17,6 +17,37 @@ opts, args = p.parse_args()
keys = [] keys = []
js = '''
<script type="text/javascript">
function readCookie(key) {
var s = key + '=';
var toks = document.cookie.split(';');
for (var i = 0; i < toks.length; i++) {
var tok = toks[i];
while (tok.charAt(0) == ' ') {
tok = tok.substring(1, tok.length);
}
if (tok.indexOf(s) == 0) {
return tok.substring(s.length, tok.length);
}
}
return null;
}
function getTeamInfo() {
team = readCookie('team');
passwd = readCookie('passwd');
if (team != null) {
document.getElementById("form").t.value = team;
}
if (passwd != null) {
document.getElementById("form").w.value = passwd;
}
}
window.onload = getTeamInfo;
</script>
'''
for cat in os.listdir(opts.puzzles): for cat in os.listdir(opts.puzzles):
dirname = os.path.join(opts.puzzles, cat) dirname = os.path.join(opts.puzzles, cat)
for points in os.listdir(dirname): for points in os.listdir(dirname):
@ -43,47 +74,7 @@ for cat in os.listdir(opts.puzzles):
title = '%s for %s points' % (cat, points) title = '%s for %s points' % (cat, points)
f = open(os.path.join(outdir, 'index.html'), 'w', encoding='utf-8') f = open(os.path.join(outdir, 'index.html'), 'w', encoding='utf-8')
f.write('''<?xml version="1.0" encoding="UTF-8"?> f.write(config.start_html(title, js))
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>%(title)s</title>
<link rel="stylesheet" href="%(css)s" type="text/css" />
<script type="text/javascript">
function readCookie(key) {
var s = key + '=';
var toks = document.cookie.split(';');
for (var i = 0; i < toks.length; i++) {
var tok = toks[i];
while (tok.charAt(0) == ' ') {
tok = tok.substring(1, tok.length);
}
if (tok.indexOf(s) == 0) {
return tok.substring(s.length, tok.length);
}
}
return null;
}
function getTeamInfo() {
team = readCookie('team');
passwd = readCookie('passwd');
if (team != null) {
document.getElementById("form").t.value = team;
}
if (passwd != null) {
document.getElementById("form").w.value = passwd;
}
}
window.onload = getTeamInfo;
</script>
</head>
<body>
<h1>%(title)s</h1>
''' % {'title': title,
'css': config.css})
if readme: if readme:
f.write('<div class="readme">%s</div>\n' % readme) f.write('<div class="readme">%s</div>\n' % readme)
if files: if files:
@ -105,11 +96,10 @@ for cat in os.listdir(opts.puzzles):
<input type="submit" /> <input type="submit" />
</fieldset> </fieldset>
</form> </form>
</body>
</html>
''' % {'cgi': config.get('puzzler', 'cgi_url'), ''' % {'cgi': config.get('puzzler', 'cgi_url'),
'cat': cat, 'cat': cat,
'points': points}) 'points': points})
f.write(config.end_html())
f = open(opts.keyfile, 'w', encoding='utf-8') f = open(opts.keyfile, 'w', encoding='utf-8')
for key in keys: for key in keys:

View File

@ -1,8 +1,9 @@
#! /bin/sh #! /bin/sh
case "$REMOTEADDR" in ip=$(echo $UDPREMOTEADDR | cut -d: -f1)
10.0.0.[2-254]) case "$ip" in
touch /var/lib/pollster/$REMOTEADDR 10.0.0.*)
touch /var/lib/pollster/$ip
;; ;;
esac esac
echo 'Hello.'

View File

@ -13,10 +13,10 @@ from ctf import config
from ctf import pointscli from ctf import pointscli
DEBUG = False DEBUG = False
POLL_INTERVAL = config.get('poll_interval') POLL_INTERVAL = config.get('pollster', 'poll_interval')
IP_DIR = config.get('heartbeat_dir') IP_DIR = config.get('pollster', 'heartbeat_dir')
REPORT_PATH = config.get('poll_dir') REPORT_PATH = config.get('pollster', 'results')
SOCK_TIMEOUT = config.get('poll_timeout') SOCK_TIMEOUT = config.get('pollster', 'poll_timeout')
class PointSubmitter(threading.Thread): class PointSubmitter(threading.Thread):
''' Pulls point allocations from the queue and submits them. ''' ''' Pulls point allocations from the queue and submits them. '''
@ -184,16 +184,9 @@ while True:
out = open(REPORT_PATH, 'w') out = open(REPORT_PATH, 'w')
except Exception as e: except Exception as e:
out = None out = None
pass
if out is not None:
out.write('<html>\n<head>\n')
out.write('<title>Pollster Results</title>\n')
out.write('<link rel="stylesheet" href="ctf.css" type="text/css" media="all" />\n')
out.write('</head><body>\n<h1>Polling Results</h1>\n')
out.write(config.start_html('Team Service Availability'))
for ip in ips: for ip in ips:
# check file name format is ip # check file name format is ip
if ip_re.match(ip) is None: if ip_re.match(ip) is None:
continue continue
@ -240,8 +233,7 @@ while True:
sleep_time = POLL_INTERVAL - exec_time sleep_time = POLL_INTERVAL - exec_time
if out is not None: if out is not None:
out.write('<p><b>Next poll in: %ds</b></p>\n' % sleep_time) out.write(config.end_html())
out.write('</body>\n</html>\n')
out.close() out.close()
# sleep until its time to poll again # sleep until its time to poll again

View File

@ -1,3 +1,3 @@
#! /bin/sh #! /bin/sh
exec udpsvd 0 9 /usr/sbin/in.heartbeatd exec udpsvd 0 9 envuidgid ctf /usr/sbin/in.heartbeatd

View File

@ -14,6 +14,8 @@ MAX_HIST = 30
HIST_STEP = 100 HIST_STEP = 100
key = 'tanks:::2bac5e912ff2e1ad559b177eb5aeecca' key = 'tanks:::2bac5e912ff2e1ad559b177eb5aeecca'
running = True
class Flagger(asynchat.async_chat): class Flagger(asynchat.async_chat):
"""Use to connect to flagd and submit the current flag holder.""" """Use to connect to flagd and submit the current flag holder."""
@ -31,6 +33,7 @@ class Flagger(asynchat.async_chat):
def handle_error(self): def handle_error(self):
# If we lose the connection to flagd, nobody can score any # If we lose the connection to flagd, nobody can score any
# points. Terminate everything. # points. Terminate everything.
running = False
asyncore.close_all() asyncore.close_all()
asynchat.async_chat.handle_error(self) asynchat.async_chat.handle_error(self)
@ -84,6 +87,8 @@ def main():
lastrun = 0 lastrun = 0
while True: while True:
asyncore.loop(60, count=1) asyncore.loop(60, count=1)
if not running:
break
now = time.time() now = time.time()
if now - lastrun >= 60: if now - lastrun >= 60:
run_tanks(args, turns, flagger) run_tanks(args, turns, flagger)