package fbhttp import ( "log" "net/http" "strconv" "github.com/tomasen/realip" "github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/runner" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/users" ) type handleFunc func(w http.ResponseWriter, r *http.Request, d *data) (int, error) type data struct { *runner.Runner settings *settings.Settings server *settings.Server store *storage.Storage user *users.User raw interface{} } // Check implements rules.Checker. func (d *data) Check(path string) bool { if d.user.HideDotfiles && rules.MatchHidden(path) { return false } allow := true for _, rule := range d.settings.Rules { if rule.Matches(path) { allow = rule.Allow } } for _, rule := range d.user.Rules { if rule.Matches(path) { allow = rule.Allow } } return allow } func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler { // trackingWriter wraps the ResponseWriter to detect if headers/body were already written. type trackingWriter struct { http.ResponseWriter wroteHeader bool wroteBody bool } func (tw *trackingWriter) WriteHeader(code int) { if !tw.wroteHeader { tw.wroteHeader = true tw.ResponseWriter.WriteHeader(code) } } func (tw *trackingWriter) Write(b []byte) (int, error) { // Any Write implies headers committed (status 200 if none set) if !tw.wroteHeader { tw.wroteHeader = true } tw.wroteBody = true return tw.ResponseWriter.Write(b) } handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Wrap writer to prevent superfluous WriteHeader calls tw := &trackingWriter{ResponseWriter: w} for k, v := range globalHeaders { tw.Header().Set(k, v) } settings, err := store.Settings.Get() if err != nil { log.Fatalf("ERROR: couldn't get settings: %v\n", err) return } status, err := fn(tw, r, &data{ Runner: &runner.Runner{Enabled: server.EnableExec, Settings: settings}, store: store, settings: settings, server: server, }) if status >= 400 || err != nil { clientIP := realip.FromRequest(r) log.Printf("%s: %v %s %v", r.URL.Path, status, clientIP, err) } if status != 0 { txt := http.StatusText(status) if status == http.StatusBadRequest && err != nil { txt += " (" + err.Error() + ")" } // Only write an error response if nothing was written yet. if !tw.wroteHeader && !tw.wroteBody { http.Error(tw, strconv.Itoa(status)+" "+txt, status) } return } }) return stripPrefix(prefix, handler) }