package main
import (
"html/template"
"io/fs"
"log"
"net/http"
"os"
"strings"
)
var logger *log.Logger
var appTemplate *template.Template = template.New("app")
type TemplateData struct {
TOC []string
Entry string
Title string
EntryTitle string
}
func loadTemplate() {
data, err := os.ReadFile(TemplateFile)
if err != nil { logger.Panic(err) }
appTemplate.Parse(string(data))
}
func getTOC() []string {
entriesDirFs := os.DirFS(EntriesDirectory)
m, err := fs.Glob(entriesDirFs, "*")
if err != nil { logger.Panic(err) }
return m
}
func getEntry(name string) (string, error) {
data, err := os.ReadFile(strings.TrimRight(EntriesDirectory, "/") + "/" + name)
if err != nil { return "", err }
return string(data), err
}
func handleApplication(w http.ResponseWriter, req *http.Request) {
var entry string
var err error
entryName := strings.Trim(req.URL.Path, "/")
if entryName != "" {
if strings.Contains(entryName, "/") || strings.Contains(entryName, ".") {
// path traversal
logger.Println("Possible path traversal attempt from", req.RemoteAddr, "to", entryName)
w.WriteHeader(http.StatusForbidden)
return
}
// load entry
entry, err = getEntry(entryName)
if err != nil {
logger.Println("Couldn't open entry", entryName)
w.WriteHeader(http.StatusNotFound)
}
}
err = appTemplate.ExecuteTemplate(w, "app", TemplateData{TOC: getTOC(), Entry: entry, Title: MainTitle, EntryTitle: entryName})
if err != nil { logger.Println(err) }
}
func main() {
// get logger
logger = log.Default()
// load template
loadTemplate()
// handle static files
staticHandler := http.StripPrefix("/static/", http.FileServer(http.Dir(StaticDirectory)))
http.Handle("/static/", staticHandler)
// handle application
http.HandleFunc("/", handleApplication)
// start server
logger.Println("Starting server on", ServerListen)
err := http.ListenAndServe(ServerListen, nil)
if err != nil { logger.Panic(err) }
}