Browse Source

Support input requests

pull/5/head
Trevor Slocum 2 years ago
parent
commit
72c8172ab8
  1. 11
      README.md
  2. 33
      pkg/gmitohtml/assets.go
  3. 4
      pkg/gmitohtml/convert.go
  4. 49
      pkg/gmitohtml/daemon.go

11
README.md

@ -19,19 +19,16 @@ The resulting binary is available as `~/go/bin/gmitohtml`.
## Usage
Convert a single document:
Run daemon at [http://localhost:1967](http://localhost:1967):
```bash
gmitohtml < document.gmi
gmitohtml --daemon=localhost:1967
```
Run as daemon at `http://localhost:8080`:
Convert a single document:
```bash
# Start the daemon:
gmitohtml --daemon=localhost:8080
# Access via browser:
xdg-open http://localhost:8080/gemini/twins.rocketnine.space/
gmitohtml < document.gmi
```
## Support

33
pkg/gmitohtml/assets.go

@ -2,27 +2,38 @@ package gmitohtml
var fs = make(inMemoryFS)
const indexPage = `
const pageHeader = `
<!DOCTYPE html>
<html>
<head>
<title>Xenia</title>
<link rel="stylesheet" href="/assets/style.css"></link>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/assets/style.css">
</head>
<body>
<h1>Welcome to Xenia</h1><br>
<form method="post" action="/">
<input type="text" name="address" placeholder="Address" size="50" autofocus> <input type="submit" value="Go">
<body>`
const pageFooter = `
</body>
</html>
`
const indexPage = pageHeader + `
<form method="post" action="/" novalidate>
<input type="url" name="address" placeholder="Address" size="50" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus> <input type="submit" value="Go">
</form>
<br>
<ul>
<li><a href="/gemini/gus.guru/">GUS - Gemini Universal Search</a></li>
<li><a href="/gemini/gemini.circumlunar.space/">Gemini protocol</a></li>
<li><a href="https://gitlab.com/tslocum/xenia">Xenia</a></li>
</ul>
</body>
</html>
`
` + pageFooter
const inputPage = pageHeader + `
<form method="post" action="GEMINICURRENTURL">
<b>GEMINICURRENTURL</b> requests input.<br><br><br>
<b>GEMINIINPUTPROMPT</b><br><br>
<input type="GEMINIINPUTTYPE" name="input" placeholder="Input" size="50" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus> <input type="submit" value="Go">
</form>
` + pageFooter
func loadAssets() {
fs["/assets/style.css"] = loadFile("style.css", `

4
pkg/gmitohtml/convert.go

@ -105,7 +105,7 @@ func Convert(page []byte, u string) []byte {
result = append(result, []byte("</pre>\n")...)
}
result = append([]byte("<!DOCTYPE html>\n<html>\n<head>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<link rel=\"stylesheet\" href=\"/assets/style.css\"></link>\n</head>\n<body>\n"), result...)
result = append(result, []byte("\n</body>\n</html>")...)
result = append([]byte(pageHeader), result...)
result = append(result, []byte(pageFooter)...)
return result
}

49
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
}
}

Loading…
Cancel
Save