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", "

Congratulations.

"); - - return 0; -} diff --git a/src/common.c b/src/common.c deleted file mode 100644 index 475700b..0000000 --- a/src/common.c +++ /dev/null @@ -1,549 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "common.h" - -#ifdef NODUMP -#define DUMPf(fmt, args...) -#else -#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args) -#endif -#define DUMP() DUMPf("") -#define DUMP_d(v) DUMPf("%s = %d", #v, v) -#define DUMP_x(v) DUMPf("%s = 0x%x", #v, v) -#define DUMP_s(v) DUMPf("%s = %s", #v, v) -#define DUMP_c(v) DUMPf("%s = '%c' (0x%02x)", #v, v, v) -#define DUMP_p(v) DUMPf("%s = %p", #v, v) - - -#define POST_MAX 1024 - -/* - * CGI - */ -static int is_cgi = 0; -static char **argv = NULL; - -static int -read_char_argv() -{ - static int arg = 0; - static char *p; - - if (NULL == argv) { - return EOF; - } - - if (0 == arg) { - arg = 1; - p = argv[1]; - } - - if (!p) { - return EOF; - } else if (!*p) { - arg += 1; - p = argv[arg]; - return '&'; - } - - return *(p++); -} - -static int -read_char_stdin() -{ - static int inlen = -1; - - if (-1 == inlen) { - char *p = getenv("CONTENT_LENGTH"); - if (p) { - inlen = atoi(p); - if (inlen > POST_MAX) { - inlen = POST_MAX; - } - if (inlen < 0) { - inlen = 0; - } - } else { - inlen = 0; - } - } - - if (inlen) { - inlen -= 1; - return getchar(); - } - return EOF; -} - -static int -read_char_query_string() -{ - static char *p = (char *) -1; - - if ((char *) -1 == p) { - p = getenv("QUERY_STRING"); - } - - if (!p) { - return EOF; - } else if (!*p) { - return EOF; - } else { - return *(p++); - } -} - -static int (*read_char) () = read_char_argv; - -int -cgi_init(char *global_argv[]) -{ - char *rm = getenv("REQUEST_METHOD"); - - if (!rm) { - read_char = read_char_argv; - argv = global_argv; - } else if (0 == strcmp(rm, "POST")) { - read_char = read_char_stdin; - is_cgi = 1; - } else if (0 == strcmp(rm, "GET")) { - read_char = read_char_query_string; - is_cgi = 1; - } else { - printf(("405 Method not allowed\r\n" - "Allow: GET, POST\r\n" "Content-type: text/plain\r\n" "\r\n" "%s is not allowed.\n"), rm); - return -1; - } - - return 0; -} - -static char -tonum(int c) -{ - if ((c >= '0') && (c <= '9')) { - return c - '0'; - } - if ((c >= 'a') && (c <= 'f')) { - return 10 + c - 'a'; - } - if ((c >= 'A') && (c <= 'F')) { - return 10 + c - 'A'; - } - return 0; -} - -static char -read_hex() -{ - int a = read_char(); - int b = read_char(); - - return tonum(a) * 16 + tonum(b); -} - -/* - * Read a key or a value. Since & and = aren't supposed to appear outside of boundaries, we can use the same function for both. - */ -size_t -cgi_item(char *str, size_t maxlen) -{ - int c; - size_t pos = 0; - - while (1) { - c = read_char(); - switch (c) { - case EOF: - case '=': - case '&': - str[pos] = '\0'; - return pos; - case '%': - c = read_hex(); - break; - case '+': - c = ' '; - break; - } - if (pos < maxlen - 1) { - str[pos] = c; - pos += 1; - } - } -} - -void -cgi_head(char *title) -{ - if (is_cgi) { - printf("Content-type: text/html\r\n\r\n"); - } - printf(("\n" - "\n" - " \n" - " %s\n" - " \n" - " \n" - "

%s

