package api import ( "encoding/json" "errors" "fmt" "io" "log" "net/http" "strconv" "strings" "time" "srs.epita.fr/fic-server/libfic" "github.com/julienschmidt/httprouter" ) type DispatchFunction func(httprouter.Params, []byte) (interface{}, error) func apiHandler(f DispatchFunction) func(http.ResponseWriter, *http.Request, httprouter.Params) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { if addr := r.Header.Get("X-Forwarded-For"); addr != "" { r.RemoteAddr = addr } log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent()) // Read the body if r.ContentLength < 0 || r.ContentLength > 6553600 { http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}"), http.StatusRequestEntityTooLarge) return } var body []byte if r.ContentLength > 0 { tmp := make([]byte, 1024) for { n, err := r.Body.Read(tmp) for j := 0; j < n; j++ { body = append(body, tmp[j]) } if err != nil || n <= 0 { break } } } var ret interface{} var err error = nil ret, err = f(ps, body) // Format response resStatus := http.StatusOK if err != nil { ret = map[string]string{"errmsg": err.Error()} resStatus = http.StatusBadRequest log.Println(r.RemoteAddr, resStatus, err.Error()) } if ret == nil { ret = map[string]string{"errmsg": "Page not found"} resStatus = http.StatusNotFound } w.Header().Set("X-FIC-Time", fmt.Sprintf("%f", float64(time.Now().UnixNano()/1000)/1000000)) if str, found := ret.(string); found { w.Header().Set("Content-Type", "application/json") w.WriteHeader(resStatus) io.WriteString(w, str) } else if bts, found := ret.([]byte); found { w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", "attachment") w.Header().Set("Content-Transfer-Encoding", "binary") w.WriteHeader(resStatus) w.Write(bts) } else if j, err := json.Marshal(ret); err != nil { w.Header().Set("Content-Type", "application/json") http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", err), http.StatusInternalServerError) } else { w.Header().Set("Content-Type", "application/json") w.WriteHeader(resStatus) w.Write(j) } } } func teamPublicHandler(f func(*fic.Team, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if tid, err := strconv.ParseInt(string(ps.ByName("tid")), 10, 64); err != nil { return nil, err } else if tid == 0 { return f(nil, body) } else if team, err := fic.GetTeam(tid); err != nil { return nil, err } else { return f(&team, body) } } } func teamHandler(f func(fic.Team, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if tid, err := strconv.ParseInt(string(ps.ByName("tid")), 10, 64); err != nil { return nil, err } else if team, err := fic.GetTeam(tid); err != nil { return nil, err } else { return f(team, body) } } } func teamAssocHandler(f func(fic.Team, string, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { var team fic.Team teamHandler(func(tm fic.Team, _ []byte) (interface{}, error) { team = tm return nil, nil })(ps, body) return f(team, string(ps.ByName("assoc")), body) } } func themeHandler(f func(fic.Theme, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if thid, err := strconv.ParseInt(string(ps.ByName("thid")), 10, 64); err != nil { return nil, err } else if theme, err := fic.GetTheme(thid); err != nil { return nil, err } else { return f(theme, body) } } } func exerciceHandler(f func(fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if eid, err := strconv.ParseInt(string(ps.ByName("eid")), 10, 64); err != nil { return nil, err } else if exercice, err := fic.GetExercice(eid); err != nil { return nil, err } else { return f(exercice, body) } } } func themedExerciceHandler(f func(fic.Theme, fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { var theme fic.Theme var exercice fic.Exercice themeHandler(func(th fic.Theme, _ []byte) (interface{}, error) { theme = th return nil, nil })(ps, body) exerciceHandler(func(ex fic.Exercice, _ []byte) (interface{}, error) { exercice = ex return nil, nil })(ps, body) return f(theme, exercice, body) } } func hintHandler(f func(fic.EHint, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if hid, err := strconv.ParseInt(string(ps.ByName("hid")), 10, 64); err != nil { return nil, err } else if hint, err := fic.GetHint(hid); err != nil { return nil, err } else { return f(hint, body) } } } func flagKeyHandler(f func(fic.FlagKey, fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { var exercice fic.Exercice exerciceHandler(func(ex fic.Exercice, _ []byte) (interface{}, error) { exercice = ex return nil, nil })(ps, body) if kid, err := strconv.ParseInt(string(ps.ByName("kid")), 10, 64); err != nil { return nil, err } else if flags, err := exercice.GetFlagKeys(); err != nil { return nil, err } else { for _, flag := range flags { if flag.Id == int(kid) { return f(flag, exercice, body) } } return nil, errors.New("Unable to find the requested key") } } } func choiceHandler(f func(fic.FlagChoice, fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { var exercice fic.Exercice var flag fic.FlagKey flagKeyHandler(func(fl fic.FlagKey, ex fic.Exercice, _ []byte) (interface{}, error) { exercice = ex flag = fl return nil, nil })(ps, body) if cid, err := strconv.ParseInt(string(ps.ByName("cid")), 10, 32); err != nil { return nil, err } else if choice, err := flag.GetChoice(int(cid)); err != nil { return nil, err } else { return f(choice, exercice, body) } } } func quizHandler(f func(fic.MCQ, fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { var exercice fic.Exercice exerciceHandler(func(ex fic.Exercice, _ []byte) (interface{}, error) { exercice = ex return nil, nil })(ps, body) if qid, err := strconv.ParseInt(string(ps.ByName("qid")), 10, 64); err != nil { return nil, err } else if mcqs, err := exercice.GetMCQ(); err != nil { return nil, err } else { for _, mcq := range mcqs { if mcq.Id == int(qid) { return f(mcq, exercice, body) } } return nil, errors.New("Unable to find the requested key") } } } func exerciceFileHandler(f func(fic.EFile, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { var exercice fic.Exercice exerciceHandler(func(ex fic.Exercice, _ []byte) (interface{}, error) { exercice = ex return nil, nil })(ps, body) if fid, err := strconv.ParseInt(string(ps.ByName("fid")), 10, 64); err != nil { return nil, err } else if files, err := exercice.GetFiles(); err != nil { return nil, err } else { for _, file := range files { if file.Id == fid { return f(file, body) } } return nil, errors.New("Unable to find the requested file") } } } func eventHandler(f func(fic.Event, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if evid, err := strconv.ParseInt(string(ps.ByName("evid")), 10, 64); err != nil { return nil, err } else if event, err := fic.GetEvent(evid); err != nil { return nil, err } else { return f(event, body) } } } func claimHandler(f func(fic.Claim, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if cid, err := strconv.ParseInt(string(ps.ByName("cid")), 10, 64); err != nil { return nil, fmt.Errorf("Invalid claim id: %w", err) } else if claim, err := fic.GetClaim(cid); err != nil { return nil, fmt.Errorf("Unable to find requested claim (id=%d): %w", cid, err) } else { return f(claim, body) } } } func claimAssigneeHandler(f func(fic.ClaimAssignee, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if aid, err := strconv.ParseInt(string(ps.ByName("aid")), 10, 64); err != nil { return nil, err } else if assignee, err := fic.GetAssignee(aid); err != nil { return nil, err } else { return f(assignee, body) } } } func fileHandler(f func(fic.EFile, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if fileid, err := strconv.ParseInt(string(ps.ByName("fileid")), 10, 64); err != nil { return nil, err } else if file, err := fic.GetFile(fileid); err != nil { return nil, err } else { return f(file, body) } } } func fileDependancyHandler(f func(fic.EFile, int, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if depid, err := strconv.ParseInt(string(ps.ByName("depid")), 10, 64); err != nil { return nil, err } else { return fileHandler(func(file fic.EFile, b []byte) (interface{}, error) { return f(file, int(depid), b) })(ps, body) } } } func certificateHandler(f func(fic.Certificate, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { var cid uint64 var err error certid := strings.TrimSuffix(ps.ByName("certid"), ".p12") if cid, err = strconv.ParseUint(certid, 10, 64); err != nil { if cid, err = strconv.ParseUint(certid, 16, 64); err != nil { return nil, err } } if cert, err := fic.GetCertificate(cid); err != nil { return nil, err } else { return f(cert, body) } } } func notFound(ps httprouter.Params, _ []byte) (interface{}, error) { return nil, nil }