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 feed := generateFeedFromUser(user)
23 data := struct {
24 Host string
25 Title string
26 FeedEntries []FeedEntry
27 }{c.Host, feed.Title, feed.Entries}
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 logGemini(r) // TODO move into wrapper
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 logGemini(r) // TODO move into wrapper
83 var userName string
84 host, _, _ := net.SplitHostPort(r.Host)
85 custom := domains[host]
86 log.Println(custom)
87 if custom != "" {
88 userName = custom
89 } else {
90 userName = filepath.Clean(strings.Split(r.URL.Host, ".")[0]) // clean probably unnecessary
91 }
92 fileName := filepath.Clean(r.URL.Path)
93 if fileName == "/" {
94 fileName = "index.gmi"
95 } else if strings.HasPrefix(fileName, "/"+HiddenFolder) {
96 w.WriteStatus(gmi.StatusNotFound)
97 return
98 }
99 fullPath := path.Join(c.FilesDirectory, userName, fileName)
100 if fileName == "/gemlog" { // temp hack
101 _, err := os.Stat(path.Join(fullPath, "index.gmi"))
102 if err != nil {
103 w.SetMediaType("text/gemini")
104 io.Copy(w, strings.NewReader(generateGemfeedPage(userName)))
105 return
106 }
107 } else if fileName == "/gemlog/atom.xml" {
108 _, err := os.Stat(fullPath)
109 if err != nil {
110 w.SetMediaType("application/atom+xml")
111 feed := generateFeedFromUser(userName)
112 atomString := feed.toAtomFeed()
113 io.Copy(w, strings.NewReader(atomString))
114 return
115 }
116 }
117
118 gmi.ServeFile(w, gmi.Dir(path.Join(c.FilesDirectory, userName)), fileName)
119}
120
121func runGeminiServer() {
122 log.Println("Starting gemini server")
123 var err error
124 gt, err = template.ParseGlob(path.Join(c.TemplatesDirectory, "*.gmi"))
125 if err != nil {
126 log.Fatal(err)
127 }
128 var server gmi.Server
129 server.ReadTimeout = 1 * time.Minute
130 server.WriteTimeout = 2 * time.Minute
131
132 hostname := strings.SplitN(c.Host, ":", 2)[0]
133 // is this necc?
134 err = server.Certificates.Load(c.GeminiCertStore)
135 if err != nil {
136 }
137 server.CreateCertificate = func(h string) (tls.Certificate, error) {
138 log.Println("Generating certificate for", h)
139 return gmi.CreateCertificate(gmi.CertificateOptions{
140 Subject: pkix.Name{
141 CommonName: h,
142 },
143 DNSNames: []string{h},
144 Duration: time.Hour * 8760 * 100, // 100 years
145 })
146 }
147
148 var mux gmi.ServeMux
149 // replace with wildcard cert
150 mux.HandleFunc("/", gmiIndex)
151
152 var wildcardMux gmi.ServeMux
153 wildcardMux.HandleFunc("/", gmiPage)
154 server.Register(hostname, &mux)
155 server.Register("*."+hostname, &wildcardMux)
156 for k, _ := range domains { // TODO fix
157 server.Register(k, &wildcardMux)
158 }
159
160 err = server.ListenAndServe()
161 if err != nil {
162 log.Fatal(err)
163 }
164}