mirror of https://github.com/dirtbags/moth.git
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:
parent
5b018eab42
commit
78f2e2a79c
24
src/award.go
24
src/award.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
108
src/handlers.go
108
src/handlers.go
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue