From 72c8172ab88880a02fbc50fb15773b30959ccae8 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Sun, 22 Nov 2020 18:32:29 -0800 Subject: [PATCH] Support input requests --- README.md | 15 +++++------- pkg/gmitohtml/assets.go | 33 ++++++++++++++++++--------- pkg/gmitohtml/convert.go | 4 ++-- pkg/gmitohtml/daemon.go | 49 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 76 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 54bfdcf..350ab3d 100644 --- a/README.md +++ b/README.md @@ -19,21 +19,18 @@ The resulting binary is available as `~/go/bin/gmitohtml`. ## Usage +Run daemon at [http://localhost:1967](http://localhost:1967): + +```bash +gmitohtml --daemon=localhost:1967 +``` + Convert a single document: ```bash gmitohtml < document.gmi ``` -Run as daemon at `http://localhost:8080`: - -```bash -# Start the daemon: -gmitohtml --daemon=localhost:8080 -# Access via browser: -xdg-open http://localhost:8080/gemini/twins.rocketnine.space/ -``` - ## Support Please share issues and suggestions [here](https://gitlab.com/tslocum/gmitohtml/issues). diff --git a/pkg/gmitohtml/assets.go b/pkg/gmitohtml/assets.go index 5fd034e..d7d6e3c 100644 --- a/pkg/gmitohtml/assets.go +++ b/pkg/gmitohtml/assets.go @@ -2,27 +2,38 @@ package gmitohtml var fs = make(inMemoryFS) -const indexPage = ` +const pageHeader = ` -Xenia - + + - -

Welcome to Xenia


-
- +` + +const pageFooter = ` + + +` + +const indexPage = pageHeader + ` + +

- - -` +` + pageFooter + +const inputPage = pageHeader + ` +
+GEMINICURRENTURL requests input.


+GEMINIINPUTPROMPT

+ +
+` + pageFooter func loadAssets() { fs["/assets/style.css"] = loadFile("style.css", ` diff --git a/pkg/gmitohtml/convert.go b/pkg/gmitohtml/convert.go index 1540142..3728eb7 100644 --- a/pkg/gmitohtml/convert.go +++ b/pkg/gmitohtml/convert.go @@ -105,7 +105,7 @@ func Convert(page []byte, u string) []byte { result = append(result, []byte("\n")...) } - result = append([]byte("\n\n\n\n\n\n\n"), result...) - result = append(result, []byte("\n\n")...) + result = append([]byte(pageHeader), result...) + result = append(result, []byte(pageFooter)...) return result } diff --git a/pkg/gmitohtml/daemon.go b/pkg/gmitohtml/daemon.go index 3337f9f..7c1b828 100644 --- a/pkg/gmitohtml/daemon.go +++ b/pkg/gmitohtml/daemon.go @@ -65,7 +65,40 @@ func fetch(u string, clientCertFile string, clientCertKey string) ([]byte, []byt data = data[firstNewLine+1:] } - if bytes.HasPrefix(header, []byte("text/html")) { + requestInput := bytes.HasPrefix(header, []byte("1")) + if requestInput { + requestSensitiveInput := bytes.HasPrefix(header, []byte("11")) + + data = []byte(inputPage) + + data = bytes.Replace(data, []byte("GEMINICURRENTURL"), []byte(rewriteURL(u, requestURL)), 1) + + currentURL := u + if strings.HasPrefix(currentURL, "gemini://") { + currentURL = currentURL[9:] + } + data = bytes.Replace(data, []byte("GEMINICURRENTURL"), []byte(currentURL), 1) + + prompt := "(No input prompt)" + if len(header) > 3 { + prompt = string(header[3:]) + } + data = bytes.Replace(data, []byte("GEMINIINPUTPROMPT"), []byte(prompt), 1) + + inputType := "text" + if requestSensitiveInput { + inputType = "password" + } + data = bytes.Replace(data, []byte("GEMINIINPUTTYPE"), []byte(inputType), 1) + + return header, data, nil + } + + if !bytes.HasPrefix(header, []byte("2")) { + return header, []byte(fmt.Sprintf("Unexpected header: %s", header)), nil + } + + if bytes.HasPrefix(header, []byte("20 text/html")) { return header, data, nil } return header, Convert(data, requestURL.String()), nil @@ -74,7 +107,7 @@ func fetch(u string, clientCertFile string, clientCertKey string) ([]byte, []byt func handleIndex(writer http.ResponseWriter, request *http.Request) { address := request.FormValue("address") if address != "" { - http.Redirect(writer, request, rewriteURL(address, request.URL), http.StatusTemporaryRedirect) + http.Redirect(writer, request, rewriteURL(address, request.URL), http.StatusSeeOther) return } @@ -92,6 +125,13 @@ func handleRequest(writer http.ResponseWriter, request *http.Request) { return } + inputText := request.PostFormValue("input") + if inputText != "" { + request.URL.RawQuery = inputText + http.Redirect(writer, request, request.URL.String(), http.StatusSeeOther) + return + } + pathSplit := strings.Split(request.URL.Path, "/") if len(pathSplit) < 2 || pathSplit[1] != "gemini" { writer.Write([]byte("Error: invalid protocol, only Gemini is supported")) @@ -103,6 +143,9 @@ func handleRequest(writer http.ResponseWriter, request *http.Request) { writer.Write([]byte("Error: invalid URL")) return } + if request.URL.RawQuery != "" { + u.RawQuery = request.URL.RawQuery + } header, data, err := fetch(u.String(), "", "") if err != nil { @@ -113,7 +156,7 @@ func handleRequest(writer http.ResponseWriter, request *http.Request) { if len(header) > 0 && header[0] == '3' { split := bytes.SplitN(header, []byte(" "), 2) if len(split) == 2 { - http.Redirect(writer, request, rewriteURL(string(split[1]), request.URL), http.StatusTemporaryRedirect) + http.Redirect(writer, request, rewriteURL(string(split[1]), request.URL), http.StatusSeeOther) return } }