package main import ( "bufio" "fmt" "io/ioutil" "log" "net/http" "os" "path/filepath" "strings" "net/smtp" "encoding/json" "crypto/sha256" "encoding/hex" "sort" // _ "expvar" // _ "net/http/pprof" ) // private recaptcha v3 const pivcap string = "PUT_YOUR_KEY" // score recaptcha v3 const scorecap float64 = 0.6 // Configuration structure type Config struct { LogLevel string LogFile string } // recaptcha v3 structure type jDaten struct { Success bool ChallengeTS string Hostname string Score float64 Action string } var ( configFile = "config.json" config Config filterFile = "filter.cfg" logFile = "requests.log" blockFile = "blocklist.cfg" ) func init() { // Load configuration loadConfig() } func main() { // Open the log file for writing, create it if it doesn't exist logFile, err := os.OpenFile(config.LogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Fatal(err) } defer logFile.Close() // Create a new logger with the log file logger := log.New(logFile, "", log.Ldate|log.Ltime|log.Lmicroseconds) // Create a new ServeMux mux := http.NewServeMux() // Handle all requests with the logger http.Handle("/", RequestLogger(mux, logger, config.LogLevel)) // Define your existing handlers mux.HandleFunc("/", handleRequest) mux.HandleFunc("/subscribe", handleSubscriptionForm) // Set up and start the server port := 8080 fmt.Printf("Server listening on :%d...\n", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) } func RequestLogger(targetMux http.Handler, logger *log.Logger, logLevel string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Wrap the original response writer with the buffered one secheader(w) // Check if the IP is in the blocklist if _, err := os.Stat(blockFile); err == nil { // Check if the IP is in the blocklist if isBlocked(getClientIP(r)) { // Return a 403 Forbidden response http.Error(w, "Access denied", http.StatusForbidden) return } } // Create a buffer for the response writer buffer := &responseBuffer{ResponseWriter: w} // Log request based on the specified log level switch logLevel { case "info": logger.Printf("%s\t\t%s\t\t%s\t\t%s\n", getClientIP(r), r.Method, r.Referer(), r.RequestURI) case "debug": logger.Printf( "%s\t\t%s\t\t%s\t\t%s\t\t%s\n", getClientIP(r), r.Method, r.Referer(), r.RequestURI, r.Form.Encode(), r.Header, ) default: logger.Printf("Unknown log level: %s", logLevel) } // Serve the request using the original response writer targetMux.ServeHTTP(buffer, r) }) } // Custom response buffer to capture the status code type responseBuffer struct { http.ResponseWriter statusCode int } func (b *responseBuffer) WriteHeader(code int) { b.statusCode = code b.ResponseWriter.WriteHeader(code) } func loadConfig() { config = Config{ LogLevel: "info", // Standardwert für debug_level //LogFile: "requests.log", // Standardwert für log_file } file, err := os.Open("./server.cfg") if err != nil { log.Printf("Config file not found, using default settings.") return } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() parts := strings.SplitN(line, "=", 2) if len(parts) == 2 { key := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) switch key { case "log_level": config.LogLevel = value case "log_file": config.LogFile = value } } } if err := scanner.Err(); err != nil { log.Fatalf("Error reading config file: %v", err) } } func handleRequest(w http.ResponseWriter, r *http.Request) { // Get the requested path from the URL requestedPath := filepath.Clean(r.URL.Path) // Create the absolute path to the requested resource in the ./www directory absolutePath := filepath.Join(".", "www", requestedPath) // Check if the requested path is a directory fileInfo, err := os.Stat(absolutePath) if err != nil { if os.IsNotExist(err) { log.Printf("%v\t\t\t\t404 Page not found: %s", getClientIP(r), requestedPath) http.NotFound(w, r) return } log.Printf("\t\t\t\tError checking if path is a directory: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if fileInfo.IsDir() { var indexFileName string /* if proto := r.Header.Get("X-Forwarded-Proto"); proto == "https" { indexFileName = "index.html" } else { indexFileName = "index_http.html" } */ indexFileName = "index.html" // If the requested path is a directory, check for index.html indexFilePath := filepath.Join(absolutePath, indexFileName) _, err := os.Stat(indexFilePath) if err == nil { // If index.html exists, serve its content serveFile(w, r, indexFilePath) } else { // If index.html doesn't exist, list the directory listDirectory(w, r, absolutePath, requestedPath, indexFilePath) } } else { // If the requested path is a file, serve its contents serveFile(w, r, absolutePath) } } func handleSubscriptionForm(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } err := r.ParseForm() if err != nil { http.Error(w, "Bad Request", http.StatusBadRequest) return } email := r.FormValue("email") if email == "" { http.Error(w, "Bad Request: Email is required", http.StatusBadRequest) return } token := r.FormValue("recaptcha_token") if token == "" { http.Error(w, "Bad Token", http.StatusBadRequest) return } // Get the real IP from X-Real-IP header, fallback to RemoteAddr requesterIP := getClientIP(r) if (OnPage(email, token, requesterIP , "subscript") == "1") { // Send the subscription email to root@localhost log.Printf("%v\t\t\t\tSubscription: - %s", requesterIP, email) // Send email err = sendSubscriptionEmail(email) if err != nil { log.Printf("\t\t\t\tError sending subscription email: %v", err) http.Error(w, fmt.Sprintf("Internal Server Error: %v", err), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html") fmt.Fprintln(w, "") fmt.Fprintln(w, "Subscription email sent successfully") } else { w.Header().Set("Content-Type", "text/html") fmt.Fprintln(w, "") fmt.Fprintln(w, "Google Token failed") } } func sendSubscriptionEmail(email string) error { to := "root@localhost" subject := "New Subscription" body := fmt.Sprintf("Email: %s has subscribed.", email) // Update the "From" address to use the email parameter msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s", email, to, subject, body) err := smtp.SendMail("localhost:25", nil, email, []string{to}, []byte(msg)) if err != nil { log.Printf("Error sending subscription email: %v", err) return err } return nil } func getVisitorFingerprint(r *http.Request) (string, error) { // ip := strings.Split(r.RemoteAddr, ":")[0] ip := getClientIP(r) userAgent := r.UserAgent() // Concatenate IP address and user agent fingerprint := ip + userAgent // Hash the fingerprint using SHA-256 to create a fixed-size hash hash := sha256.New() hash.Write([]byte(fingerprint)) hashBytes := hash.Sum(nil) return hex.EncodeToString(hashBytes), nil } func serveFile(w http.ResponseWriter, r *http.Request, path string) { // Open the file with proper error handling file, err := os.Open(path) if err != nil { log.Printf("\t\t\t\tError opening file: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } defer file.Close() // Get the visitor's fingerprint fingerprint, err := getVisitorFingerprint(r) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } // Extract the filename and extension filename := filepath.Base(path) // Check if the fingerprint is already in the file fingerprintPath := filepath.Join(filepath.Dir(path), "."+filename) if fingerprintExists(fingerprintPath, fingerprint) { // Fingerprint is already present, serve the file content and return fileInfo, err := file.Stat() if err != nil { log.Printf("\t\t\t\tError getting file info: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.ServeContent(w, r, path, fileInfo.ModTime(), file) return } // Append the fingerprint as a new line to the file fingerprintFile, err := os.OpenFile(fingerprintPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { log.Printf("\t\t\t\tError opening or creating fingerprint file: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } defer fingerprintFile.Close() // Write the fingerprint as a new line to the file _, err = fmt.Fprintln(fingerprintFile, fingerprint) if err != nil { log.Printf("\t\t\t\tError writing fingerprint to file: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } // Use http.ServeContent for efficient file serving fileInfo, err := file.Stat() if err != nil { log.Printf("\t\t\t\tError getting file info: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } http.ServeContent(w, r, path, fileInfo.ModTime(), file) } // Function to check if a fingerprint already exists in the file func fingerprintExists(fingerprintPath, fingerprint string) bool { existingFingerprints, err := ioutil.ReadFile(fingerprintPath) if err != nil { return false // Assume the file does not exist } fingerprints := strings.Split(string(existingFingerprints), "\n") for _, fp := range fingerprints { if fp == fingerprint { return true } } return false } func listDirectory(w http.ResponseWriter, r *http.Request, basePath, relativePath , indexFilePath string) { // Open the directory with proper error handling dir, err := os.Open(basePath) if err != nil { log.Printf("\t\t\t\tError opening directory: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } defer dir.Close() // Read the directory entries files, err := dir.Readdir(-1) if err != nil { log.Printf("\t\t\t\tError reading directory: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } // Filter out files/directories starting with a dot var visibleFiles []os.FileInfo for _, file := range files { if !strings.HasPrefix(file.Name(), ".") { visibleFiles = append(visibleFiles, file) } } // Sortiere alphabetisch sort.Slice(visibleFiles, func(i, j int) bool { return visibleFiles[i].Name() < visibleFiles[j].Name() }) // Create a styled HTML page to list the directory contents w.Header().Set("Content-Type", "text/html") fmt.Fprintln(w, `
`) fmt.Fprintf(w, "Name | ") fmt.Fprintln(w, "Last Modified | ") fmt.Fprintln(w, "Size | ") fmt.Fprintln(w, "Visitors | ") fmt.Fprintln(w, "||
---|---|---|---|---|---|
Parent Directory | |||||
%s | ", linkPath, file.Name()) if fileInfo.IsDir() { // If it's a directory, set file size to an empty string fmt.Fprintf(w, "--- | ") } else { // If it's a file, display file date fmt.Fprintf(w, "%s | ", file.ModTime().Format("2006-01-02 15:04:05")) } if fileInfo.IsDir() { // If it's a directory, set file size to an empty string fmt.Fprintf(w, "--- | ") } else { // If it's a file, display file size fmt.Fprintf(w, "%d bytes | ", file.Size()) } fmt.Fprintf(w, "%v | ", visitorCount) fmt.Fprintln(w, "