diff options
author | tjp <tjp@ctrl-c.club> | 2023-11-13 07:25:39 -0700 |
---|---|---|
committer | tjp <tjp@ctrl-c.club> | 2023-11-13 07:27:16 -0700 |
commit | 1e0f8e0aaeaf1bd2ee39c02e922238b641bcf88b (patch) | |
tree | 020e5de91f2343119fed10dede9d2c8262a3cd83 /contrib/fs/handlers.go | |
parent | a808b4692656c10bb43e2d54a2f5ef2746d231d5 (diff) |
refactor contribs to work with a Protocol interface
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 +} |