a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
at main 2.8 kB view raw
1package server 2 3import ( 4 "fmt" 5 "log" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/charmbracelet/ssh" 11 "github.com/charmbracelet/wish/scp" 12 13 "battleship-arena/internal/storage" 14) 15 16func NewSCPHandlers(uploadDir string) (scp.CopyToClientHandler, scp.CopyFromClientHandler) { 17 baseHandler := scp.NewFileSystemHandler(uploadDir) 18 19 uploadHandler := &validatingHandler{ 20 baseHandler: baseHandler, 21 uploadDir: uploadDir, 22 } 23 24 return nil, uploadHandler 25} 26 27type validatingHandler struct { 28 baseHandler scp.CopyFromClientHandler 29 uploadDir string 30} 31 32func (h *validatingHandler) Write(s ssh.Session, entry *scp.FileEntry) (int64, error) { 33 filename := filepath.Base(entry.Name) 34 log.Printf("SCP Write called: entry.Name=%s, filename=%s, size=%d", entry.Name, filename, entry.Size) 35 36 // Skip validation for directory markers 37 if filename == "~" || filename == "." || filename == ".." { 38 log.Printf("Skipping directory marker: %s", filename) 39 return 0, nil 40 } 41 42 // Validate filename 43 if !strings.HasPrefix(filename, "memory_functions_") || !strings.HasSuffix(filename, ".cpp") { 44 log.Printf("Invalid filename from %s: %s", s.User(), filename) 45 return 0, fmt.Errorf("only memory_functions_*.cpp files are accepted") 46 } 47 48 // Check if this is an admin override session 49 isAdmin := false 50 if val := s.Context().Value("admin_override"); val != nil { 51 isAdmin = val.(bool) 52 } 53 54 targetUser := s.User() 55 if isAdmin { 56 log.Printf("🔑 Admin override: uploading as %s", targetUser) 57 } 58 59 userDir := filepath.Join(h.uploadDir, targetUser) 60 if err := os.MkdirAll(userDir, 0755); err != nil { 61 log.Printf("Failed to create user directory: %v", err) 62 return 0, err 63 } 64 65 targetPath := filepath.Join(userDir, filename) 66 if _, err := os.Stat(targetPath); err == nil { 67 log.Printf("Removing old file: %s", targetPath) 68 os.Remove(targetPath) 69 } 70 71 userEntry := &scp.FileEntry{ 72 Name: filename, 73 Filepath: filepath.Join(targetUser, filename), 74 Mode: entry.Mode, 75 Size: entry.Size, 76 Reader: entry.Reader, 77 } 78 79 log.Printf("Writing to: %s", filepath.Join(h.uploadDir, userEntry.Name)) 80 81 n, err := h.baseHandler.Write(s, userEntry) 82 if err != nil { 83 log.Printf("Write error: %v", err) 84 return n, err 85 } 86 87 log.Printf("Uploaded %s from %s (%d bytes)", filename, targetUser, n) 88 89 submissionID, err := storage.AddSubmission(targetUser, filename) 90 if err != nil { 91 log.Printf("Failed to add submission: %v", err) 92 } else { 93 log.Printf("Queued submission %d for testing", submissionID) 94 } 95 96 return n, nil 97} 98 99func (h *validatingHandler) Mkdir(s ssh.Session, entry *scp.DirEntry) error { 100 // Allow mkdir but namespace it to user directory 101 userEntry := &scp.DirEntry{ 102 Name: filepath.Join(s.User(), entry.Name), 103 Mode: entry.Mode, 104 } 105 return h.baseHandler.Mkdir(s, userEntry) 106}