// Copyright (C) 2022 Marius Schellenberger package srv import ( "bytes" "encoding/xml" "html/template" "net/http" "strings" "cachefs/pkg/fs" "golang.org/x/exp/slices" ) type statusInterceptor struct { w http.ResponseWriter status int } func (s *statusInterceptor) Header() http.Header { return s.w.Header() } func (s *statusInterceptor) Write(p []byte) (int, error) { return s.w.Write(p) } func (s *statusInterceptor) WriteHeader(statusCode int) { s.w.WriteHeader(statusCode) s.status = statusCode } func (s *statusInterceptor) Status() int { if s.status == 0 { return http.StatusOK } return s.status } type dirContents struct { AllPaths []string `xml:"a"` Base string `xml:"-"` Dirs dirs `xml:"-"` Files files `xml:"-"` } type dir struct { Name template.HTML URI template.HTML } type dirs []dir func (dirs) Less(i, j dir) bool { return i.Name < j.Name } type file struct { Name template.HTML URI template.HTML Anchor string Status int Prio int Errc int Running bool } type files []file func (files) Less(i, j file) bool { return i.Name < j.Name } type responseInterceptor struct { buf bytes.Buffer w http.ResponseWriter status int } func (r *responseInterceptor) Header() http.Header { return r.w.Header() } func (r *responseInterceptor) Write(p []byte) (int, error) { return r.buf.Write(p) } func (r *responseInterceptor) WriteHeader(statusCode int) { r.status = statusCode } func (r *responseInterceptor) Status() int { if r.status == 0 { return http.StatusOK } return r.status } func (r *responseInterceptor) GetPaths(path string, fs *fs.FS, relative bool) (dc dirContents, err error) { buf := r.buf.Bytes() buf = bytes.ReplaceAll(buf, []byte{'&'}, []byte("&")) err = xml.Unmarshal(buf, &dc) if err != nil { return } dc.Base = path path = strings.TrimSuffix(path, "/") for _, p := range dc.AllPaths { p = strings.ReplaceAll(p, "&", "&") p = strings.ReplaceAll(p, "'", "'") name := template.HTML(p) full := path + "/" + p uri := template.HTML(full) if relative { uri = template.HTML(p) } if p[len(p)-1] == '/' { dc.Dirs = append(dc.Dirs, dir{ Name: name, URI: uri, }) } else { dc.Files = append(dc.Files, file{ Name: name, URI: uri, Anchor: anchor(p), Status: fs.CacheStatus(full), }) } } slices.SortFunc(dc.Dirs, dc.Dirs.Less) slices.SortFunc(dc.Files, dc.Files.Less) return } func getPreloads(path string, fs *fs.FS) (dc dirContents) { dc.Base = path for _, p := range fs.Preloads() { dc.Files = append(dc.Files, file{ Name: template.HTML(p.Name), URI: template.HTML(p.Name), Status: p.Status, Prio: p.Prio, Errc: p.Errc, Running: p.Running, }) } return } func anchor(s string) string { if i := strings.LastIndex(s, "/"); i > 0 && i+1 < len(s) { s = s[i+1:] } s = strings.ReplaceAll(s, " ", "_") return strings.ToLower(s) }