package main import ( "fmt" "log" "net/url" "os" "path/filepath" "strings" gemini "git.sr.ht/~yotam/go-gemini" ) // Handler is the main handler of the server type Handler struct { cfg Config } func (h Handler) urlAbsPath(rawURL string) (string, error) { u, err := url.Parse(rawURL) if err != nil { return "", gemini.Error{Err: err, Status: gemini.StatusBadRequest} } itemPath, err := filepath.Abs(filepath.Join(h.cfg.SourceDir, u.Path)) if err != nil { return "", gemini.Error{Err: err, Status: gemini.StatusTemporaryFailure} } if !strings.HasPrefix(itemPath, h.cfg.SourceDir) { return "", gemini.Error{Err: fmt.Errorf("Permission Denied"), Status: gemini.StatusBadRequest} } return itemPath, nil } func (h Handler) isFile(path string) bool { fileInfo, err := os.Stat(path) if err != nil { return false } return fileInfo.Mode().IsRegular() } func (h Handler) getFilePath(rawURL string) (string, error) { itemPath, err := h.urlAbsPath(rawURL) if err != nil { return "", err } if h.isFile(itemPath) { return itemPath, nil } indexPath := filepath.Join(itemPath, "index.gemi") if h.isFile(indexPath) { return indexPath, nil } return "", gemini.Error{Err: fmt.Errorf("File Not Found"), Status: gemini.StatusNotFound} } // Handle implement the gemini.Handler interface by serving files from a given source directory func (h Handler) Handle(r gemini.Request) gemini.Response { itemPath, err := h.getFilePath(r.URL) if err != nil { return gemini.ErrorResponse(err) } log.Println("Serving file from", itemPath) file, err := os.Open(itemPath) if err != nil { return gemini.ErrorResponse(err) } return gemini.Response{Status: gemini.StatusSuccess, Meta: "text/gemini", Body: file} }