package fs import ( "context" "net/url" "os" "path/filepath" "strings" "text/template" sr "tildegit.org/tjp/sliderule" ) func fileHandler(protocol sr.ServerProtocol, fsroot, urlroot string) sr.Handler { fsroot = strings.TrimRight(fsroot, "/") return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response { if !strings.HasPrefix(request.Path, urlroot) { return nil } fpath, _ := rebasePath(fsroot, urlroot, request) if isPrivate(fpath) { return nil } if isf, err := isFile(fpath); err != nil { return protocol.TemporaryServerError(err) } else if !isf { return nil } file, err := os.Open(fpath) if err != nil { return protocol.TemporaryServerError(err) } return protocol.Success(filepath.Base(fpath), file) }) } func directoryDefault( protocol sr.ServerProtocol, fsroot string, urlroot string, redirectSlash bool, filenames ...string, ) sr.Handler { fsroot = strings.TrimRight(fsroot, "/") return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response { if !strings.HasPrefix(request.Path, urlroot) { return nil } fpath, _ := rebasePath(fsroot, urlroot, request) if isPrivate(fpath) { return nil } if isd, err := isDir(fpath); err != nil { return protocol.TemporaryServerError(err) } else if !isd { return nil } if redirectSlash && !strings.HasSuffix(request.Path, "/") { return protocol.PermanentRedirect(appendSlash(request.URL)) } for _, fname := range filenames { fpath := filepath.Join(fpath, fname) if isf, err := isFile(fpath); err != nil { return protocol.TemporaryServerError(err) } else if !isf { continue } file, err := os.Open(fpath) if err != nil { return protocol.TemporaryServerError(err) } return protocol.Success(filepath.Base(fpath), file) } return nil }) } func directoryListing( protocol sr.ServerProtocol, fsroot string, urlroot string, successFilename string, redirectSlash bool, tmpl *template.Template, ) sr.Handler { fsroot = strings.TrimRight(fsroot, "/") return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response { if !strings.HasPrefix(request.Path, urlroot) { return nil } dpath, rpath := rebasePath(fsroot, urlroot, request) if isPrivate(dpath) { return nil } if isd, err := isDir(dpath); err != nil { return protocol.TemporaryServerError(err) } else if !isd { return nil } if redirectSlash && !strings.HasSuffix(request.Path, "/") { return protocol.PermanentRedirect(appendSlash(request.URL)) } body, err := RenderDirectoryListing(dpath, rpath, tmpl, request.Server) if err != nil { return protocol.TemporaryServerError(err) } return protocol.Success(successFilename, body) }) } func rebasePath(fsroot, urlroot string, request *sr.Request) (string, string) { p := strings.TrimPrefix(request.Path, urlroot) p = strings.Trim(p, "/") return filepath.Join(fsroot, p), p } func appendSlash(u *url.URL) *url.URL { v := *u v.Path += "/" return &v } func isDir(path string) (bool, error) { info, err := os.Stat(path) if err != nil { if isNotFound(err) { err = nil } return false, err } return info.IsDir() && info.Mode()&4 == 4, nil } func isFile(path string) (bool, error) { info, err := os.Stat(path) if err != nil { if isNotFound(err) { err = nil } return false, err } m := info.Mode() return m.IsRegular() && m&4 == 4, nil }