summaryrefslogtreecommitdiff
path: root/contrib/fs/handlers.go
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/fs/handlers.go')
-rw-r--r--contrib/fs/handlers.go162
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
+}