diff options
Diffstat (limited to 'contrib/fs/handlers.go')
-rw-r--r-- | contrib/fs/handlers.go | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/contrib/fs/handlers.go b/contrib/fs/handlers.go new file mode 100644 index 0000000..75422d9 --- /dev/null +++ b/contrib/fs/handlers.go @@ -0,0 +1,162 @@ +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 +} |