a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
1package main 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 14func newSCPHandlers() (scp.CopyToClientHandler, scp.CopyFromClientHandler) { 15 // Use FileSystemHandler as base 16 baseHandler := scp.NewFileSystemHandler(uploadDir) 17 18 // Wrap it to add validation and user namespacing 19 uploadHandler := &validatingHandler{ 20 baseHandler: baseHandler, 21 } 22 23 // Return nil for downloads (disabled), wrapped handler for uploads 24 return nil, uploadHandler 25} 26 27type validatingHandler struct { 28 baseHandler scp.CopyFromClientHandler 29} 30 31func (h *validatingHandler) Write(s ssh.Session, entry *scp.FileEntry) (int64, error) { 32 filename := filepath.Base(entry.Name) 33 log.Printf("SCP Write called: entry.Name=%s, filename=%s, size=%d", entry.Name, filename, entry.Size) 34 35 // Skip validation for directory markers (SCP protocol negotiation) 36 if filename == "~" || filename == "." || filename == ".." { 37 log.Printf("Skipping directory marker: %s", filename) 38 return 0, nil 39 } 40 41 // Validate filename (must be memory_functions_*.cpp) 42 if !strings.HasPrefix(filename, "memory_functions_") || !strings.HasSuffix(filename, ".cpp") { 43 log.Printf("Invalid filename from %s: %s", s.User(), filename) 44 return 0, fmt.Errorf("only memory_functions_*.cpp files are accepted") 45 } 46 47 // Create user-specific subdirectory 48 userDir := filepath.Join(uploadDir, s.User()) 49 if err := os.MkdirAll(userDir, 0755); err != nil { 50 log.Printf("Failed to create user directory: %v", err) 51 return 0, err 52 } 53 54 // Remove old file if it exists to ensure clean overwrite 55 targetPath := filepath.Join(userDir, filename) 56 if _, err := os.Stat(targetPath); err == nil { 57 log.Printf("Removing old file: %s", targetPath) 58 os.Remove(targetPath) 59 } 60 61 // Modify the entry to write to user's subdirectory 62 userEntry := &scp.FileEntry{ 63 Name: filepath.Join(s.User(), filename), 64 Mode: entry.Mode, 65 Size: entry.Size, 66 Reader: entry.Reader, 67 } 68 69 log.Printf("Writing to: %s", filepath.Join(uploadDir, userEntry.Name)) 70 71 n, err := h.baseHandler.Write(s, userEntry) 72 if err != nil { 73 log.Printf("Write error: %v", err) 74 return n, err 75 } 76 77 log.Printf("Uploaded %s from %s (%d bytes)", filename, s.User(), n) 78 79 // Add submission and trigger testing 80 submissionID, err := addSubmission(s.User(), filename) 81 if err != nil { 82 log.Printf("Failed to add submission: %v", err) 83 } else { 84 log.Printf("Queued submission %d for testing", submissionID) 85 // The worker will pick it up automatically 86 } 87 88 return n, nil 89} 90 91func (h *validatingHandler) Mkdir(s ssh.Session, entry *scp.DirEntry) error { 92 // Allow mkdir but namespace it to user directory 93 userEntry := &scp.DirEntry{ 94 Name: filepath.Join(s.User(), entry.Name), 95 Mode: entry.Mode, 96 } 97 return h.baseHandler.Mkdir(s, userEntry) 98}