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) } }