gemini.go (view raw)
1package main
2
3import (
4 "bytes"
5 "crypto/tls"
6 "crypto/x509/pkix"
7 gmi "git.sr.ht/~adnano/go-gemini"
8 "io"
9 "io/ioutil"
10 "log"
11 "os"
12 "path"
13 "path/filepath"
14 "strings"
15 "text/template"
16 "time"
17)
18
19var gt *template.Template
20
21func generateGemfeedPage(user string) string {
22 feedItems := generateFeedFromUser(user)
23 data := struct {
24 Host string
25 Title string
26 FeedEntries []FeedEntry
27 }{c.Host, user + "'s Gemlog", feedItems}
28 var buff bytes.Buffer
29 gt.ExecuteTemplate(&buff, "gemfeed.gmi", data)
30 return buff.String()
31}
32
33func generateFolderPage(fullpath string) string {
34 files, _ := ioutil.ReadDir(fullpath)
35 var renderedFiles = []File{}
36 for _, file := range files {
37 // Very awkward
38 res := fileFromPath(path.Join(fullpath, file.Name()))
39 renderedFiles = append(renderedFiles, res)
40 }
41 var buff bytes.Buffer
42 data := struct {
43 Host string
44 Folder string
45 Files []File
46 }{c.Host, getLocalPath(fullpath), renderedFiles}
47 err := gt.ExecuteTemplate(&buff, "folder.gmi", data)
48 if err != nil {
49 log.Println(err)
50 return ""
51 }
52 return buff.String()
53}
54
55func gmiIndex(w *gmi.ResponseWriter, r *gmi.Request) {
56 log.Println("Index request")
57 t, err := template.ParseFiles("templates/index.gmi")
58 if err != nil {
59 log.Fatal(err)
60 }
61 files, err := getIndexFiles(false)
62 users, err := getActiveUserNames()
63 if err != nil {
64 log.Println(err)
65 w.WriteHeader(40, "Internal server error")
66 }
67 data := struct {
68 Host string
69 SiteTitle string
70 Files []*File
71 Users []string
72 }{
73 Host: c.Host,
74 SiteTitle: c.SiteTitle,
75 Files: files,
76 Users: users,
77 }
78 t.Execute(w, data)
79}
80
81func gmiPage(w *gmi.ResponseWriter, r *gmi.Request) {
82 userName := filepath.Clean(strings.Split(r.URL.Host, ".")[0]) // clean probably unnecessary
83 fileName := filepath.Clean(r.URL.Path)
84 if fileName == "/" {
85 fileName = "index.gmi"
86 } else if strings.HasPrefix(fileName, "/.hidden") {
87 w.WriteStatus(gmi.StatusNotFound)
88 return
89 }
90 log.Println("Request for gemini file", fileName, "for user", userName)
91
92 if fileName == "/gemlog" { // temp hack
93 _, err := os.Stat(path.Join(c.FilesDirectory, userName, fileName, "index.gmi"))
94 if err != nil {
95 w.SetMediaType("text/gemini")
96 io.Copy(w, strings.NewReader(generateGemfeedPage(userName)))
97 return
98 }
99 }
100
101 gmi.ServeFile(w, gmi.Dir(path.Join(c.FilesDirectory, userName)), fileName)
102}
103
104func runGeminiServer() {
105 log.Println("Starting gemini server")
106 var err error
107 gt, err = template.ParseGlob(path.Join(c.TemplatesDirectory, "*.gmi"))
108 if err != nil {
109 log.Fatal(err)
110 }
111 var server gmi.Server
112 server.ReadTimeout = 1 * time.Minute
113 server.WriteTimeout = 2 * time.Minute
114
115 hostname := strings.SplitN(c.Host, ":", 2)[0]
116 // is this necc?
117 err = server.Certificates.Load(c.GeminiCertStore)
118 if err != nil {
119 }
120 server.CreateCertificate = func(h string) (tls.Certificate, error) {
121 log.Println("Generating certificate for", h)
122 return gmi.CreateCertificate(gmi.CertificateOptions{
123 Subject: pkix.Name{
124 CommonName: hostname,
125 },
126 DNSNames: []string{h},
127 Duration: time.Hour * 760, // one month
128 })
129 }
130
131 var mux gmi.ServeMux
132 // replace with wildcard cert
133 mux.HandleFunc("/", gmiIndex)
134
135 var wildcardMux gmi.ServeMux
136 wildcardMux.HandleFunc("/", gmiPage)
137 server.Register(hostname, &mux)
138 server.Register("*."+hostname, &wildcardMux)
139
140 err = server.ListenAndServe()
141 if err != nil {
142 log.Fatal(err)
143 }
144}