diff options
| author | tjp <tjp@ctrl-c.club> | 2024-01-04 12:47:06 -0700 |
|---|---|---|
| committer | tjp <tjp@ctrl-c.club> | 2024-01-04 12:47:06 -0700 |
| commit | 684539c70d4b5dfac56e840f46d8dd08821e93d6 (patch) | |
| tree | 3f6f7e49964879367a511dbb5b568fb1b0ee9c42 | |
| parent | 1c96ee6e4c2268f69091aba9b7b3ed0f8515207f (diff) | |
handle input types
* spartan prompts
* gemini [sensitive]input responses
* gopher search servers
| -rw-r--r-- | actions.go | 122 | ||||
| -rw-r--r-- | handlers.go | 28 | ||||
| -rw-r--r-- | main.go | 1 | ||||
| -rw-r--r-- | state.go | 1 |
4 files changed, 141 insertions, 11 deletions
@@ -14,6 +14,8 @@ import ( "syscall" "tildegit.org/tjp/sliderule" + "tildegit.org/tjp/sliderule/gemini" + "tildegit.org/tjp/sliderule/gopher" ) var client sliderule.Client @@ -103,10 +105,66 @@ func Reload(state *BrowserState, conf *Config) error { return ErrMustBeOnAPage } - urlStr, _ := gopherURL(state.Url) - response, err := client.Fetch(urlStr) - if err != nil { - return err + urlStr, itemType := gopherURL(state.Url) + if itemType == gopher.SearchType && state.Url.RawQuery == "" { + state.Readline.SetPrompt("query: ") + line, err := state.Readline.Readline() + if err != nil { + return err + } + + state.Url.RawQuery = url.QueryEscape(strings.TrimRight(line, "\n")) + urlStr, _ = gopherURL(state.Url) + } + + var response *sliderule.Response + var err error + if state.Url.Scheme == "spartan" && state.Url.Fragment == "prompt" { + input, err := externalMessage() + if err != nil { + return err + } + body := io.LimitReader(bytes.NewBuffer(input), int64(len(input))) + + state.Url.Fragment = "" + response, err = client.Upload(state.Url.String(), body) + state.Url.Fragment = "prompt" + if err != nil { + return err + } + } else { + response, err = client.Fetch(urlStr) + if err != nil { + return err + } + } + + if state.Url.Scheme == "gemini" { + switch response.Status { + case gemini.StatusInput: + state.Readline.SetPrompt("input: ") + line, err := state.Readline.Readline() + if err != nil { + return err + } + + state.Url.RawQuery = url.QueryEscape(strings.TrimRight(line, "\n")) + response, err = client.Fetch(state.Url.String()) + if err != nil { + return err + } + case gemini.StatusSensitiveInput: + line, err := state.Readline.ReadPassword("password: ") + if err != nil { + return err + } + + state.Url.RawQuery = url.QueryEscape(strings.TrimRight(string(line), "\n")) + response, err = client.Fetch(state.Url.String()) + if err != nil { + return err + } + } } state.DocType = docType(state.Url, response) @@ -123,6 +181,59 @@ func Reload(state *BrowserState, conf *Config) error { return print(state) } +func externalMessage() ([]byte, error) { + tmpf, err := os.CreateTemp("", "*") + if err != nil { + return nil, err + } + defer func() { _ = os.Remove(tmpf.Name()) }() + + prompt := []byte("# enter input below (this line will be ignored)\n") + + err = (func() error { + defer func() { _ = tmpf.Close() }() + + if _, err := tmpf.Write(prompt); err != nil { + return err + } + + return nil + }()) + if err != nil { + return nil, err + } + + editor := os.Getenv("EDITOR") + if editor == "" { + editor = "vi" + } + editor, err = exec.LookPath(editor) + if err != nil { + return nil, err + } + + cmd := exec.Command(editor, tmpf.Name()) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return nil, err + } + + tmpf, err = os.Open(tmpf.Name()) + if err != nil { + return nil, err + } + defer func() { _ = tmpf.Close() }() + + buf, err := io.ReadAll(tmpf) + if err != nil { + return nil, err + } + + return bytes.TrimPrefix(buf, prompt), nil +} + func back(state *BrowserState) error { if state.Back == nil { return ErrNoPreviousHistory @@ -287,6 +398,9 @@ func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL, return nil, -1, ErrInvalidNumericLink } u = state.Links[i].Target + if state.Links[i].Prompt { + u.Fragment = "prompt" + } } else { i = -1 u, err = url.Parse(str) diff --git a/handlers.go b/handlers.go index e68c5f5..b4ce3f2 100644 --- a/handlers.go +++ b/handlers.go @@ -12,12 +12,13 @@ import ( "tildegit.org/tjp/sliderule/gemini/gemtext" "tildegit.org/tjp/sliderule/gopher" "tildegit.org/tjp/sliderule/gopher/gophermap" + "tildegit.org/tjp/sliderule/spartan" ) func docType(u *url.URL, response *sliderule.Response) string { _, gopherType := gopherURL(u) switch gopherType { - case gopher.MenuType: + case gopher.MenuType, gopher.SearchType: return "text/x-gophermap" case gopher.TextFileType, gopher.ErrorType, gopher.InfoMessageType: return "text/plain" @@ -49,18 +50,26 @@ func docType(u *url.URL, response *sliderule.Response) string { return "application/xml" } - if u.Scheme == "gemini" { - if response.Status == gemini.StatusSuccess { - mtype, _, err := mime.ParseMediaType(response.Meta.(string)) - if err == nil { - return mtype - } + if metaIsMediaType(u, response) { + mtype, _, err := mime.ParseMediaType(response.Meta.(string)) + if err == nil { + return mtype } } return "text/plain" } +func metaIsMediaType(u *url.URL, response *sliderule.Response) bool { + if u.Scheme == "gemini" && response.Status == gemini.StatusSuccess { + return true + } + if u.Scheme == "spartan" && response.Status == spartan.StatusSuccess { + return true + } + return false +} + func parseDoc(doctype string, body []byte, conf *Config) (string, []Link, error) { switch doctype { case "text/x-gophermap": @@ -114,7 +123,11 @@ func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) { i := 0 for _, item := range gemdoc { + isPrompt := false switch item.Type() { + case gemtext.LineTypePrompt: + isPrompt = true + fallthrough case gemtext.LineTypeLink: ll := item.(gemtext.LinkLine) u, err := url.Parse(ll.URL()) @@ -124,6 +137,7 @@ func parseGemtextDoc(body []byte, softWrap int) (string, []Link, error) { l = append(l, Link{ Text: ll.Label(), Target: u, + Prompt: isPrompt, }) label := ll.Label() if len(label) == 0 { @@ -42,6 +42,7 @@ func main() { state.Readline = rl for { + rl.SetPrompt(Prompt) line, err := rl.Readline() if err == io.EOF { break @@ -45,6 +45,7 @@ type History struct { type Link struct { Text string Target *url.URL + Prompt bool } func NewBrowserState() *BrowserState { |
