mirror of
https://github.com/makew0rld/amfora.git
synced 2025-01-03 14:56:27 -05:00
Merge branch 'master' into cview-update
This commit is contained in:
commit
4bdd1a0494
20
CHANGELOG.md
20
CHANGELOG.md
@ -5,11 +5,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Changed
|
||||
- Update cview to `36671ba7d31c2287748e22966a92c5e94ff850cc` for large perf and feature updates (#107)
|
||||
- Update to tcell v2 (depencency of cview)
|
||||
|
||||
### Fixed
|
||||
- More reliable start, no more flash of unindented text, or text that stays unindented (#107)
|
||||
|
||||
|
||||
## [v1.6.0] - 2020-11-04
|
||||
### Added
|
||||
- **Support client certificates** through config (#112)
|
||||
- `ansi` config setting, to disable ANSI colors in pages (#79, #86)
|
||||
- Edit current URL with <kbd>e</kbd> (#87)
|
||||
- If `emoji_favicons` is enabled, new bookmarks will have the domain's favicon prepended (#69, #90)
|
||||
- The `BROWSER` env var is now also checked when opening web links on Unix (#93)
|
||||
- More accurate error messages based on server response code
|
||||
|
||||
### Changed
|
||||
- Disabling the `color` config setting also disables ANSI colors in pages (#79, #86)
|
||||
@ -17,12 +28,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- The web browser code doesn't check for Xorg anymore, just display variables (#93)
|
||||
- Bookmarks can be made to non-gemini URLs (#94)
|
||||
- Remove pointless directory fallbacks (#101)
|
||||
- Update cview to `36671ba7d31c2287748e22966a92c5e94ff850cc` for large perf and feature updates (#107)
|
||||
- Update to tcell v2 (depencency of cview)
|
||||
- Don't load page from cache when redirected to it (#114)
|
||||
|
||||
### Fixed
|
||||
- XDG user dir file is parsed instead of looking for XDG env vars (#97, #100)
|
||||
- More reliable start, no more flash of unindented text, or text that stays unindented (#107)
|
||||
- Support paths with spaces in HTTP browser config setting (#77)
|
||||
- Clicking "Change" on an existing bookmark without changing the text no longer removes it (#91)
|
||||
- Display HTTP Error if "Open In Portal" fails (#81)
|
||||
- Support ANSI color codes again, but only in preformatted blocks (#59)
|
||||
- Make the `..` command work lke it used to in v1.4.0
|
||||
|
||||
|
||||
## [v1.5.0] - 2020-09-01
|
||||
|
22
README.md
22
README.md
@ -121,15 +121,16 @@ Features in *italics* are in the master branch, but not in the latest release.
|
||||
- Disabled by default, enable in config
|
||||
- [x] Proxying
|
||||
- Schemes like Gopher or HTTP can be proxied through a Gemini server
|
||||
- [x] Client certificate support
|
||||
- [ ] Full client certificate UX within the client
|
||||
- Create transient and permanent certs within the client, per domain
|
||||
- Manage and browse them
|
||||
- Similar to [Kristall](https://github.com/MasterQ32/kristall)
|
||||
- https://lists.orbitalfox.eu/archives/gemini/2020/001400.html
|
||||
- [ ] Subscribe to RSS and Atom feeds and display them
|
||||
- Subscribing to page changes, similar to how Spacewalk works, will also be supported
|
||||
- *In progress on `feeds` branch*
|
||||
- [ ] Stream support
|
||||
- [ ] Full client certificate UX within the client
|
||||
- Create transient and permanent certs within the client, per domain
|
||||
- Manage and browse them
|
||||
- Similar to [Kristall](https://github.com/MasterQ32/kristall)
|
||||
- https://lists.orbitalfox.eu/archives/gemini/2020/001400.html
|
||||
- [ ] Table of contents for pages
|
||||
- [ ] Search in pages with <kbd>Ctrl-F</kbd>
|
||||
- [ ] Support Markdown rendering
|
||||
@ -140,10 +141,19 @@ The config file is written in the intuitive [TOML](https://github.com/toml-lang/
|
||||
|
||||
On Windows, the file is in `%APPDATA%\amfora\config.toml`, which usually expands to `C:\Users\<username>\AppData\Roaming\amfora\config.toml`.
|
||||
|
||||
## Client Certificates
|
||||
|
||||
Amfora has early support for client certs. Eventually Amfora will be able to generate them itself, but for you can do it by using OpenSSL (not Windows friendly):
|
||||
|
||||
```shell
|
||||
openssl req -new -subj "/CN=username" -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes -out cert.pem -keyout key.pem
|
||||
```
|
||||
|
||||
This will create a certificate and key file, that can be renamed and moved as you like. See the configuration section above for how to edit your config file to tell Amfora about them.
|
||||
|
||||
## Known Bugs
|
||||
|
||||
- Pasting on Windows is truncated, the full paste content won't be added. ([#43](https://github.com/makeworld-the-better-one/amfora/issues/43))
|
||||
- ANSI codes aren't displaying properly ([#59](https://github.com/makeworld-the-better-one/amfora/issues/59))
|
||||
|
||||
You can also check out [all the issues with the bug label](https://github.com/makeworld-the-better-one/amfora/issues?q=is%3Aopen+is%3Aissue+label%3Abug).
|
||||
|
||||
|
13
THANKS.md
Normal file
13
THANKS.md
Normal file
@ -0,0 +1,13 @@
|
||||
# THANKS
|
||||
|
||||
Thank you to the following contributors, who have helped make Amfora great. FOSS projects are a community effort, and we would be worse off without you.
|
||||
|
||||
- Sotiris Papatheodorou (@sotpapathe)
|
||||
- Chloe Kudryavtsev (@CosmicToast)
|
||||
- Adrian Hesketh (@a-h)
|
||||
- Jansen Price (@sumpygump)
|
||||
- Alex Wennerberg (@alexwennerberg)
|
||||
- Timur Ismagilov (@bouncepaw)
|
||||
- Matt Caroll (@ohiolab)
|
||||
- Patryk Niedźwiedziński (@pniedzwiedzinski)
|
||||
- Trevor Slocum (@tsclocum)
|
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.5.0"
|
||||
version = "v1.6.0"
|
||||
commit = "unknown"
|
||||
builtBy = "unknown"
|
||||
)
|
||||
|
@ -2,23 +2,74 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/makeworld-the-better-one/go-gemini"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var certCache = make(map[string][][]byte)
|
||||
|
||||
func clientCert(host string) ([]byte, []byte) {
|
||||
if cert := certCache[host]; cert != nil {
|
||||
return cert[0], cert[1]
|
||||
}
|
||||
|
||||
// Expand paths starting with ~/
|
||||
certPath, err := homedir.Expand(viper.GetString("auth.certs." + host))
|
||||
if err != nil {
|
||||
certPath = viper.GetString("auth.certs." + host)
|
||||
}
|
||||
keyPath, err := homedir.Expand(viper.GetString("auth.keys." + host))
|
||||
if err != nil {
|
||||
keyPath = viper.GetString("auth.keys." + host)
|
||||
}
|
||||
if certPath == "" && keyPath == "" {
|
||||
certCache[host] = [][]byte{nil, nil}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cert, err := ioutil.ReadFile(certPath)
|
||||
if err != nil {
|
||||
certCache[host] = [][]byte{nil, nil}
|
||||
return nil, nil
|
||||
}
|
||||
key, err := ioutil.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
certCache[host] = [][]byte{nil, nil}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
certCache[host] = [][]byte{cert, key}
|
||||
return cert, key
|
||||
}
|
||||
|
||||
// HasClientCert returns whether or not a client certificate exists for a host.
|
||||
func HasClientCert(host string) bool {
|
||||
cert, _ := clientCert(host)
|
||||
return cert != nil
|
||||
}
|
||||
|
||||
// Fetch returns response data and an error.
|
||||
// The error text is human friendly and should be displayed.
|
||||
func Fetch(u string) (*gemini.Response, error) {
|
||||
parsed, _ := url.Parse(u)
|
||||
cert, key := clientCert(parsed.Host)
|
||||
|
||||
res, err := gemini.Fetch(u)
|
||||
var res *gemini.Response
|
||||
var err error
|
||||
if cert != nil {
|
||||
res, err = gemini.FetchWithCert(u, cert, key)
|
||||
} else {
|
||||
res, err = gemini.Fetch(u)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsed, _ := url.Parse(u)
|
||||
|
||||
ok := handleTofu(parsed.Hostname(), parsed.Port(), res.Cert)
|
||||
if !ok {
|
||||
return res, ErrTofu
|
||||
@ -29,7 +80,16 @@ func Fetch(u string) (*gemini.Response, error) {
|
||||
|
||||
// FetchWithProxy is the same as Fetch, but uses a proxy.
|
||||
func FetchWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, error) {
|
||||
res, err := gemini.FetchWithHost(net.JoinHostPort(proxyHostname, proxyPort), u)
|
||||
parsed, _ := url.Parse(u)
|
||||
cert, key := clientCert(parsed.Host)
|
||||
|
||||
var res *gemini.Response
|
||||
var err error
|
||||
if cert != nil {
|
||||
res, err = gemini.FetchWithHostAndCert(net.JoinHostPort(proxyHostname, proxyPort), u, cert, key)
|
||||
} else {
|
||||
res, err = gemini.FetchWithHost(net.JoinHostPort(proxyHostname, proxyPort), u)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Package config initializes all files required for Amfora, even those used by
|
||||
// other packages. It also reads in the config file and initializes a Viper and
|
||||
// the theme
|
||||
//nolint:golint,goerr113
|
||||
package config
|
||||
|
||||
import (
|
||||
@ -38,7 +39,9 @@ var bkmkPath string
|
||||
|
||||
var DownloadsDir string
|
||||
|
||||
//nolint:golint,goerr113
|
||||
// Command for opening HTTP(S) URLs in the browser, from "a-general.http" in config.
|
||||
var HTTPCommand []string
|
||||
|
||||
func Init() error {
|
||||
|
||||
// *** Set paths ***
|
||||
@ -237,5 +240,14 @@ func Init() error {
|
||||
cview.Styles.PrimitiveBackgroundColor = GetColor("bg")
|
||||
} // Otherwise it's black by default
|
||||
|
||||
// Parse HTTP command
|
||||
HTTPCommand = viper.GetStringSlice("a-general.http")
|
||||
if len(HTTPCommand) == 0 {
|
||||
// Not a string array, interpret as a string instead
|
||||
// Split on spaces to maintain compatibility with old versions
|
||||
// The new better way to is to just define a string array in config
|
||||
HTTPCommand = strings.Fields(viper.GetString("a-general.http"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -21,10 +21,20 @@ home = "gemini://gemini.circumlunar.space"
|
||||
# If set to false, a prompt will be shown before following redirects.
|
||||
auto_redirect = false
|
||||
|
||||
# What command to run to open a HTTP(S) URL. Set to "default" to try to guess the browser,
|
||||
# or set to "off" to not open HTTP(S) URLs.
|
||||
# What command to run to open a HTTP(S) URL.
|
||||
# Set to "default" to try to guess the browser, or set to "off" to not open HTTP(S) URLs.
|
||||
# If a command is set, than the URL will be added (in quotes) to the end of the command.
|
||||
# A space will be prepended if necessary.
|
||||
# A space will be prepended to the URL.
|
||||
#
|
||||
# The best to define a command is using a string array.
|
||||
# Examples:
|
||||
# http = ["firefox"]
|
||||
# http = ["custom-browser", "--flag", "--option=2"]
|
||||
# http = ["/path/with spaces/in it/firefox"]
|
||||
#
|
||||
# Using just a string will also work, but it is deprecated,
|
||||
# and will degrade if you use paths with spaces.
|
||||
|
||||
http = "default"
|
||||
|
||||
# Any URL that will accept a query string can be put here
|
||||
@ -33,7 +43,7 @@ search = "gemini://gus.guru/search"
|
||||
# Whether colors will be used in the terminal
|
||||
color = true
|
||||
|
||||
# Whether ANSI codes from the page content should be rendered
|
||||
# Whether ANSI color codes from the page content should be rendered
|
||||
ansi = true
|
||||
|
||||
# Whether to replace list asterisks with unicode bullets
|
||||
@ -59,6 +69,20 @@ page_max_time = 10
|
||||
emoji_favicons = false
|
||||
|
||||
|
||||
[auth]
|
||||
# Authentication settings
|
||||
|
||||
[auth.certs]
|
||||
# Client certificates
|
||||
# Set domain name equal to path to client cert
|
||||
# "example.com" = "mycert.crt"
|
||||
|
||||
[auth.keys]
|
||||
# Client certificate keys
|
||||
# Set domain name equal to path to key for the client cert above
|
||||
# "example.com" = "mycert.key"
|
||||
|
||||
|
||||
[keybindings]
|
||||
# In the future there will be more settings here.
|
||||
|
||||
|
@ -18,10 +18,20 @@ home = "gemini://gemini.circumlunar.space"
|
||||
# If set to false, a prompt will be shown before following redirects.
|
||||
auto_redirect = false
|
||||
|
||||
# What command to run to open a HTTP(S) URL. Set to "default" to try to guess the browser,
|
||||
# or set to "off" to not open HTTP(S) URLs.
|
||||
# What command to run to open a HTTP(S) URL.
|
||||
# Set to "default" to try to guess the browser, or set to "off" to not open HTTP(S) URLs.
|
||||
# If a command is set, than the URL will be added (in quotes) to the end of the command.
|
||||
# A space will be prepended if necessary.
|
||||
# A space will be prepended to the URL.
|
||||
#
|
||||
# The best to define a command is using a string array.
|
||||
# Examples:
|
||||
# http = ["firefox"]
|
||||
# http = ["custom-browser", "--flag", "--option=2"]
|
||||
# http = ["/path/with spaces/in it/firefox"]
|
||||
#
|
||||
# Using just a string will also work, but it is deprecated,
|
||||
# and will degrade if you use paths with spaces.
|
||||
|
||||
http = "default"
|
||||
|
||||
# Any URL that will accept a query string can be put here
|
||||
@ -30,7 +40,7 @@ search = "gemini://gus.guru/search"
|
||||
# Whether colors will be used in the terminal
|
||||
color = true
|
||||
|
||||
# Whether ANSI codes from the page content should be rendered
|
||||
# Whether ANSI color codes from the page content should be rendered
|
||||
ansi = true
|
||||
|
||||
# Whether to replace list asterisks with unicode bullets
|
||||
@ -56,6 +66,20 @@ page_max_time = 10
|
||||
emoji_favicons = false
|
||||
|
||||
|
||||
[auth]
|
||||
# Authentication settings
|
||||
|
||||
[auth.certs]
|
||||
# Client certificates
|
||||
# Set domain name equal to path to client cert
|
||||
# "example.com" = "mycert.crt"
|
||||
|
||||
[auth.keys]
|
||||
# Client certificate keys
|
||||
# Set domain name equal to path to key for the client cert above
|
||||
# "example.com" = "mycert.key"
|
||||
|
||||
|
||||
[keybindings]
|
||||
# In the future there will be more settings here.
|
||||
|
||||
|
@ -89,7 +89,7 @@ func openBkmkModal(name string, exists bool, favicon string) (string, int) {
|
||||
if favicon != "" && !exists {
|
||||
name = favicon + " " + name
|
||||
}
|
||||
bkmkModalText = ""
|
||||
bkmkModalText = name
|
||||
bkmkModal.GetForm().AddInputField("Name: ", name, 0, nil,
|
||||
func(text string) {
|
||||
// Store for use later
|
||||
|
@ -114,6 +114,13 @@ func Init() {
|
||||
// This shouldn't occur
|
||||
return
|
||||
}
|
||||
|
||||
if query == ".." && tabs[tab].page.URL[len(tabs[tab].page.URL)-1] != '/' {
|
||||
// Support what ".." used to work like
|
||||
// If on /dir/doc.gmi, got to /dir/
|
||||
query = "./"
|
||||
}
|
||||
|
||||
target, err := current.Parse(query)
|
||||
if err != nil {
|
||||
// Invalid relative url
|
||||
|
@ -118,10 +118,12 @@ func dlChoice(text, u string, resp *gemini.Response) {
|
||||
portalURL = parsed.String() + "%3F" + query
|
||||
}
|
||||
portalURL = strings.TrimPrefix(portalURL, "gemini://") + "?raw=1"
|
||||
handleHTTP("https://portal.mozz.us/gemini/"+portalURL, false)
|
||||
browser.SetCurrentTab(strconv.Itoa(curTab))
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
ok := handleHTTP("https://portal.mozz.us/gemini/"+portalURL, false)
|
||||
if ok {
|
||||
browser.SetCurrentTab(strconv.Itoa(curTab))
|
||||
App.SetFocus(tabs[curTab].view)
|
||||
App.Draw()
|
||||
}
|
||||
return
|
||||
}
|
||||
browser.SetCurrentTab(strconv.Itoa(curTab))
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/makeworld-the-better-one/amfora/cache"
|
||||
"github.com/makeworld-the-better-one/amfora/client"
|
||||
"github.com/makeworld-the-better-one/amfora/config"
|
||||
"github.com/makeworld-the-better-one/amfora/renderer"
|
||||
"github.com/makeworld-the-better-one/amfora/structs"
|
||||
"github.com/makeworld-the-better-one/amfora/webbrowser"
|
||||
@ -142,26 +143,42 @@ func setPage(t *tab, p *structs.Page) {
|
||||
|
||||
// handleHTTP is used by handleURL.
|
||||
// It opens HTTP links and displays Info and Error modals.
|
||||
func handleHTTP(u string, showInfo bool) {
|
||||
switch strings.TrimSpace(viper.GetString("a-general.http")) {
|
||||
case "", "off":
|
||||
Error("HTTP Error", "Opening HTTP URLs is turned off.")
|
||||
case "default":
|
||||
s, err := webbrowser.Open(u)
|
||||
if err != nil {
|
||||
Error("Webbrowser Error", err.Error())
|
||||
} else if showInfo {
|
||||
Info(s)
|
||||
}
|
||||
default:
|
||||
// The config has a custom command to execute for HTTP URLs
|
||||
fields := strings.Fields(viper.GetString("a-general.http"))
|
||||
err := exec.Command(fields[0], append(fields[1:], u)...).Start()
|
||||
if err != nil {
|
||||
Error("HTTP Error", "Error executing custom browser command: "+err.Error())
|
||||
// Returns false if there was an error.
|
||||
func handleHTTP(u string, showInfo bool) bool {
|
||||
if len(config.HTTPCommand) == 1 {
|
||||
// Possibly a non-command
|
||||
|
||||
switch strings.TrimSpace(config.HTTPCommand[0]) {
|
||||
case "", "off":
|
||||
Error("HTTP Error", "Opening HTTP URLs is turned off.")
|
||||
return false
|
||||
case "default":
|
||||
s, err := webbrowser.Open(u)
|
||||
if err != nil {
|
||||
Error("Webbrowser Error", err.Error())
|
||||
return false
|
||||
}
|
||||
if showInfo {
|
||||
Info(s)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Custom command
|
||||
var err error = nil
|
||||
if len(config.HTTPCommand) > 1 {
|
||||
err = exec.Command(config.HTTPCommand[0], append(config.HTTPCommand[1:], u)...).Start()
|
||||
} else {
|
||||
err = exec.Command(config.HTTPCommand[0], u).Start()
|
||||
}
|
||||
if err != nil {
|
||||
Error("HTTP Error", "Error executing custom browser command: "+err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
App.Draw()
|
||||
return true
|
||||
}
|
||||
|
||||
// handleOther is used by handleURL.
|
||||
@ -364,11 +381,14 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
|
||||
|
||||
// Gemini URL, or one with a Gemini proxy available
|
||||
|
||||
// Load page from cache if possible
|
||||
page, ok := cache.GetPage(u)
|
||||
if ok {
|
||||
setPage(t, page)
|
||||
return ret(u, true)
|
||||
// Load page from cache if it exists,
|
||||
// and this isn't a page that was redirected to by the server (indicates dynamic content)
|
||||
if numRedirects == 0 {
|
||||
page, ok := cache.GetPage(u)
|
||||
if ok {
|
||||
setPage(t, page)
|
||||
return ret(u, true)
|
||||
}
|
||||
}
|
||||
// Otherwise download it
|
||||
bottomBar.SetText("Loading...")
|
||||
@ -446,7 +466,12 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
|
||||
}
|
||||
|
||||
page.Width = termW
|
||||
go cache.AddPage(page)
|
||||
|
||||
if !client.HasClientCert(parsed.Host) {
|
||||
// Don't cache pages with client certs
|
||||
go cache.AddPage(page)
|
||||
}
|
||||
|
||||
setPage(t, page)
|
||||
return ret(u, true)
|
||||
}
|
||||
@ -454,8 +479,8 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
|
||||
// Could be a non 20 (or 21) status code, or a different kind of document
|
||||
|
||||
// Handle each status code
|
||||
switch gemini.SimplifyStatus(res.Status) {
|
||||
case 10:
|
||||
switch res.Status {
|
||||
case 10, 11:
|
||||
userInput, ok := Input(res.Meta)
|
||||
if ok {
|
||||
// Make another request with the query string added
|
||||
@ -468,7 +493,7 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
|
||||
return ret(handleURL(t, parsed.String(), 0))
|
||||
}
|
||||
return ret("", false)
|
||||
case 30:
|
||||
case 30, 31:
|
||||
parsedMeta, err := url.Parse(res.Meta)
|
||||
if err != nil {
|
||||
Error("Redirect Error", "Invalid URL: "+err.Error())
|
||||
@ -496,13 +521,44 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
|
||||
case 40:
|
||||
Error("Temporary Failure", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 41:
|
||||
Error("Server Unavailable", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 42:
|
||||
Error("CGI Error", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 43:
|
||||
Error("Proxy Failure", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 44:
|
||||
Error("Slow Down", "You should wait "+cview.Escape(res.Meta)+" seconds before making another request.")
|
||||
return ret("", false)
|
||||
case 50:
|
||||
Error("Permanent Failure", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 51:
|
||||
Error("Not Found", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 52:
|
||||
Error("Gone", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 53:
|
||||
Error("Proxy Request Refused", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 59:
|
||||
Error("Bad Request", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 60:
|
||||
Info("The server requested a certificate. Cert handling is coming to Amfora soon!")
|
||||
Error("Client Certificate Required", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 61:
|
||||
Error("Certificate Not Authorised", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
case 62:
|
||||
Error("Certificate Not Valid", cview.Escape(res.Meta))
|
||||
return ret("", false)
|
||||
}
|
||||
|
||||
// Status code 20, but not a document that can be displayed
|
||||
go dlChoice("That file could not be displayed. What would you like to do?", u, res)
|
||||
return ret("", false)
|
||||
|
@ -80,17 +80,6 @@ func wrapLine(line string, width int, prefix, suffix string, includeFirst bool)
|
||||
return ret
|
||||
}
|
||||
|
||||
// tagLines splits a string into lines and adds a the given
|
||||
// string to the start and another to the end.
|
||||
// It is used for adding cview color tags.
|
||||
func tagLines(s, start, end string) string {
|
||||
lines := strings.Split(s, "\n")
|
||||
for i := range lines {
|
||||
lines[i] = start + lines[i] + end
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// convertRegularGemini converts non-preformatted blocks of text/gemini
|
||||
// into a cview-compatible format.
|
||||
// Since this only works on non-preformatted blocks, RenderGemini
|
||||
@ -283,11 +272,6 @@ func convertRegularGemini(s string, numLinks, width int, proxied bool) (string,
|
||||
// If it's not a gemini:// page, set this to true.
|
||||
func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []string) {
|
||||
s = cview.Escape(s)
|
||||
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
|
||||
s = cview.TranslateANSI(s)
|
||||
} else {
|
||||
s = ansiRegex.ReplaceAllString(s, "")
|
||||
}
|
||||
|
||||
lines := strings.Split(s, "\n")
|
||||
|
||||
@ -302,13 +286,22 @@ func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []stri
|
||||
if pre {
|
||||
// In a preformatted block, so add the text as is
|
||||
// Don't add the current line with backticks
|
||||
rendered += tagLines(
|
||||
buf,
|
||||
fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")),
|
||||
"[-]",
|
||||
)
|
||||
|
||||
// Support ANSI color codes in preformatted blocks - see #59
|
||||
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
|
||||
buf = cview.TranslateANSI(buf)
|
||||
} else {
|
||||
buf = ansiRegex.ReplaceAllString(buf, "")
|
||||
}
|
||||
|
||||
rendered += fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) +
|
||||
buf + "[-]"
|
||||
} else {
|
||||
// Not preformatted, regular text
|
||||
|
||||
// ANSI not allowed in regular text - see #59
|
||||
buf = ansiRegex.ReplaceAllString(buf, "")
|
||||
|
||||
ren, lks := convertRegularGemini(buf, len(links), width, proxied)
|
||||
links = append(links, lks...)
|
||||
rendered += ren
|
||||
@ -323,10 +316,21 @@ func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []stri
|
||||
// Gone through all the lines, but there still is likely a block in the buffer
|
||||
if pre {
|
||||
// File ended without closing the preformatted block
|
||||
rendered += buf
|
||||
// Same code as in the loop above
|
||||
|
||||
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
|
||||
buf = cview.TranslateANSI(buf)
|
||||
} else {
|
||||
buf = ansiRegex.ReplaceAllString(buf, "")
|
||||
}
|
||||
rendered += fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) +
|
||||
buf + "[-]"
|
||||
} else {
|
||||
// Not preformatted, regular text
|
||||
// Same code as in the loop above
|
||||
|
||||
buf = ansiRegex.ReplaceAllString(buf, "")
|
||||
|
||||
ren, lks := convertRegularGemini(buf, len(links), width, proxied)
|
||||
links = append(links, lks...)
|
||||
rendered += ren
|
||||
|
Loading…
Reference in New Issue
Block a user