package main import ( "bufio" "github.com/namsral/flag" "fmt" "log" "net/http" "os" "path" "strings" "time" ) var moduleDir string var stateDir string var cacheDir string var categories = []string{} // anchoredSearch looks for needle in filename, // skipping the first skip space-delimited words func anchoredSearch(filename string, needle string, skip int) bool { f, err := os.Open(filename) if err != nil { log.Print("Can't open %s: %s", filename, err) return false } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() parts := strings.SplitN(" ", line, skip+1) if parts[skip+1] == needle { return true } } return false } func showPage(w http.ResponseWriter, title string, body string) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "") fmt.Fprintf(w, "") fmt.Fprintf(w, "%s", title) fmt.Fprintf(w, "") fmt.Fprintf(w, "") fmt.Fprintf(w, "") fmt.Fprintf(w, "") fmt.Fprintf(w, "

%s

", title) fmt.Fprintf(w, "
%s
", body) fmt.Fprintf(w, "") fmt.Fprintf(w, "") } func modulesPath(parts ...string) string { tail := path.Join(parts...) return path.Join(moduleDir, tail) } func statePath(parts ...string) string { tail := path.Join(parts...) return path.Join(stateDir, tail) } func cachePath(parts ...string) string { tail := path.Join(parts...) return path.Join(cacheDir, tail) } func logRequest(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("HTTP %s %s %s\n", r.RemoteAddr, r.Method, r.URL) handler.ServeHTTP(w, r) }) } func setup() error { // Roll over and die if directories aren't even set up if _, err := os.Stat(modulesPath()); os.IsNotExist(err) { return err } if _, err := os.Stat(statePath()); os.IsNotExist(err) { return err } if _, err := os.Stat(cachePath()); os.IsNotExist(err) { return err } // Make sure points directories exist os.Mkdir(statePath("points.tmp"), 0755) os.Mkdir(statePath("points.new"), 0755) // Preseed available team ids if file doesn't exist if f, err := os.OpenFile(statePath("teamids.txt"), os.O_WRONLY | os.O_CREATE | os.O_EXCL, 0644); err == nil { defer f.Close() for i := 0; i <= 9999; i += 1 { fmt.Fprintf(f, "%04d\n", i) } } return nil } func main() { var maintenanceInterval time.Duration var listen string fs := flag.NewFlagSetWithEnvPrefix(os.Args[0], "MOTH", flag.ExitOnError) fs.StringVar( &moduleDir, "modules", "/moth/modules", "Path where your moth modules live", ) fs.StringVar( &stateDir, "state", "/moth/state", "Path where state should be written", ) fs.StringVar( &cacheDir, "cache", "/moth/cache", "Path for ephemeral cache", ) fs.DurationVar( &maintenanceInterval, "maint", 20 * time.Second, "Maintenance interval", ) fs.StringVar( &listen, "listen", ":8080", "[host]:port to bind and listen", ) fs.Parse(os.Args[1:]) if err := setup(); err != nil { log.Fatal(err) } go maintenance(maintenanceInterval) fileserver := http.FileServer(http.Dir(cacheDir)) http.HandleFunc("/", rootHandler) http.Handle("/static/", http.StripPrefix("/static", fileserver)) http.HandleFunc("/register", registerHandler) http.HandleFunc("/token", tokenHandler) http.HandleFunc("/answer", answerHandler) http.HandleFunc("/puzzles.json", puzzlesHandler) http.HandleFunc("/points.json", pointsHandler) log.Printf("Listening on %s", listen) log.Fatal(http.ListenAndServe(listen, logRequest(http.DefaultServeMux))) }