diff --git a/TODO b/TODO
index e69de29..4228e72 100644
--- a/TODO
+++ b/TODO
@@ -0,0 +1,9 @@
+* Scoreboard refresh
+
+Test:
+
+* awarding points
+* points already awarded
+* bad team hash
+* category doesn't exist
+* puzzle doesn't exist
diff --git a/bin/award b/bin/award
new file mode 100755
index 0000000..76ef55e
--- /dev/null
+++ b/bin/award
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+if [ $# -lt 3 ] || [ $# -gt 4 ]; then
+ echo "Usage: $0 TEAM CATEGORY POINTS [COMMENT]"
+ exit 1
+fi
+
+cd $(dirname $0)/../state
+
+if grep -q "^[0-9]* $1 $2 $3 $4" points.log; then
+ echo "Points already awarded"
+ exit 1
+fi
+
+now=$(date +%s)
+echo "$now $1 $2 $3 cli:$4" > points.new/$now.$$
diff --git a/bin/once b/bin/once
index 2f199b0..23b06c9 100755
--- a/bin/once
+++ b/bin/once
@@ -16,14 +16,14 @@ fi
# Collect new points
find state/points.new -type f | while read fn; do
# Skip partially-written files
- [ $(wc -l $fn) -eq 1 ] || continue
+ [ $(wc -l < $fn) -eq 1 ] || continue
cat $fn >> state/points.log
rm $fn
done
# Generate new puzzles.html
-if www/puzzles.cgi > www/puzzles.new; then
+if bin/puzzles > www/puzzles.new; then
mv www/puzzles.new www/puzzles.html
fi
diff --git a/bin/puzzles b/bin/puzzles
new file mode 100755
index 0000000..abd2610
--- /dev/null
+++ b/bin/puzzles
@@ -0,0 +1,47 @@
+#! /usr/bin/lua
+
+package.path = "cgi/?.lua"
+local koth = require "koth"
+
+local max_by_cat = {}
+
+local f = io.popen("ls packages")
+for cat in f:lines() do
+ max_by_cat[cat] = 0
+end
+f:close()
+
+
+for line in io.lines("state/points.log") do
+ local ts, team, cat, points, comment = line:match("^(%d+) (%w+) (%w+) (%d+) ?(.*)")
+ points = tonumber(points) or 0
+
+ -- Skip scores for removed categories
+ if (max_by_cat[cat] ~= nil) then
+ max_by_cat[cat] = math.max(max_by_cat[cat], points)
+ end
+end
+
+local body = "
\n"
+for cat, biggest in pairs(max_by_cat) do
+ local points, dirname
+
+ body = body .. "
" .. cat .. "
"
+ body = body .. "
"
+ for line in io.lines("packages/" .. cat .. "/map.txt") do
+ points, dirname = line:match("^(%d+) (.*)")
+ points = tonumber(points)
+
+ body = body .. "" .. points .. " "
+ if (points > biggest) then
+ break
+ end
+ end
+ if (points == biggest) then
+ body = body .. "⁂"
+ end
+ body = body .. "
\n"
+end
+body = body .. "
\n"
+
+koth.page("Open Puzzles", body)
\ No newline at end of file
diff --git a/cgi/koth.lua b/cgi/koth.lua
index 529b610..ded0124 100644
--- a/cgi/koth.lua
+++ b/cgi/koth.lua
@@ -45,7 +45,7 @@ end
-- We're going to rely on `bin/once` only processing files with the right number of lines.
--
function koth.award_points(team, category, points, comment)
- local filename = team .. "." .. category .. "." points
+ local filename = team .. "." .. category .. "." .. points
local entry = team .. " " .. category .. " " .. points
if (comment) then
diff --git a/install b/install
index 7456426..cd8e600 100755
--- a/install
+++ b/install
@@ -28,15 +28,6 @@ copy () {
fi
}
-cc () {
- target=$DESTDIR/bin/$(basename $1 .c)
- if older $target $@; then
- src=$1; shift
- echo "CC $src"
- gcc -Wall -Werror -o $target $@ $src
- fi
-}
-
web () {
target=$DESTDIR/www/${1#*/}
if older $target $1; then
@@ -46,16 +37,6 @@ web () {
fi
}
-cgi () {
- target=$DESTDIR/www/$(basename $1 .c)
- if older $target src/common.c $@; then
- mkdir -p $(dirname $target)
- src=$1; shift
- echo "CGI $src"
- gcc -Wall -Werror -o $target $@ src/common.c $src
- fi
-}
-
setup() {
[ -d $DESTDIR/state ] && return
echo "SETUP"
@@ -96,18 +77,8 @@ git ls-files | while read fn; do
bin/*)
copy $fn
;;
- src/common.c)
- ;;
- src/*.h)
- ;;
- src/pointscli.c)
- cc $fn src/common.c
- ;;
- src/*.cgi.c)
- cgi $fn
- ;;
- src/*.c)
- cc $fn
+ cgi/*)
+ copy $fn
;;
*)
echo "??? $fn"
diff --git a/src/bubblebabble.c b/src/bubblebabble.c
deleted file mode 100644
index 7477fcd..0000000
--- a/src/bubblebabble.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#include
-#include
-
-/** Compute bubble babble for input buffer.
- *
- * The generated output will be of length 6*((inlen/2)+1), including the
- * trailing NULL.
- *
- * Test vectors:
- * `' (empty string) `xexax'
- * `1234567890' `xesef-disof-gytuf-katof-movif-baxux'
- * `Pineapple' `xigak-nyryk-humil-bosek-sonax'
- */
-static char const consonants[] = "bcdfghklmnprstvz";
-static char const vowels[] = "aeiouy";
-
-int
-main(int argc, char *argv[])
-{
- int seed = 1;
-
- putchar('x');
- while (1) {
- int c;
-
- c = getchar();
- if (EOF == c) {
- putchar(vowels[seed % 6]);
- putchar('x');
- putchar(vowels[seed / 6]);
- break;
- }
-
- putchar(vowels[(((c >> 6) & 3) + seed) % 6]);
- putchar(consonants[(c >> 2) & 15]);
- putchar(vowels[((c & 3) + (seed / 6)) % 6]);
-
- seed = (seed * 5) + (c * 7);
- c = getchar();
- seed = (seed + c) % 36;
-
- if (EOF == c) {
- break;
- }
- putchar(consonants[(c >> 4) & 15]);
- putchar('-');
- putchar(consonants[c & 15]);
- }
-
- putchar('x');
- putchar('\n');
-
- return 0;
-}
diff --git a/src/claim.cgi.c b/src/claim.cgi.c
deleted file mode 100644
index 14ef6cb..0000000
--- a/src/claim.cgi.c
+++ /dev/null
@@ -1,90 +0,0 @@
-#include
-#include
-#include "common.h"
-
-int
-main(int argc, char *argv[])
-{
- char team[TEAM_MAX] = {0};
- char token[TOKEN_MAX] = {0};
-
- if (-1 == cgi_init(argv)) {
- return 0;
- }
- ctf_chdir();
-
- /* Read in team and token */
- while (1) {
- size_t len;
- char key[20];
-
- len = cgi_item(key, sizeof(key));
- if (0 == len) break;
- switch (key[0]) {
- case 't':
- cgi_item(team, sizeof(team));
- break;
- case 'k':
- cgi_item(token, sizeof(token));
- break;
- }
- }
-
-
- /* Any weird characters in token name? */
- {
- char *p;
-
- if ('\0' == token[0]) {
- cgi_result(409, "Must supply token", "
Your request did not contain a k= parameter.
");
- }
- for (p = token; *p; p += 1) {
- if ((! isalnum(*p)) &&
- (*p != '-') &&
- (*p != ':')) {
- cgi_result(409, "Not a token", "
This token has untokenlike characteristics.
");
- }
- }
- }
-
- /* Award points */
- {
- char *p = token;
- char *q;
- char category[40];
- char points_s[40];
- int points;
- int ret;
-
- /* Pull category name out of the token */
- for (q = category; *p && (*p != ':'); p += 1) {
- *p = tolower(*p);
- *(q++) = *p;
- }
- *q = '\0';
- if (p) p += 1;
-
- /* Now we can check to see if it's valid */
- if ((! anchored_search(package_path("%s/tokens.txt", category), token, 0)) &&
- (! anchored_search(state_path("tokens.db"), token, 0))) {
- cgi_result(409, "No such token", "
This token has not been issued.
");
- }
-
- /* Pull point value out of the token (if it has one) */
- for (q = points_s; *p && (*p != ':'); p += 1) {
- *(q++) = *p;
- }
- *q = '\0';
- points = atoi(points_s);
- if (0 == points) points = 1;
-
- ret = award_points(team, category, points, token);
- if (ret < 0) {
- cgi_fail(ret);
- }
- }
-
- cgi_page("Point awarded", "
New puzzles are unlocked when any team answers the highest-scoring puzzle in a category.
");
- printf("
Are you using Windows? You have our pity. But you might enjoy a program called wget, which will allow you to download every open puzzle with a single wget -r!