/
static.go
105 lines (91 loc) · 2.38 KB
/
static.go
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
package main
import (
"embed"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"sync"
)
//go:embed static
var staticFS embed.FS
var staticHandler = http.FileServer(http.FS(staticFS))
func init() {
// register static resources.
http.HandleFunc("/static/", staticServe)
// serve local files.
http.HandleFunc("/file/", serveLocalFile)
}
func staticServe(resp http.ResponseWriter, req *http.Request) {
logRequest(req)
staticHandler.ServeHTTP(resp, req)
}
var (
errSpecialFile = errors.New("not a regular file")
errNonPackageFile = errors.New("not from a package")
)
var (
filenames = map[string]bool{}
filenamesOk = new(sync.Once)
)
func initFilenames() {
h := getAlpm()
db, er := h.LocalDb()
if er != nil {
panic(er)
}
forallFilenames(db, func(p string) error {
// p is relative to root.
filenames["/"+p] = true
return nil
})
logger.Printf("local filenames initialized with %d elements", len(filenames))
}
// isPackageFilepath decides whether a filename is from a package
// (or only probably from a package.
func isPackageFilepath(name string) bool {
filenamesOk.Do(initFilenames)
t, ok := filenames[name]
return t && ok
}
func serveLocalFile(resp http.ResponseWriter, req *http.Request) {
logRequest(req)
er := req.ParseForm()
if er != nil {
resp.WriteHeader(http.StatusInternalServerError)
ErrorPage(resp, CommonData{}, http.StatusInternalServerError, er)
return
}
filename := req.Form.Get("path")
// file path sanity checks.
switch {
case !filepath.IsAbs(filename),
filepath.HasPrefix(filename, "/etc"),
filepath.HasPrefix(filename, "/home"),
filepath.HasPrefix(filename, "/var"),
filepath.HasPrefix(filename, "/tmp"):
resp.WriteHeader(http.StatusForbidden)
er := fmt.Errorf("access to %s is forbidden", filename)
ErrorPage(resp, CommonData{}, http.StatusForbidden, er)
return
}
info, er := os.Stat(filename)
switch {
case er != nil:
resp.WriteHeader(http.StatusNotFound)
ErrorPage(resp, CommonData{}, http.StatusNotFound, er)
return
case info.Mode()&os.ModeType != 0:
// not a regular file.
resp.WriteHeader(http.StatusForbidden)
ErrorPage(resp, CommonData{}, http.StatusForbidden, errSpecialFile)
return
case !isPackageFilepath(filename):
// not a package file
resp.WriteHeader(http.StatusForbidden)
ErrorPage(resp, CommonData{}, http.StatusForbidden, errNonPackageFile)
return
}
http.ServeFile(resp, req, filename)
}