2016-01-13 19:38:45 +00:00
package main
import (
2016-01-14 17:17:35 +00:00
"encoding/base64"
"encoding/binary"
2016-01-23 11:28:31 +00:00
"encoding/json"
"fmt"
2020-01-30 17:59:45 +00:00
"html"
2016-01-23 11:28:31 +00:00
"io/ioutil"
2016-01-13 19:38:45 +00:00
"log"
2016-01-14 17:17:35 +00:00
"math/rand"
2016-01-13 19:38:45 +00:00
"os"
"strconv"
2018-11-21 02:22:42 +00:00
"golang.org/x/crypto/blake2b"
2016-01-13 19:38:45 +00:00
"srs.epita.fr/fic-server/libfic"
)
2017-12-16 00:16:30 +00:00
type ResponsesUpload struct {
2021-08-30 16:33:14 +00:00
Keys map [ int ] string ` json:"flags" `
MCQs map [ int ] bool ` json:"mcqs" `
MCQJ map [ int ] string ` json:"justifications" `
2017-12-16 00:16:30 +00:00
}
2021-11-22 14:35:07 +00:00
func treatSubmission ( pathname string , team * fic . Team , exercice_id string ) {
2018-11-16 19:00:48 +00:00
// Generate a unique identifier to follow the request in logs
2016-01-14 17:17:35 +00:00
bid := make ( [ ] byte , 5 )
binary . LittleEndian . PutUint32 ( bid , rand . Uint32 ( ) )
id := "[" + base64 . StdEncoding . EncodeToString ( bid ) + "]"
log . Println ( id , "New submission receive" , pathname )
2016-01-13 19:38:45 +00:00
2018-11-16 19:00:48 +00:00
// Parse exercice_id argument
2018-12-01 15:15:28 +00:00
eid , err := strconv . ParseInt ( exercice_id , 10 , 64 )
2018-11-16 19:00:48 +00:00
if err != nil {
log . Printf ( "%s [ERR] %s is not a valid number: %s\n" , id , exercice_id , err )
return
}
2023-04-06 13:27:10 +00:00
// Identifier should not be blacklisted for the team
if blacklistteam , ok := TeamLockedExercices [ team . Id ] ; ok {
if locked , ok := blacklistteam [ exercice_id ] ; ok && locked {
log . Printf ( "%s [!!!] Submission received for team's locked exercice %d\n" , id , eid )
return
}
}
2018-11-16 19:00:48 +00:00
// Find the given exercice
2018-12-01 15:15:28 +00:00
exercice , err := fic . GetExercice ( eid )
2018-11-16 19:00:48 +00:00
if err != nil {
2018-11-28 00:11:47 +00:00
log . Printf ( "%s [ERR] Unable to find exercice %d: %s\n" , id , eid , err )
2018-11-16 19:00:48 +00:00
return
}
2023-03-20 10:23:03 +00:00
// Check the exercice is not disabled
if exercice . Disabled {
log . Println ( "[!!!] The team submits something for a disabled exercice" )
return
}
2019-02-05 02:24:52 +00:00
// Check the team can access this exercice
if ! team . HasAccess ( exercice ) {
log . Println ( "[!!!] The team submits something for an exercice it doesn't have access yet" )
return
}
2018-11-16 19:00:48 +00:00
// Find the corresponding theme
2024-03-12 09:12:40 +00:00
var theme * fic . Theme
if exercice . IdTheme != nil {
theme , err = fic . GetTheme ( * exercice . IdTheme )
if err != nil {
log . Printf ( "%s [ERR] Unable to retrieve theme for exercice %d: %s\n" , id , eid , err )
return
}
2018-11-16 19:00:48 +00:00
2024-03-12 09:12:40 +00:00
// Theme should not be locked
if theme . Locked {
log . Printf ( "%s [!!!] Submission received for locked theme %d (eid=%d): %s\n" , id , exercice . IdTheme , eid , theme . Name )
return
}
2023-03-20 14:20:20 +00:00
}
2018-11-16 19:00:48 +00:00
// Read received file
cnt_raw , err := ioutil . ReadFile ( pathname )
if err != nil {
log . Println ( id , "[ERR] Unable to read file;" , err )
return
}
2018-11-21 02:22:42 +00:00
// Save checksum to treat duplicates
cksum := blake2b . Sum512 ( cnt_raw )
if err != nil {
log . Println ( id , "[ERR] JSON parsing error:" , err )
return
}
2018-11-16 19:00:48 +00:00
// Parse it
2017-12-16 00:16:30 +00:00
var responses ResponsesUpload
2018-11-16 19:00:48 +00:00
err = json . Unmarshal ( cnt_raw , & responses )
if err != nil {
log . Println ( id , "[ERR] JSON parsing error:" , err )
return
}
2016-01-23 11:28:31 +00:00
2018-11-16 19:00:48 +00:00
// Ensure the team didn't already solve this exercice
2021-11-22 14:35:07 +00:00
tm := team . HasSolved ( exercice )
if tm != nil {
2024-03-12 09:12:40 +00:00
if theme == nil {
log . Printf ( "%s [WRN] Team %d ALREADY solved standalone exercice %d (%s), continuing for eventual bonus flags\n" , id , team . Id , exercice . Id , exercice . Title )
} else {
log . Printf ( "%s [WRN] Team %d ALREADY solved exercice %d (%s : %s), continuing for eventual bonus flags\n" , id , team . Id , exercice . Id , theme . Name , exercice . Title )
}
2018-11-16 19:00:48 +00:00
}
2018-11-28 06:39:50 +00:00
// Handle MCQ justifications: convert to expected keyid
for cid , j := range responses . MCQJ {
if mcq , choice , err := fic . GetMCQbyChoice ( cid ) ; err != nil {
log . Println ( id , "[ERR] Unable to retrieve mcq from justification:" , err )
return
} else if mcq . IdExercice != exercice . Id {
log . Println ( id , "[ERR] We retrieve an invalid MCQ: from exercice" , mcq . IdExercice , "whereas expected from exercice" , exercice . Id )
return
2018-12-02 03:52:15 +00:00
} else if key , err := choice . GetJustifiedFlag ( exercice ) ; err != nil {
2018-12-01 14:35:15 +00:00
// Most probably, we enter here because the selected choice has not to be justified
// So, just ignore this case as it will be invalidated by the mcq validation
continue
2018-11-28 06:39:50 +00:00
} else {
if responses . Keys == nil {
2021-08-30 16:33:14 +00:00
responses . Keys = map [ int ] string { }
2018-11-28 06:39:50 +00:00
}
responses . Keys [ key . Id ] = j
}
}
2018-11-16 19:00:48 +00:00
// Check given answers
2018-11-21 02:22:42 +00:00
solved , err := exercice . CheckResponse ( cksum [ : ] , responses . Keys , responses . MCQs , team )
2018-11-16 19:00:48 +00:00
if err != nil {
log . Println ( id , "[ERR] Unable to CheckResponse:" , err )
2023-07-10 07:17:02 +00:00
appendGenQueue ( fic . GenStruct { Id : id , Type : fic . GenTeam , TeamId : team . Id } )
2018-11-16 19:00:48 +00:00
return
}
// At this point, we have treated the file, so it can be safely deleted
if err := os . Remove ( pathname ) ; err != nil {
log . Println ( id , "[ERR] Can't remove file:" , err )
}
2022-05-31 20:03:51 +00:00
if tm != nil {
2023-07-10 07:17:02 +00:00
appendGenQueue ( fic . GenStruct { Id : id , Type : fic . GenTeam , TeamId : team . Id } )
2022-05-31 20:03:51 +00:00
} else if solved {
2024-03-12 09:12:40 +00:00
if theme == nil {
log . Printf ( "%s Team %d SOLVED exercice %d (%s)\n" , id , team . Id , exercice . Id , exercice . Title )
} else {
log . Printf ( "%s Team %d SOLVED exercice %d (%s : %s)\n" , id , team . Id , exercice . Id , theme . Name , exercice . Title )
}
2018-11-16 19:00:48 +00:00
if err := exercice . Solved ( team ) ; err != nil {
log . Println ( id , "[ERR] Unable to mark the challenge as solved:" , err )
}
// Write event
2024-03-12 09:12:40 +00:00
if theme == nil {
if _ , err := fic . NewEvent ( fmt . Sprintf ( "L'équipe %s a résolu le défi %s !" , html . EscapeString ( team . Name ) , exercice . Title ) , "success" ) ; err != nil {
log . Println ( id , "[WRN] Unable to create event:" , err )
}
} else {
if lvl , err := exercice . GetLevel ( ) ; err != nil {
log . Println ( id , "[ERR] Unable to get exercice level:" , err )
} else if _ , err := fic . NewEvent ( fmt . Sprintf ( "L'équipe %s a résolu le <strong>%d<sup>e</sup></strong> défi %s !" , html . EscapeString ( team . Name ) , lvl , theme . Name ) , "success" ) ; err != nil {
log . Println ( id , "[WRN] Unable to create event:" , err )
}
2018-11-16 19:00:48 +00:00
}
2023-07-10 07:17:02 +00:00
appendGenQueue ( fic . GenStruct { Id : id , Type : fic . GenTeam , TeamId : team . Id } )
appendGenQueue ( fic . GenStruct { Id : id , Type : fic . GenThemes } )
appendGenQueue ( fic . GenStruct { Id : id , Type : fic . GenTeams } )
2016-01-13 19:38:45 +00:00
} else {
2024-03-12 09:12:40 +00:00
if theme == nil {
log . Printf ( "%s Team %d submit an invalid solution for exercice %d (%s)\n" , id , team . Id , exercice . Id , exercice . Title )
2024-03-15 16:46:50 +00:00
} else {
log . Printf ( "%s Team %d submit an invalid solution for exercice %d (%s : %s)\n" , id , team . Id , exercice . Id , theme . Name , exercice . Title )
2024-03-12 09:12:40 +00:00
}
2016-01-25 02:09:22 +00:00
2018-11-16 19:00:48 +00:00
// Write event (only on first try)
2021-11-22 14:35:07 +00:00
if tm == nil {
2024-03-12 09:12:40 +00:00
if theme == nil {
if _ , err := fic . NewEvent ( fmt . Sprintf ( "L'équipe %s tente le défi %s !" , html . EscapeString ( team . Name ) , exercice . Title ) , "warning" ) ; err != nil {
log . Println ( id , "[WRN] Unable to create event:" , err )
}
} else {
if lvl , err := exercice . GetLevel ( ) ; err != nil {
log . Println ( id , "[ERR] Unable to get exercice level:" , err )
} else if _ , err := fic . NewEvent ( fmt . Sprintf ( "L'équipe %s tente le <strong>%d<sup>e</sup></strong> défi %s !" , html . EscapeString ( team . Name ) , lvl , theme . Name ) , "warning" ) ; err != nil {
log . Println ( id , "[WRN] Unable to create event:" , err )
}
2016-01-25 02:09:22 +00:00
}
2018-01-23 00:16:25 +00:00
}
2023-07-10 07:17:02 +00:00
appendGenQueue ( fic . GenStruct { Id : id , Type : fic . GenTeam , TeamId : team . Id } )
2018-11-16 19:00:48 +00:00
}
2023-07-10 07:17:02 +00:00
appendGenQueue ( fic . GenStruct { Id : id , Type : fic . GenEvents } )
2016-01-23 11:28:31 +00:00
}