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