mirror of
https://code.rocketnine.space/tslocum/gmitohtml.git
synced 2024-11-27 14:48:14 +01:00
Support input requests
This commit is contained in:
parent
44aa04981e
commit
72c8172ab8
4 changed files with 76 additions and 25 deletions
15
README.md
15
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).
|
||||
|
|
|
@ -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", `
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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…
Reference in a new issue