package main // Utilities for filesystem, IDs, HTTP scheme detection, and input sanitization. // These helpers are used by the HTTP handlers and startup code. import ( "crypto/sha256" "encoding/hex" "fmt" "net/http" "os" "path/filepath" "strings" "time" ) // MakeDir creates the directory if it does not already exist. func MakeDir(dirname string) error { if _, err := os.Stat(dirname); os.IsNotExist(err) { return os.Mkdir(dirname, 0666) } return nil } // DeleteDirContents removes all files and directories inside dirname but not the directory itself. func DeleteDirContents(dirname string) error { entries, err := os.ReadDir(dirname) if err != nil { return err } for _, entry := range entries { if entry.IsDir() { err := os.RemoveAll(filepath.Join(dirname, entry.Name())) if err != nil { return err } } else { err := os.Remove(filepath.Join(dirname, entry.Name())) if err != nil { return err } } } return nil } // IsUsernameValid validates length, allowed characters, and rejects common placeholder names. func IsUsernameValid(username string) bool { minLength := 3 maxLength := 24 allowedChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" disallowedUsernames := []string{ "admin", "user", "guest", "test", "root", "system", "anonymous", "default", } if len(username) < minLength || len(username) > maxLength { return false } for _, char := range username { if !strings.ContainsRune(allowedChars, char) { return false } } for _, disallowed := range disallowedUsernames { if strings.EqualFold(username, disallowed) { return false } } return true } // GenerateId creates a short pseudo-unique ID derived from time and a hash. func GenerateId() string { timestamp := time.Now().UnixNano() randomComponent := time.Now().UnixNano() % 1000000 // Add some randomness data := fmt.Sprintf("%d-%d", timestamp, randomComponent) hash := sha256.Sum256([]byte(data)) return hex.EncodeToString(hash[:])[:16] } // GetScheme determines the request scheme, respecting TLS and X-Forwarded-Proto. func GetScheme(r *http.Request) string { if r.URL.Scheme != "" { return r.URL.Scheme } if r.TLS != nil { return "https" } proto := r.Header.Get("X-Forwarded-Proto") if proto != "" { return proto } return "http" } // SanitizeFilename removes path traversal characters and trims whitespace. func SanitizeFilename(filename string) string { filename = strings.ReplaceAll(filename, "/", "") filename = strings.ReplaceAll(filename, "\\", "") filename = strings.ReplaceAll(filename, "..", "") filename = strings.TrimSpace(filename) return filename }