Flesh out remaining handlers

I think at this point we just need to drop in text for puzzle.html, puzzle-list.html, and scoreboard.html,
and we'll have a working server.
This commit is contained in:
Neale Pickett 2018-09-17 23:40:05 +00:00
parent 3b3783f9ca
commit 8b1441a591
3 changed files with 76 additions and 68 deletions

View File

@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
)
@ -46,25 +45,16 @@ func ParseAward(s string) (*Award, error) {
s = strings.Trim(s, " \t\n")
parts := strings.SplitN(s, " ", 5)
if len(parts) < 4 {
return nil, fmt.Errorf("Malformed award string")
var whenEpoch int64
n, err := fmt.Sscanf(s, "%d %s %s %d", &whenEpoch, &ret.TeamId, &ret.Category, &ret.Points)
if err != nil {
return nil, err
} else if n != 4 {
return nil, fmt.Errorf("Malformed award string: only parsed %d fields", n)
}
whenEpoch, err := strconv.ParseInt(parts[0], 10, 64)
if err != nil {
return nil, fmt.Errorf("Malformed timestamp: %s", parts[0])
}
ret.When = time.Unix(whenEpoch, 0)
ret.TeamId = parts[1]
ret.Category = parts[2]
points, err := strconv.Atoi(parts[3])
if err != nil {
return nil, fmt.Errorf("Malformed Points: %s: %v", parts[3], err)
}
ret.Points = points
return &ret, nil
}

View File

@ -7,18 +7,18 @@ import (
"log"
"net/http"
"os"
"regexp"
"strconv"
"strings"
)
func respond(w http.ResponseWriter, req *http.Request, status Status, short string, description string) {
func respond(w http.ResponseWriter, req *http.Request, status Status, short string, format string, a ...interface{}) {
long := fmt.Sprintf(format, a...)
// This is a kludge. Do proper parsing when this causes problems.
accept := req.Header.Get("Accept")
if strings.Contains(accept, "application/json") {
ShowJSend(w, status, short, description)
ShowJSend(w, status, short, long)
} else {
ShowHtml(w, status, short, description)
ShowHtml(w, status, short, long)
}
}
@ -71,40 +71,50 @@ func (ctx Instance) registerHandler(w http.ResponseWriter, req *http.Request) {
}
func (ctx Instance) tokenHandler(w http.ResponseWriter, req *http.Request) {
teamid := req.FormValue("t")
token := req.FormValue("k")
teamid := req.FormValue("id")
token := req.FormValue("token")
// Check answer
if !anchoredSearchFile(ctx.StatePath("tokens.txt"), token, 0) {
var category string
var points int
var fluff string
stoken := strings.Replace(token, ":", " ", 2)
n, err := fmt.Sscanf(stoken, "%s %d %s", &category, &points, &fluff)
if err != nil || n != 3 {
respond(
w, req, Fail,
"Unrecognized token",
"I don't recognize that token. Did you type in the whole thing?",
"Malformed token",
"That doesn't look like a token: %v.", err,
)
return
}
parts := strings.Split(token, ":")
category := ""
pointstr := ""
if len(parts) >= 2 {
category = parts[0]
pointstr = parts[1]
}
points, err := strconv.Atoi(pointstr)
if err != nil {
points = 0
}
// Defang category name; prevent directory traversal
if matched, _ := regexp.MatchString("^[A-Za-z0-9_-]", category); matched {
category = ""
if (category == "") || (points <= 0) {
respond(
w, req, Fail,
"Weird token",
"That token doesn't make any sense.",
)
return
}
if (category == "") || (points == 0) {
f, err := ctx.OpenCategoryFile(category, "tokens.txt")
if err != nil {
respond(
w, req, Fail,
"Cannot list valid tokens",
err.Error(),
)
return
}
defer f.Close()
// Make sure the token is in the list
if !anchoredSearch(f, token, 0) {
respond(
w, req, Fail,
"Unrecognized token",
"Something doesn't look right about that token",
"I don't recognize that token. Did you type in the whole thing?",
)
return
}
@ -120,38 +130,32 @@ func (ctx Instance) tokenHandler(w http.ResponseWriter, req *http.Request) {
respond(
w, req, Success,
"Points awarded",
fmt.Sprintf("%d points for %s!", points, teamid),
"%d points for %s!", points, teamid,
)
}
func (ctx Instance) answerHandler(w http.ResponseWriter, req *http.Request) {
teamid := req.FormValue("t")
category := req.FormValue("c")
pointstr := req.FormValue("p")
answer := req.FormValue("a")
teamid := req.FormValue("id")
category := req.FormValue("cat")
pointstr := req.FormValue("points")
answer := req.FormValue("answer")
points, err := strconv.Atoi(pointstr)
if err != nil {
points = 0
}
catmb, ok := ctx.Categories[category]
if !ok {
respond(
w, req, Fail,
"Category does not exist",
"The requested category does not exist. Sorry!",
"Cannot parse point value",
"This doesn't look like an integer: %s", pointstr,
)
return
}
// Get the answers
haystack, err := catmb.Open("answers.txt")
haystack, err := ctx.OpenCategoryFile(category, "answers.txt")
if err != nil {
respond(
w, req, Error,
"Answers do not exist",
"Please tell the contest people that the mothball for this category has no answers.txt in it!",
w, req, Fail,
"Cannot list answers",
"Unable to read the list of answers for this category.",
)
return
}
@ -163,7 +167,7 @@ func (ctx Instance) answerHandler(w http.ResponseWriter, req *http.Request) {
respond(
w, req, Fail,
"Wrong answer",
err.Error(),
"That is not the correct answer for %s %d.", category, points,
)
return
}
@ -204,16 +208,18 @@ func (ctx Instance) puzzlesHandler(w http.ResponseWriter, req *http.Request) {
scanner := bufio.NewScanner(mf)
for scanner.Scan() {
line := scanner.Text()
parts := strings.Split(line, " ")
if len(parts) != 2 {
continue
}
pointval, err := strconv.Atoi(parts[0])
var pointval int
var dir string
n, err := fmt.Sscanf(line, "%d %s", &pointval, &dir)
if err != nil {
log.Print(err)
log.Printf("Parsing map for %s: %v", catName, err)
continue
} else if n != 2 {
log.Printf("Parsing map for %s: short read", catName)
continue
}
dir := parts[1]
pm = append(pm, PuzzleMap{pointval, dir})
log.Print(pm)

View File

@ -3,6 +3,7 @@ package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
@ -132,3 +133,14 @@ func (ctx *Instance) AwardPoints(teamid string, category string, points int) err
log.Printf("Award %s %s %d", teamid, category, points)
return nil
}
func (ctx *Instance) OpenCategoryFile(category string, parts ...string) (io.ReadCloser, error) {
mb, ok := ctx.Categories[category]
if !ok {
return nil, fmt.Errorf("No such category: %s", category)
}
filename := path.Join(parts...)
f, err := mb.Open(filename)
return f, err
}