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 ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strconv"
"strings" "strings"
"time" "time"
) )
@ -46,25 +45,16 @@ func ParseAward(s string) (*Award, error) {
s = strings.Trim(s, " \t\n") s = strings.Trim(s, " \t\n")
parts := strings.SplitN(s, " ", 5) var whenEpoch int64
if len(parts) < 4 {
return nil, fmt.Errorf("Malformed award string") 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.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 return &ret, nil
} }

View File

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

View File

@ -3,6 +3,7 @@ package main
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "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) log.Printf("Award %s %s %d", teamid, category, points)
return nil 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
}