From 24a8642761c2e12c384178274074adac48a9866c Mon Sep 17 00:00:00 2001 From: Yotam Nachum Date: Fri, 1 Nov 2019 13:04:47 +0200 Subject: [PATCH] Initial commit --- .gitignore | 4 ++++ config.go | 29 +++++++++++++++++++++++++ config.toml | 3 +++ go.mod | 9 ++++++++ go.sum | 10 +++++++++ main.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 .gitignore create mode 100644 config.go create mode 100644 config.toml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01e530c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +go-gemini-server +server.crt +server.key +/source/* diff --git a/config.go b/config.go new file mode 100644 index 0000000..6b24a9d --- /dev/null +++ b/config.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "io/ioutil" + + "github.com/BurntSushi/toml" +) + +type Config struct { + SourceDir string `toml:"source"` + TLSCert string `toml:"tls_certificate"` + TLSKey string `toml:"tls_key"` +} + +func getConfig(path string) (Config, error) { + raw, err := ioutil.ReadFile(path) + if err != nil { + return Config{}, fmt.Errorf("failed to read config file: %v", err) + } + + var cfg Config + err = toml.Unmarshal(raw, &cfg) + if err != nil { + return Config{}, fmt.Errorf("failed to parse config file: %v", err) + } + + return cfg, nil +} diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..5e52f8c --- /dev/null +++ b/config.toml @@ -0,0 +1,3 @@ +source = "source" +tls_certificate = "server.crt" +tls_key = "server.key" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..64752a3 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module git.sr.ht/~yotam/go-gemini-server + +go 1.12 + +require ( + git.sr.ht/~yotam/go-gemini v0.0.0-20191101110121-e84e0b63cd37 + github.com/BurntSushi/toml v0.3.1 + github.com/pelletier/go-toml v1.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3faa810 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +git.sr.ht/~yotam/go-gemini v0.0.0-20191101110121-e84e0b63cd37 h1:NWDz215k8nz6PGmbCb1YsUWrp9ocqnyKLnUoDCzzgU8= +git.sr.ht/~yotam/go-gemini v0.0.0-20191101110121-e84e0b63cd37/go.mod h1:KxQlipD0Ti7MfV3itYJfuvgcvd+SOlRTtbOK+A0DCCE= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..5e1e5e4 --- /dev/null +++ b/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "log" + "net/url" + "os" + "path/filepath" + "strings" + + "git.sr.ht/~yotam/go-gemini" +) + +type MainHandler struct { + source string +} + +func (h MainHandler) Handle(r gemini.Request) gemini.Response { + u, err := url.Parse(r.URL) + if err != nil { + return gemini.Response{gemini.StatusBadRequest, err.Error(), nil} + } + + itemPath, err := filepath.Abs(filepath.Join(h.source, u.Path)) + if err != nil { + return gemini.Response{gemini.StatusTemporaryFailure, err.Error(), nil} + } + + log.Println("Serving file from", itemPath) + + if !strings.HasPrefix(itemPath, h.source) { + return gemini.Response{gemini.StatusBadRequest, "Permission denied", nil} + } + + if _, err := os.Stat(itemPath); os.IsNotExist(err) { + return gemini.Response{gemini.StatusNotFound, "Not found", nil} + } + + file, err := os.Open(itemPath) + if err != nil { + return gemini.Response{gemini.StatusTemporaryFailure, err.Error(), nil} + } + + return gemini.Response{gemini.StatusSuccess, "text/gemini", file} +} + +func main() { + cfg, err := getConfig("config.toml") + if err != nil { + log.Fatal(err) + } + + absSourceDir, err := filepath.Abs(cfg.SourceDir) + if err != nil { + log.Fatal(err) + } + + err = gemini.ListenAndServe("", cfg.TLSCert, cfg.TLSKey, MainHandler{absSourceDir}) + if err != nil { + log.Fatal(err) + } +}