1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
package fs
import (
"context"
"os"
"path/filepath"
"strings"
"text/template"
sr "tildegit.org/tjp/sliderule"
"tildegit.org/tjp/sliderule/spartan"
)
// SpartanFileHandler builds a handler which serves up files from a root directory.
//
// It only serves responses for paths which correspond to regular files or symlinks to them.
func SpartanFileHandler(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
}
requestpath := strings.Trim(strings.TrimPrefix(request.Path, urlroot), "/")
fpath := filepath.Join(fsroot, requestpath)
if isPrivate(fpath) {
return nil
}
if isf, err := isFile(fpath); err != nil {
return spartan.ServerError(err)
} else if !isf {
return nil
}
file, err := os.Open(fpath)
if err != nil {
return spartan.ServerError(err)
}
return spartan.Success(mediaType(fpath), file)
})
}
// SpartanDirectoryDefault serves up default files for directory path requests.
//
// If any of the supported filenames are found, the contents of the file is returned as the
// spartan response.
//
// It returns nil for any paths which don't correspond to a directory.
//
// When it encounters a directory path which doesn't end in a trailing slash (/) it
// redirects to the URL with the slash appended. This is necessary for relative links
// in the directory's contents to function properly.
func SpartanDirectoryDefault(fsroot, urlroot string, 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
}
if !strings.HasSuffix(request.Path, "/") {
u := *request.URL
u.Path += "/"
return spartan.Redirect(u.String())
}
requestpath := strings.Trim(strings.TrimPrefix(request.Path, urlroot), "/")
fpath := filepath.Join(fsroot, requestpath)
if isPrivate(fpath) {
return nil
}
if isd, err := isDir(fpath); err != nil {
return spartan.ServerError(err)
} else if !isd {
return nil
}
for _, fname := range filenames {
candidatepath := filepath.Join(fpath, fname)
if isf, err := isFile(candidatepath); err != nil {
return spartan.ServerError(err)
} else if !isf {
continue
}
file, err := os.Open(candidatepath)
if err != nil {
return spartan.ServerError(err)
}
return spartan.Success(mediaType(candidatepath), file)
}
return nil
})
}
// SpartanDirectoryListing produces a listing of the contents of any requested directories.
//
// It returns a nil response for any paths which don't correspond to a filesystem directory.
//
// When it encounters a directory path which doesn't end in a trailing slash (/) it
// redirects to a URL with the trailing slash appended. This is necessary for relative
// links not the directory's contents to function properly.
//
// The template may be nil, in which case DefaultSpartanDirectoryList is used instead. The
// template is then processed with RenderDirectoryListing.
func SpartanDirectoryListing(fsroot, urlroot string, template *template.Template) sr.Handler {
fsroot = strings.TrimRight(fsroot, "/")
return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
if !strings.HasSuffix(request.Path, "/") {
u := *request.URL
u.Path += "/"
return spartan.Redirect(u.String())
}
if !strings.HasPrefix(request.Path, urlroot) {
return nil
}
requestpath := strings.Trim(strings.TrimPrefix(request.Path, urlroot), "/")
fpath := filepath.Join(fsroot, requestpath)
if isPrivate(fpath) {
return nil
}
if isd, err := isDir(fpath); err != nil {
return spartan.ServerError(err)
} else if !isd {
return nil
}
if template == nil {
template = DefaultSpartanDirectoryList
}
body, err := RenderDirectoryListing(fpath, requestpath, template, request.Server)
if err != nil {
return spartan.ServerError(err)
}
return spartan.Success("text/gemini", body)
})
}
// DefaultSpartanDirectoryList is a tmeplate which renders a reasonable gemtext dir listing.
var DefaultSpartanDirectoryList = DefaultGeminiDirectoryList
|