\n"), title, title); -} - -void -cgi_foot() -{ - printf("\n
\n"); - printf("\n"); - printf("
\n"); - printf("\"Los\n"); - printf("\"US\n"); - printf("\"Sandia\n"); - printf("
\n"); - printf("\n"); - -} - -void -cgi_result(int code, char *desc, char *fmt, ...) -{ - va_list ap; - - if (is_cgi) { - printf("Status: %d %s\r\n", code, desc); - } - cgi_head(desc); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - cgi_foot(); - exit(0); -} - -void -cgi_fail(int err) -{ - switch (err) { - case ERR_GENERAL: - cgi_result(500, "Points not awarded", "

The server is unable to award your points at this time.

"); - case ERR_NOTEAM: - cgi_result(409, "No such team", "

There is no team with that hash.

"); - case ERR_CLAIMED: - cgi_result(409, "Already claimed", - "

That is the correct answer, but your team has already claimed these points.

"); - default: - cgi_result(409, "Failure", "

Failure code: %d

", err); - } -} - -void -cgi_page(char *title, char *fmt, ...) -{ - va_list ap; - - cgi_head(title); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - cgi_foot(); - exit(0); -} - -void -cgi_error(char *text) -{ - cgi_result(500, "Internal error", "

%s

", text); -} - - -/* - * Common routines - */ - -/* - * cut -d$ANCHOR -f2- | grep -Fx "$NEEDLE" - */ -int -anchored_search(char const *filename, char const *needle, char const anchor) -{ - FILE *f = fopen(filename, "r"); - size_t nlen = strlen(needle); - char line[1024]; - int ret = 0; - - while (f) { - char *p; - - if (NULL == fgets(line, sizeof line, f)) { - break; - } - - /* - * Find anchor - */ - if (anchor) { - p = strchr(line, anchor); - if (!p) { - continue; - } - p += 1; - } else { - p = line; - } - - /* - * Don't bother with strcmp if string lengths differ. If this string is shorter than the previous, it's okay. This - * is just a performance hack. - */ - if ((p[nlen] != '\n') && (p[nlen] != '\0')) { - continue; - } - p[nlen] = 0; - - /* - * Okay, now we can compare! - */ - if (0 == strcmp(p, needle)) { - ret = 1; - break; - } - } - - if (f) { - fclose(f); - } - - return ret; -} - -void -urandom(char *buf, size_t buflen) -{ - static int fd = -2; - - if (-2 == fd) { - srandom(time(NULL) * getpid()); - fd = open("/dev/urandom", O_RDONLY); - } - if (-1 != fd) { - int len; - - len = read(fd, buf, buflen); - if (len == buflen) { - return; - } - } - - /* - * Fall back to libc's crappy thing - */ - { - int i; - - for (i = 0; i < buflen; i += 1) { - buf[i] = (char) random(); - } - } -} - -int -my_snprintf(char *buf, size_t buflen, char *fmt, ...) -{ - int len; - va_list ap; - - va_start(ap, fmt); - len = vsnprintf(buf, buflen - 1, fmt, ap); - va_end(ap); - buf[buflen - 1] = '\0'; - if (len >= buflen) { - return buflen - 1; - } else { - return len; - } -} - -void -ctf_chdir() -{ - static int initialized = 0; - int i; - - if (initialized) { - return; - } - initialized = 1; - - /* - * chdir to $CTF_BASE - */ - { - char const *ctf_base = getenv("CTF_BASE"); - - if (ctf_base) { - chdir(ctf_base); - } - } - - /* - * Keep going up one directory until there's an assigned.txt file - */ - for (i = 0; i < 5; i += 1) { - struct stat st; - - if ((0 == stat("assigned.txt", &st)) && S_ISREG(st.st_mode)) { - return; - } - chdir(".."); - } - fprintf(stderr, "Can not determine CTF_BASE directory: exiting.\n"); - exit(66); -} - - -static char * -mkpath(char const *type, char const *fmt, va_list ap) -{ - char relpath[PATH_MAX]; - static char path[PATH_MAX]; - - ctf_chdir(); - vsnprintf(relpath, sizeof(relpath) - 1, fmt, ap); - relpath[sizeof(relpath) - 1] = '\0'; - - /* - * $CTF_BASE/type/relpath - */ - my_snprintf(path, sizeof(path), "%s/%s", type, relpath); - return path; -} - -char * -state_path(char const *fmt, ...) -{ - va_list ap; - char *ret; - - va_start(ap, fmt); - ret = mkpath("state", fmt, ap); - va_end(ap); - return ret; -} - -char * -package_path(char const *fmt, ...) -{ - va_list ap; - char *ret; - - va_start(ap, fmt); - ret = mkpath("packages", fmt, ap); - va_end(ap); - return ret; -} - - -int -team_exists(char const *teamhash) -{ - struct stat buf; - int ret; - int i; - - if ((!teamhash) || (!*teamhash)) { - return 0; - } - - /* - * Check for invalid characters. - */ - for (i = 0; teamhash[i]; i += 1) { - if (!isalnum(teamhash[i])) { - return 0; - } - } - - /* - * stat seems to be the preferred way to check for existence. - */ - ret = stat(state_path("teams/%s", teamhash), &buf); - if (-1 == ret) { - return 0; - } - - return 1; -} - -/* - * Return values: -1: general error -2: no such team -3: points already awarded - */ -int -award_points(char const *teamhash, char const *category, const long points, char const *uid) -{ - char line[100]; - int linelen; - char *filename; - FILE *f; - time_t now = time(NULL); - - if (!team_exists(teamhash)) { - return ERR_NOTEAM; - } - - linelen = snprintf(line, sizeof(line), "%s %s %ld %s", teamhash, category, points, uid); - if (sizeof(line) <= linelen) { - return ERR_GENERAL; - } - - if (anchored_search(state_path("points.log"), line, ' ')) { - return ERR_CLAIMED; - } - - /* - * At one time I had this writing to a single log file, using lockf. This works, as long as nobody ever tries to edit the - * log file. Editing the log file would require locking it, which would block everything trying to score, effectively - * taking down the entire contest. If you can't lock it first--and nothing in busybox lets you do this--you have to bring - * down pretty much everything manually anyway. - * - * By putting new scores into new files and periodically appending those files to the main log file, it is possible to stop - * the thing that appends, edit the file at leisure, and then start the appender back up, all without affecting things - * trying to score: they're still able to record their score and move on. - */ - - filename = state_path("points.tmp/%lu.%d.%s.%s.%ld", (unsigned long) now, getpid(), teamhash, category, points); - f = fopen(filename, "w"); - if (!f) { - return ERR_GENERAL; - } - - if (EOF == fprintf(f, "%lu %s\n", (unsigned long) now, line)) { - return ERR_GENERAL; - } - - fclose(f); - - /* - * Rename into points.new - */ - { - char ofn[PATH_MAX]; - - strncpy(ofn, filename, sizeof(ofn)); - filename = state_path("points.new/%lu.%d.%s.%s.%ld", (unsigned long) now, getpid(), teamhash, category, points); - rename(ofn, filename); - } - - return 0; -} diff --git a/src/common.h b/src/common.h deleted file mode 100644 index fd48a86..0000000 --- a/src/common.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef __COMMON_H__ -#define __COMMON_H__ - -#include -#include - -#define TEAM_MAX 40 -#define CAT_MAX 40 -#define TOKEN_MAX 80 -#define itokenlen 5 - -#define ERR_GENERAL -1 -#define ERR_NOTEAM -2 -#define ERR_CLAIMED -3 - -int cgi_init(char *global_argv[]); -size_t cgi_item(char *str, size_t maxlen); -void cgi_head(char *title); -void cgi_foot(); -void cgi_result(int code, char *desc, char *fmt, ...); -void cgi_fail(int err); -void cgi_page(char *title, char *fmt, ...); -void cgi_error(char *text); - - -void ctf_chdir(); -int anchored_search(char const *filename, char const *needle, const char anchor); -void urandom(char *buf, size_t buflen); -int my_snprintf(char *buf, size_t buflen, char *fmt, ...); -char *state_path(char const *fmt, ...); -char *package_path(char const *fmt, ...); -int team_exists(char const *teamhash); -int award_points(char const *teamhash, - char const *category, - long point, - char const *uid); - -#endif diff --git a/src/pointscli.c b/src/pointscli.c deleted file mode 100644 index 232b606..0000000 --- a/src/pointscli.c +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include -#include -#include "common.h" - -int -main(int argc, char *argv[]) -{ - int points; - int ret; - char comment[512]; - - if (argc != 5) { - fprintf(stderr, "Usage: pointscli TEAM CATEGORY POINTS 'COMMENT'\n"); - return EX_USAGE; - } - ctf_chdir(); - - points = atoi(argv[3]); - if (0 == points) { - fprintf(stderr, "Error: award 0 points?\n"); - return EX_USAGE; - } - - snprintf(comment, sizeof comment, "--%s", argv[4]); - - ret = award_points(argv[1], argv[2], points, comment); - switch (ret) { - case 0: - return 0; - case ERR_GENERAL: - perror("General error"); - return EX_UNAVAILABLE; - case ERR_NOTEAM: - fprintf(stderr, "No such team\n"); - return EX_NOUSER; - case ERR_CLAIMED: - fprintf(stderr, "Duplicate entry\n"); - return EX_DATAERR; - default: - fprintf(stderr, "Error %d\n", ret); - return EX_SOFTWARE; - } - - return 0; -} diff --git a/src/puzzler.cgi.c b/src/puzzler.cgi.c deleted file mode 100644 index b1a8163..0000000 --- a/src/puzzler.cgi.c +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include "common.h" - -int -main(int argc, char *argv[]) -{ - char team[TEAM_MAX] = {0}; - char category[CAT_MAX] = {0}; - char points_str[11] = {0}; - char answer[500] = {0}; - long points = 0; - - if (-1 == cgi_init(argv)) { - return 0; - } - ctf_chdir(); - - /* Read in team and answer */ - 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 'c': - cgi_item(category, sizeof(category)); - break; - case 'p': - cgi_item(points_str, sizeof(points_str)); - points = atol(points_str); - break; - case 'a': - cgi_item(answer, sizeof(answer)); - break; - } - } - - /* Validate category name (prevent directory traversal) */ - { - char *p; - - for (p = category; *p; p += 1) { - if ((! isalnum(*p)) && ('-' != *p)) { - cgi_page("Invalid category", ""); - } - } - } - - /* Check answer (also assures category exists) */ - { - char needle[400]; - - my_snprintf(needle, sizeof(needle), "%ld %s", points, answer); - - if (! anchored_search(package_path("%s/answers.txt", category), needle, 0)) { - cgi_page("Wrong answer", ""); - } - } - - { - int ret = award_points(team, category, points, "P"); - if (ret < 0) { - cgi_fail(ret); - } - } - - cgi_page("Points awarded", - ("

%d points for %s.

\n" - "

Back to puzzles

\n" - ""), - points, team, points); - - return 0; -} diff --git a/src/puzzles.cgi.c b/src/puzzles.cgi.c deleted file mode 100644 index cda601d..0000000 --- a/src/puzzles.cgi.c +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "common.h" - - -int -longcmp(long *a, long *b) -{ - if (*a < *b) return -1; - if (*a > *b) return 1; - return 0; -} - -#define PUZZLES_MAX 100 - -/** Keeps track of the most points yet awarded in each category */ -int ncats = 0; -struct { - char cat[CAT_MAX]; - long points; -} points_by_cat[PUZZLES_MAX]; - - -size_t -read_until_char(FILE *f, char *buf, size_t buflen, char delim) -{ - size_t i = 0; - - while (1) { - int c = fgetc(f); - - if ((EOF == c) || delim == c) { - if (buf) { - buf[i] = '\0'; - } - return i; - } - - if (i + 1 < buflen) { - buf[i] = c; - } - i += 1; - } -} - -int -main(int argc, char *argv[]) -{ - int i; - DIR *opt; - - if (-1 == cgi_init(argv)) { - return 0; - } - ctf_chdir(); - - { - FILE *f = fopen(state_path("points.log"), "r"); - char cat[CAT_MAX]; - char points_str[11]; - long points; - int i; - - while (f && (! feof(f))) { - read_until_char(f, NULL, 0, ' '); - read_until_char(f, NULL, 0, ' '); - read_until_char(f, cat, sizeof(cat), ' '); - read_until_char(f, points_str, sizeof(points_str), ' '); - read_until_char(f, NULL, 0, '\n'); - points = atol(points_str); - - for (i = 0; i < ncats; i += 1) { - if (0 == strcmp(cat, points_by_cat[i].cat)) break; - } - if (i == ncats) { - if (PUZZLES_MAX == ncats) { - continue; - } - strncpy(points_by_cat[i].cat, cat, sizeof(points_by_cat[i].cat)); - points_by_cat[i].points = 0; - ncats += 1; - } - if (points > points_by_cat[i].points) { - points_by_cat[i].points = points; - } - } - - if (f) fclose(f); - } - - opt = opendir(package_path("")); - if (NULL == opt) { - cgi_error("Cannot open packages directory"); - } - - cgi_head("Open puzzles"); - printf("
\n"); - - /* For each file in /packages/ ... */ - while (1) { - struct dirent *e = readdir(opt); - char *cat; - long maxpoints = 0; - FILE *map; - - if (! e) break; - - cat = e->d_name; - if ('.' == cat[0]) continue; - - /* Find the most points scored in this category */ - for (i = 0; i < ncats; i += 1) { - if (0 == strcmp(cat, points_by_cat[i].cat)) { - maxpoints = points_by_cat[i].points; - break; - } - } - - /* Read in category's map file, print html until point limit reached */ - map = fopen(package_path("%s/map.txt", cat), "r"); - if (map == NULL) continue; - - printf("
%s
\n", cat); - printf("
\n"); - - while (i < PUZZLES_MAX && (!feof(map))) { - char hash[20]; - char points_str[20]; - long points; - - read_until_char(map, points_str, sizeof(points_str), ' '); - read_until_char(map, hash, sizeof(hash), '\n'); - points = atol(points_str); - - if (0 == points) { - printf(" \n"); - break; - } - - printf(" %ld\n", cat, hash, points); - - if (points > maxpoints) break; - } - - printf("
\n"); - fclose(map); - } - - closedir(opt); - - printf("
\n"); - - printf("

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!

"); - cgi_foot(); - - return 0; -} diff --git a/src/tea.c b/src/tea.c deleted file mode 100644 index 9aa09b5..0000000 --- a/src/tea.c +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include -#include -#include -#include -#include - -static const char magic[4] = "tea."; - -#define min(a,b) (((a)<(b))?(a):(b)) - -void -tea_encrypt(uint32_t *v, uint32_t *k) { - uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */ - uint32_t delta=0x9e3779b9; /* a key schedule constant */ - uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */ - for (i=0; i < 32; i++) { /* basic cycle start */ - sum += delta; - v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1); - v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3); - } /* end cycle */ - v[0]=v0; v[1]=v1; -} - -/* - * Use TEA in CTR mode to create a stream cipher. - */ -static void -tea_apply(FILE *out, FILE *in, uint32_t *k, uint32_t ivec) -{ - uint32_t count = 0; - uint32_t v[2]; - size_t idx = sizeof v; - char *p = (char *)v; - - while (1) { - int c = fgetc(in); - - if (EOF == c) { - break; - } - - if (sizeof v == idx) { - v[0] = ivec; - v[1] = count++; - tea_encrypt(v, k); - idx = 0; - } - - fputc(c ^ p[idx++], out); - } -} - -int -tea_decrypt_stream(FILE *out, FILE *in, uint32_t *k) -{ - uint32_t ivec; - - { - char m[4] = {0}; - - fread(m, sizeof m, 1, in); - if (memcmp(m, magic, 4)) { - return -1; - } - } - - fread(&ivec, sizeof ivec, 1, in); - tea_apply(out, in, k, ivec); - - return 0; -} - -int -tea_encrypt_stream(FILE *out, FILE *in, uint32_t *k) -{ - uint32_t ivec; - - fwrite(magic, sizeof magic, 1, out); - - { - FILE *r = fopen("/dev/urandom", "r"); - - if (! r) { - return -1; - } - fread(&ivec, sizeof ivec, 1, r); - fclose(r); - } - fwrite(&ivec, sizeof ivec, 1, out); - - tea_apply(out, in, k, ivec); - - return 0; -} - -int -usage(const char *prog) -{ - fprintf(stderr, "Usage: %s [-e]