gemini.go (3906B) - raw
1 package main 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "io/ioutil" 8 "log" 9 "os" 10 "path" 11 "strings" 12 "text/template" 13 "time" 14 15 gmi "git.sr.ht/~adnano/go-gemini" 16 "git.sr.ht/~adnano/go-gemini/certificate" 17 ) 18 19 var gt *template.Template 20 21 func 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 33 func 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 55 func gmiIndex(w gmi.ResponseWriter, r *gmi.Request) { 56 t, err := template.ParseFiles("templates/index.gmi") 57 if err != nil { 58 log.Fatal(err) 59 } 60 files, err := getUpdatedFiles(false, "") 61 users, err := getActiveUserNames(false) 62 if err != nil { 63 log.Println(err) 64 w.WriteHeader(gmi.StatusTemporaryFailure, "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 80 func gmiPage(_ context.Context, w gmi.ResponseWriter, r *gmi.Request) { 81 // redundant 82 hostname := strings.SplitN(c.Host, ":", 2)[0] 83 if r.URL.Host == hostname { 84 gmiIndex(w, r) 85 return 86 } 87 var userName string 88 custom := domains[r.URL.Host] 89 if custom != "" { 90 userName = custom 91 } else { 92 userName = cleanPath(strings.Split(r.URL.Host, ".")[0])[1:] // clean probably unnecessary 93 } 94 fileName := cleanPath(r.URL.Path) 95 if strings.HasPrefix(fileName, "/"+HiddenFolder) { 96 w.WriteHeader(gmi.StatusNotFound, "Not found") 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.WriteHeader(gmi.StatusSuccess, "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.WriteHeader(gmi.StatusSuccess, "application/atom+xml") 111 feed := generateFeedFromUser(userName) 112 atomString := feed.toAtomFeed() 113 io.Copy(w, strings.NewReader(atomString)) 114 return 115 } 116 } 117 if fileName == "" { // mess 118 fileName = "/" 119 } 120 121 gmi.ServeFile(w, os.DirFS(path.Join(c.FilesDirectory, userName)), fileName) 122 } 123 124 var certificates = certificate.Store{} 125 126 func runGeminiServer() { 127 log.Println("Starting gemini server") 128 var err error 129 gt = template.New("main").Funcs(template.FuncMap{ 130 "urlencode": func(s string) string { 131 // Only need to escape spaces to make gemini links not break 132 return strings.Replace(s, " ", "%20", -1) 133 }, 134 }) 135 gt, err = gt.ParseGlob(path.Join(c.TemplatesDirectory, "*.gmi")) 136 if err != nil { 137 log.Fatal(err) 138 } 139 var server gmi.Server 140 server.ReadTimeout = 1 * time.Minute 141 server.WriteTimeout = 2 * time.Minute 142 143 hostname := strings.SplitN(c.Host, ":", 2)[0] 144 // is this necc? 145 err = certificates.Load(c.GeminiCertStore) 146 if err != nil { 147 log.Fatal(err) 148 } 149 certificates.Register("*." + hostname) 150 certificates.Register(hostname) 151 for k, _ := range domains { 152 certificates.Register(k) 153 } 154 server.GetCertificate = certificates.Get 155 156 var mux gmi.Mux 157 158 mux.HandleFunc("/", gmiPage) 159 server.Handler = gmi.LoggingMiddleware(&mux) 160 161 err = server.ListenAndServe(context.Background()) 162 if err != nil { 163 log.Fatal(err) 164 } 165 }