Merge branch 'web-backends' of sloum/bombadillo into develop

This commit is contained in:
Sloom Sloum Sluom IV 2019-11-15 00:01:19 -05:00 committed by Gitea
commit bb4fed6d6b
9 changed files with 148 additions and 145 deletions

View File

@ -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

View File

@ -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.
@ -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 browser 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
@ -230,10 +222,6 @@ telnetcommand
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
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.
.TP
.B
theme
Can toggle between visual modes. Valid values are \fInormal\fP and \fIinverse\fP. When set to inverse, the terminal color mode is inverted.
.TP
@ -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

View File

@ -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,19 +995,13 @@ 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.SetMessage(fmt.Sprintf("%s error: %s", wm, err.Error()), true)
c.DrawMessage()
return
}
@ -1029,23 +1023,19 @@ 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)
}
c.DrawMessage()
default:
c.SetMessage("Current 'webmode' setting does not allow http/https", false)
c.DrawMessage()
}
}

View File

@ -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", "elinks"
}
// homePath will return the path to your home directory as a string

98
http/http_render.go Normal file
View File

@ -0,0 +1,98 @@
package http
import (
"fmt"
"io/ioutil"
"net/http"
"os/exec"
"strings"
)
// Page represents the contents and links or an http/https document
type Page struct {
Content string
Links []string
}
// 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
}
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
}
return parseLinks(string(c)), nil
}
// 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 {
return false
}
ctype := resp.Header.Get("content-type")
if strings.Contains(ctype, "text") || ctype == "" {
return true
}
return false
}
func parseLinks(c string) Page {
var out Page
contentUntil := strings.LastIndex(c, "References")
if contentUntil >= 1 {
out.Content = c[:contentUntil]
} else {
out.Content = c
out.Links = make([]string, 0)
return out
}
links := c[contentUntil+11:]
links = strings.TrimSpace(links)
linkSlice := strings.Split(links, "\n")
out.Links = make([]string, 0, len(linkSlice))
for _, link := range linkSlice {
ls := strings.SplitN(link, ".", 2)
if len(ls) < 2 {
continue
}
out.Links = append(out.Links, strings.TrimSpace(ls[1]))
}
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 {
return []byte{}, err
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return []byte{}, err
}
return bodyBytes, nil
}

View File

@ -1,91 +0,0 @@
package http
import (
"fmt"
"io/ioutil"
"net/http"
"os/exec"
"strings"
)
type page struct {
Content string
Links []string
}
func Visit(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()
if err != nil {
return page{}, err
}
return parseLinks(string(c)), nil
}
// 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()
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
}
}
// 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
}
func parseLinks(c string) page {
var out page
contentUntil := strings.LastIndex(c, "References")
if contentUntil >= 1 {
out.Content = c[:contentUntil]
} else {
out.Content = c
out.Links = make([]string, 0)
return out
}
links := c[contentUntil+11:]
links = strings.TrimSpace(links)
linkSlice := strings.Split(links, "\n")
out.Links = make([]string, 0, len(linkSlice))
for _, link := range linkSlice {
ls := strings.SplitN(link, ".", 2)
if len(ls) < 2 {
continue
}
out.Links = append(out.Links, strings.TrimSpace(ls[1]))
}
return out
}
func Fetch(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return []byte{}, err
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return []byte{}, err
}
return bodyBytes, nil
}

View File

@ -2,10 +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) {
err := exec.Command("xdg-open", url).Start()
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()
if err != nil {
return "", err
}

View File

@ -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")
}

View File

@ -66,10 +66,8 @@ func saveConfig() error {
func validateOpt(opt, val string) bool {
var validOpts = map[string][]string{
"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)
@ -88,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