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