From 28c2d6b277a7001ceffb5eed64d71e62b2b17590 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Wed, 13 Nov 2019 22:23:57 -0800 Subject: [PATCH 1/6] Diversifies the web rendering backends and removes confusing configuration options --- client.go | 41 ++++++++++----------------- defaults.go | 4 +-- http/{lynx_mode.go => http_render.go} | 37 ++++++++++++------------ main.go | 1 + 4 files changed, 35 insertions(+), 48 deletions(-) rename http/{lynx_mode.go => http_render.go} (62%) diff --git a/client.go b/client.go index b590755..fa9f1ca 100644 --- a/client.go +++ b/client.go @@ -643,7 +643,7 @@ func (c *client) search(query, url, question string) { c.DrawMessage() return } else if strings.TrimSpace(entry) == "" { - c.ClearMessage() + c.ClearMessage() c.DrawMessage() return } @@ -995,17 +995,11 @@ func (c *client) handleFinger(u Url) { } func (c *client) handleWeb(u Url) { - // Following http is disabled - if strings.ToUpper(c.Options["openhttp"]) != "TRUE" { - c.SetMessage("'openhttp' is not set to true, cannot open web link", false) - c.DrawMessage() - return - } - - // Use lynxmode - if strings.ToUpper(c.Options["lynxmode"]) == "TRUE" { + wm := strings.ToLower(c.Options["webmode"]) + switch wm { + case "lynx", "w3m", "elinks": if http.IsTextFile(u.Full) { - page, err := http.Visit(u.Full, c.Width-1) + page, err := http.Visit(wm, u.Full, c.Width-1) if err != nil { c.SetMessage(fmt.Sprintf("Lynx error: %s", err.Error()), true) c.DrawMessage() @@ -1029,23 +1023,18 @@ func (c *client) handleWeb(u Url) { } c.saveFile(u, fn) } - - // Open in default web browser if available - } else { - if strings.ToUpper(c.Options["terminalonly"]) == "TRUE" { - c.SetMessage("'terminalonly' is set to true and 'lynxmode' is not enabled, cannot open web link", false) - c.DrawMessage() + case "gui": + c.SetMessage("Attempting to open in gui web browser", false) + c.DrawMessage() + msg, err := http.OpenInBrowser(u.Full) + if err != nil { + c.SetMessage(err.Error(), true) } else { - c.SetMessage("Attempting to open in web browser", false) - c.DrawMessage() - msg, err := http.OpenInBrowser(u.Full) - if err != nil { - c.SetMessage(err.Error(), true) - } else { - c.SetMessage(msg, false) - } - c.DrawMessage() + c.SetMessage(msg, false) } + default: + c.SetMessage("Current 'webmode' setting does not allow http/https", false) + c.DrawMessage() } } diff --git a/defaults.go b/defaults.go index 9da05a5..c14db8c 100644 --- a/defaults.go +++ b/defaults.go @@ -48,14 +48,12 @@ var defaultOptions = map[string]string{ "homeurl": "gopher://bombadillo.colorfield.space:70/1/user-guide.map", "savelocation": homePath(), "searchengine": "gopher://gopher.floodgap.com:70/7/v2/vs", - "openhttp": "false", "telnetcommand": "telnet", "configlocation": xdgConfigPath(), "theme": "normal", // "normal", "inverted" - "terminalonly": "true", "tlscertificate": "", "tlskey": "", - "lynxmode": "false", + "webmode": "none", // "none", "gui", "lynx", "w3m" } // homePath will return the path to your home directory as a string diff --git a/http/lynx_mode.go b/http/http_render.go similarity index 62% rename from http/lynx_mode.go rename to http/http_render.go index 782aee3..ea2e987 100644 --- a/http/lynx_mode.go +++ b/http/http_render.go @@ -13,12 +13,22 @@ type page struct { Links []string } -func Visit(url string, width int) (page, error) { +func Visit(webmode, url string, width int) (page, error) { if width > 80 { width = 80 } - w := fmt.Sprintf("-width=%d", width) - c, err := exec.Command("lynx", "-dump", w, url).Output() + var w string + switch webmode { + case "lynx": + w = "-width" + case "w3m": + w = "-cols" + case "elinks": + w = "-dump-width" + default: + return page{}, fmt.Errorf("Invalid webmode setting") + } + c, err := exec.Command(webmode, "-dump", w, fmt.Sprintf("%d", width), url).Output() if err != nil { return page{}, err } @@ -28,26 +38,16 @@ func Visit(url string, width int) (page, error) { // Returns false on err or non-text type // Else returns true func IsTextFile(url string) bool { - c, err := exec.Command("lynx", "-dump", "-head", url).Output() + resp, err := http.Head(url) if err != nil { return false } - content := string(c) - content = strings.ToLower(content) - headers := strings.Split(content, "\n") - for _, header := range headers { - if strings.Contains(header, "content-type:") && strings.Contains(header, "text") { - return true - } else if strings.Contains(header, "content-type:") { - return false - } + ctype := resp.Header.Get("content-type") + if strings.Contains(ctype, "text") || ctype == "" { + return true } - // If we made it here, there is no content-type header. - // So in the event of the unknown, lets render to the - // screen. This will allow redirects to get rendered - // as well. - return true + return false } func parseLinks(c string) page { @@ -72,7 +72,6 @@ func parseLinks(c string) page { out.Links = append(out.Links, strings.TrimSpace(ls[1])) } return out - } func Fetch(url string) ([]byte, error) { diff --git a/main.go b/main.go index 4b644bb..84627c8 100644 --- a/main.go +++ b/main.go @@ -66,6 +66,7 @@ func saveConfig() error { func validateOpt(opt, val string) bool { var validOpts = map[string][]string{ + "webmode": []string{"none", "gui", "lynx", "w3m", "elinks"}, "openhttp": []string{"true", "false"}, "theme": []string{"normal", "inverse"}, "terminalonly": []string{"true", "false"}, From 9d29acc8e895bf0a77ec5ca65bd3b072784fb8ed Mon Sep 17 00:00:00 2001 From: asdf Date: Thu, 14 Nov 2019 21:22:17 +1100 Subject: [PATCH 2/6] Review of webmode, corrections to error messages and comments --- client.go | 3 ++- defaults.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index fa9f1ca..23f4925 100644 --- a/client.go +++ b/client.go @@ -1001,7 +1001,7 @@ func (c *client) handleWeb(u Url) { if http.IsTextFile(u.Full) { page, err := http.Visit(wm, u.Full, c.Width-1) if err != nil { - c.SetMessage(fmt.Sprintf("Lynx error: %s", err.Error()), true) + c.SetMessage(fmt.Sprintf("%s error: %s", wm, err.Error()), true) c.DrawMessage() return } @@ -1032,6 +1032,7 @@ func (c *client) handleWeb(u Url) { } else { c.SetMessage(msg, false) } + c.DrawMessage() default: c.SetMessage("Current 'webmode' setting does not allow http/https", false) c.DrawMessage() diff --git a/defaults.go b/defaults.go index c14db8c..a89e7f5 100644 --- a/defaults.go +++ b/defaults.go @@ -53,7 +53,7 @@ var defaultOptions = map[string]string{ "theme": "normal", // "normal", "inverted" "tlscertificate": "", "tlskey": "", - "webmode": "none", // "none", "gui", "lynx", "w3m" + "webmode": "none", // "none", "gui", "lynx", "w3m", "elinks" } // homePath will return the path to your home directory as a string From 24fd98fa9b589518276939440346c985917d06b8 Mon Sep 17 00:00:00 2001 From: Brian Evans Date: Thu, 14 Nov 2019 10:10:05 -0800 Subject: [PATCH 3/6] Added manpage updates as well as an update to using the gui for opening web content --- README.md | 8 ++++---- bombadillo.1 | 19 ++++++------------- http/open_browser_linux.go | 11 +++++++++++ http/open_browser_other.go | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8a64232..0ed41ae 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Bombadillo - a non-web client +# Bombadillo - a non-web browser -Bombadillo is a non-web client for the terminal. +Bombadillo is a non-web browser for the terminal. ![a screenshot of the bombadillo client](bombadillo-screenshot.png) @@ -18,7 +18,7 @@ Support for the following protocols is also available via integration with 3rd p * http/https * Web support is opt-in (turned off by default). * Links can be opened in a user's default web browser when in a graphical environment. - * Web pages can be rendered directly in Bombadillo if [Lynx](https://lynx.invisible-island.net/) is installed on the system to handle the document parsing. + * Web pages can be rendered directly in Bombadillo if [Lynx](https://lynx.invisible-island.net/), [w3m](http://w3m.sourceforge.net/), or [elinks](http://elinks.or.cz/) are installed on the system to handle the document parsing. ## Getting Started @@ -115,7 +115,7 @@ Bombadillo development is largely handled by Sloum, with help from asdf, jboverf There are many ways to contribute to Bombadillo, including a fair few that don't require knowledge of programming: -- Try out the client and let us know if you have a suggestion for improvement, or if you find a bug. +- Try out the browser and let us know if you have a suggestion for improvement, or if you find a bug. - Read the documentation and let us know if something isn't well explained, or needs correction. - Maybe you have a cool logo or some art that you think would look nice. diff --git a/bombadillo.1 b/bombadillo.1 index ce200fa..0258ae6 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -210,14 +210,6 @@ homeurl The url that \fBbombadillo\fP navigates to when the program loads or when the \fIhome\fP or \fIh\fP LINE COMMAND is issued. This should be a valid url. If a scheme/protocol is not included, gopher will be assumed. .TP .B -lynxmode -Sets whether or not to use lynx as a rendering engine for http/https requests, and display results in \fBbombadillo\fP. Lynx must be installed, and \fIopenhttp\fP must be set to \fItrue\fP. Valid values are \fItrue\fP and \fIfalse\fP. -.TP -.B -openhttp -Tells the client whether or not to try to follow web (http/https) links. If set to \fItrue\fP, \fBbombadillo\fP will try to open these links in a user's default web browser, or render them via lynx, depending on the status of \fIlynxmode\fP and \fIterminalonly\fP settings. Valid values are \fItrue\fP and \fIfalse\fP. -.TP -.B savelocation The path to the directory that \fBbombadillo\fP should write files to. This must be a valid filepath for the system, must be a directory, and must already exist. .TP @@ -227,11 +219,7 @@ The url to use for the LINE COMMANDs \fI?\fP and \fIsearch\fP. Should be a valid .TP .B telnetcommand -Tells the client what command to use to start a telnet session. Should be a valid command, including any flags. The address being navigated to will be added to the end of the command. -.TP -.B -terminalonly -Sets whether web links should handled within \fBbombadillo\fP only, or if they can be opened in a user's external web browser. If \fIopenhttp\fP is true, and this setting is true, web links will be handled within \fBbombadillo\fP only. If \fIlynxmode\fP is also true, web links will be rendered in bombadillo via lynx. If \fIopenhttp\fP is true, \fIlynxmode\fP is disabled, and this setting is disabled, \fBbombadillo\fP will open web links in a user's default web browser. Valid values are \fItrue\fP and \fIfalse\fP. +Tells the browser what command to use to start a telnet session. Should be a valid command, including any flags. The address being navigated to will be added to the end of the command. .TP .B theme @@ -244,6 +232,11 @@ A path to a tls certificate file on a user's local filesystem. Defaults to NULL. .B tlskey A path to a tls key that pairs with the tlscertificate setting, on a user's local filesystem. Defaults to NULL. Both \fItlskey\fP and \fItlscertificate\fP must be set for client certificates to work in gemini. +.TP +.B +webmode +Controls behavior when following web links. The following values are valid: \fInone\fP will disable following web links, \fIgui\fP will have the browser attempt to open web links in a user's default graphical web browser; \fIlynx\fP, \fIw3m\fP, and \fIelinks\fP will have the browser attempt to use the selected terminal web browser to handle the rendering of web pages and will display the pages directly in Bombadillo. + .SH BUGS There are very likely bugs. Many known bugs can be found in the issues section of \fBbombadillo\fP's source code repository (see \fIlinks\fP). .SH LINKS diff --git a/http/open_browser_linux.go b/http/open_browser_linux.go index dc99845..5be7640 100644 --- a/http/open_browser_linux.go +++ b/http/open_browser_linux.go @@ -5,6 +5,17 @@ package http import "os/exec" func OpenInBrowser(url string) (string, error) { + // Check for a local display server, this is + // not a silver bullet but should help ssh + // connected users on many systems get accurate + // messaging and not spin off processes needlessly + err := exec.Command("type", "Xorg").Run() + if err != nil { + return "", fmt.Errorf("No gui is available, check 'webmode' setting") + } + + // Use start rather than run or output in order + // to release the process and not block err := exec.Command("xdg-open", url).Start() if err != nil { return "", err diff --git a/http/open_browser_other.go b/http/open_browser_other.go index c6e5342..1388c6b 100644 --- a/http/open_browser_other.go +++ b/http/open_browser_other.go @@ -7,5 +7,5 @@ package http import "fmt" func OpenInBrowser(url string) (string, error) { - return "", fmt.Errorf("Unsupported os for browser detection") + return "", fmt.Errorf("Unsupported os for 'webmode' 'gui' setting") } From ef27e54d0e0d86faf259a17e21d8d7f3a59def50 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Thu, 14 Nov 2019 19:30:58 -0800 Subject: [PATCH 4/6] Fixes errors introduced by a distracted commit earlier, also handles linting --- http/http_render.go | 24 ++++++++++++++++-------- http/open_browser_linux.go | 26 ++++++++++++++++---------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/http/http_render.go b/http/http_render.go index ea2e987..f68ee2d 100644 --- a/http/http_render.go +++ b/http/http_render.go @@ -8,12 +8,16 @@ import ( "strings" ) -type page struct { +// Page represents the contents and links or an http/https document +type Page struct { Content string Links []string } -func Visit(webmode, url string, width int) (page, error) { +// Visit is the main entry to viewing a web document in bombadillo. +// It takes a url, a terminal width, and which web backend the user +// currently has set. Visit returns a Page and an error +func Visit(webmode, url string, width int) (Page, error) { if width > 80 { width = 80 } @@ -26,17 +30,18 @@ func Visit(webmode, url string, width int) (page, error) { case "elinks": w = "-dump-width" default: - return page{}, fmt.Errorf("Invalid webmode setting") + return Page{}, fmt.Errorf("Invalid webmode setting") } c, err := exec.Command(webmode, "-dump", w, fmt.Sprintf("%d", width), url).Output() if err != nil { - return page{}, err + return Page{}, err } return parseLinks(string(c)), nil } -// Returns false on err or non-text type -// Else returns true +// IsTextFile makes an http(s) head request to a given URL +// and determines if the content-type is text based. It then +// returns a bool func IsTextFile(url string) bool { resp, err := http.Head(url) if err != nil { @@ -50,8 +55,8 @@ func IsTextFile(url string) bool { return false } -func parseLinks(c string) page { - var out page +func parseLinks(c string) Page { + var out Page contentUntil := strings.LastIndex(c, "References") if contentUntil >= 1 { out.Content = c[:contentUntil] @@ -74,6 +79,9 @@ func parseLinks(c string) page { return out } +// Fetch makes an http(s) request and returns the []bytes +// for the response and an error. Fetch is used for saving +// the source file of an http(s) document func Fetch(url string) ([]byte, error) { resp, err := http.Get(url) if err != nil { diff --git a/http/open_browser_linux.go b/http/open_browser_linux.go index 5be7640..3b7dfee 100644 --- a/http/open_browser_linux.go +++ b/http/open_browser_linux.go @@ -2,21 +2,27 @@ package http -import "os/exec" +import ( + "fmt" + "os" + "os/exec" +) +// OpenInBrowser checks for the presence of a display server +// and environment variables indicating a gui is present. If found +// then xdg-open is called on a url to open said url in the default +// gui web browser for the system func OpenInBrowser(url string) (string, error) { - // Check for a local display server, this is - // not a silver bullet but should help ssh - // connected users on many systems get accurate - // messaging and not spin off processes needlessly - err := exec.Command("type", "Xorg").Run() - if err != nil { + disp := os.Getenv("DISPLAY") + wayland := os.Getenv("WAYLAND_DISPLAY") + _, err := exec.LookPath("Xorg") + if disp == "" && wayland == "" && err != nil { return "", fmt.Errorf("No gui is available, check 'webmode' setting") } - // Use start rather than run or output in order - // to release the process and not block - err := exec.Command("xdg-open", url).Start() + // Use start rather than run or output in order + // to release the process and not block + err = exec.Command("xdg-open", url).Start() if err != nil { return "", err } From 33a8c51de1a196e1d94729b7d5bd1d11fff3b2a1 Mon Sep 17 00:00:00 2001 From: asdf Date: Fri, 15 Nov 2019 15:19:47 +1100 Subject: [PATCH 5/6] Further changes to the man page for webmode --- bombadillo.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bombadillo.1 b/bombadillo.1 index afcd4fc..7402a1d 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -46,11 +46,11 @@ Telnet is not supported directly, but addresses will be followed and opened as a .TP .B http, https -Neither of the world wide web protocols are supported directly. However, \fBbombadillo\fP can open web links in a user's default web browser, or display web content directly in the browser via the lynx web browser. Opening http/https links is opt-in only, controlled by the \fIopenhttp\fP setting. +Neither of the world wide web protocols are supported directly. \fBbombadillo\fP can be configured to open web links in a user's default graphical web browser. It is also possible to display web content directly in \fBbombadillo\fP using lynx, w3m, or elinks terminal web browsers to render pages. Opening http/https links is opt-in only, controlled by the \fIwebmode\fP setting. .IP -Opening links in a default web browser only works if a GUI environment is available. +Opening links in a default graphical web browser will only work in a GUI environment. .IP -Opening web content directly in the browser requires the lynx web browser, and is enabled using the \fIlynxmode\fP setting. Web content is processed using lynx, and then displayed in \fBbombadillo\fP. +Displaying web content directly in \fBbombadillo\fP requires lynx, w3m or elinks terminal web browsers are installed on the system. .SH COMMANDS .SS KEY COMMANDS These commands work as a single keypress anytime \fBbombadillo\fP is not taking in a line based command. This is the default command mode of \fBbombadillo\fP. From aa46cc26000072d82d8dac88ed7b79f97dc4771e Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Thu, 14 Nov 2019 20:35:34 -0800 Subject: [PATCH 6/6] Removed unneeded settings --- main.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index a603682..ef01c34 100644 --- a/main.go +++ b/main.go @@ -66,11 +66,8 @@ func saveConfig() error { func validateOpt(opt, val string) bool { var validOpts = map[string][]string{ - "webmode": []string{"none", "gui", "lynx", "w3m", "elinks"}, - "openhttp": []string{"true", "false"}, - "theme": []string{"normal", "inverse"}, - "terminalonly": []string{"true", "false"}, - "lynxmode": []string{"true", "false"}, + "webmode": []string{"none", "gui", "lynx", "w3m", "elinks"}, + "theme": []string{"normal", "inverse"}, } opt = strings.ToLower(opt) @@ -89,7 +86,7 @@ func validateOpt(opt, val string) bool { func lowerCaseOpt(opt, val string) string { switch opt { - case "openhttp", "theme", "terminalonly": + case "webmode", "theme": return strings.ToLower(val) default: return val