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