149 lines
3.3 KiB
Go
149 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"encoding/hex"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
sr "tildegit.org/tjp/sliderule"
|
|
"tildegit.org/tjp/sliderule/contrib/cgi"
|
|
"tildegit.org/tjp/sliderule/contrib/fs"
|
|
"tildegit.org/tjp/sliderule/contrib/tlsauth"
|
|
"tildegit.org/tjp/sliderule/finger"
|
|
"tildegit.org/tjp/sliderule/gemini"
|
|
"tildegit.org/tjp/sliderule/gemini/gemtext/atomconv"
|
|
"tildegit.org/tjp/sliderule/gopher/gophermap"
|
|
"tildegit.org/tjp/sliderule/logging"
|
|
)
|
|
|
|
func geminiRouter(conf config) sr.Handler {
|
|
fsys := os.DirFS(conf.geminiRoot)
|
|
|
|
privileged := tlsAuth(conf.privilegedUsers)
|
|
|
|
router := &sr.Router{}
|
|
|
|
router.Route(
|
|
"/*",
|
|
gemini.GeminiOnly(true)(sr.FallthroughHandler(
|
|
fs.TitanUpload(privileged, conf.geminiRoot)(postUploadRedirect),
|
|
fs.GeminiFileHandler(fsys),
|
|
fs.GeminiDirectoryDefault(fsys, "index.gmi"),
|
|
fs.GeminiDirectoryListing(fsys, nil),
|
|
)),
|
|
)
|
|
|
|
router.Route(
|
|
"/cgi-bin/*",
|
|
gemini.GeminiOnly(false)(cgi.GeminiCGIDirectory(
|
|
"/cgi-bin/",
|
|
strings.Join([]string{".", strings.Trim(conf.geminiRoot, "/"), "cgi-bin"}, "/"),
|
|
)),
|
|
)
|
|
|
|
router.Route(
|
|
"/cgi-bin/private/*",
|
|
gemini.GeminiOnly(false)(tlsauth.GeminiAuth(privileged)(
|
|
cgi.GeminiCGIDirectory("/cgi-bin/private/", strings.Join([]string{
|
|
".",
|
|
strings.Trim(conf.geminiRoot, "/"),
|
|
"cgi-bin",
|
|
"private",
|
|
}, "/")),
|
|
)),
|
|
)
|
|
|
|
h := router.Handler()
|
|
if conf.geminiAutoAtom {
|
|
h = atomconv.Auto(h)
|
|
}
|
|
|
|
return h
|
|
}
|
|
|
|
func gopherRouter(conf config) sr.Handler {
|
|
settings := gophermap.FileSystemSettings{
|
|
ParseExtended: true,
|
|
Exec: true,
|
|
ListUsers: false,
|
|
DirMaps: []string{"gophermap"},
|
|
DirTag: "gophertag",
|
|
}
|
|
|
|
router := &sr.Router{}
|
|
|
|
router.Route(
|
|
"/*",
|
|
sr.FallthroughHandler(
|
|
cgi.ExecGopherMaps("/", conf.gopherRoot, &settings),
|
|
fs.GopherFileHandler(conf.gopherRoot, &settings),
|
|
fs.GopherDirectoryDefault(conf.gopherRoot, &settings),
|
|
fs.GopherDirectoryListing(conf.gopherRoot, &settings),
|
|
),
|
|
)
|
|
|
|
router.Route(
|
|
"/cgi-bin/*",
|
|
cgi.GopherCGIDirectory("/cgi-bin/", filepath.Join(conf.gopherRoot, "cgi-bin"), &settings),
|
|
)
|
|
|
|
return router.Handler()
|
|
}
|
|
|
|
var postUploadRedirect = sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
|
|
u := *request.URL
|
|
u.Path = strings.SplitN(u.Path, ";", 2)[0]
|
|
u.Scheme = "gemini"
|
|
return gemini.Redirect(u.String())
|
|
})
|
|
|
|
func tlsAuth(uploaders []string) tlsauth.Approver {
|
|
sort.Strings(uploaders)
|
|
|
|
return func(cert *x509.Certificate) bool {
|
|
raw := sha256.Sum256(cert.Raw)
|
|
user := hex.EncodeToString(raw[:])
|
|
|
|
_, found := sort.Find(len(uploaders), func(i int) int {
|
|
switch {
|
|
case uploaders[i] < user:
|
|
return 1
|
|
case uploaders[i] == user:
|
|
return 0
|
|
default:
|
|
return -1
|
|
}
|
|
})
|
|
return found
|
|
}
|
|
}
|
|
|
|
func fingerHandler(conf config) sr.Handler {
|
|
return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
|
|
name := strings.TrimPrefix(request.Path, "/")
|
|
if name == "" {
|
|
return finger.Error("listings not permitted")
|
|
}
|
|
|
|
path, ok := conf.fingerResponses[strings.ToLower(name)]
|
|
if !ok {
|
|
return finger.Error("user not found")
|
|
}
|
|
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
ctx.Value("errorlog").(logging.Logger).Log(
|
|
"msg", "finger response file open error",
|
|
"error", err,
|
|
)
|
|
}
|
|
|
|
return finger.Success(file)
|
|
})
|
|
}
|