all repos — flounder @ 95e0f7252c189f24de72484930fcd1775b06e015

A small site builder for the Gemini protocol

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}