187 lines
4.0 KiB
Go
187 lines
4.0 KiB
Go
|
package main
|
||
|
|
||
|
import "git.tebibyte.media/sashakoshka/skipper/dom"
|
||
|
import "git.tebibyte.media/sashakoshka/skipper/input"
|
||
|
import "git.tebibyte.media/sashakoshka/skipper/bookmarks"
|
||
|
|
||
|
import "fmt"
|
||
|
import "net/url"
|
||
|
import "strings"
|
||
|
import "git.sr.ht/~yotam/go-gemini"
|
||
|
|
||
|
func fetchBackward () {
|
||
|
if page.loading { return }
|
||
|
location := page.history.Backward()
|
||
|
if location != nil {
|
||
|
go fetchNoTrace(location)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func fetchForward () {
|
||
|
if page.loading { return }
|
||
|
location := page.history.Forward()
|
||
|
if location != nil {
|
||
|
go fetchNoTrace(location)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func fetch (location *url.URL) {
|
||
|
fetchBack(location, false, true)
|
||
|
}
|
||
|
|
||
|
func fetchStringUrl (stringLocation string) {
|
||
|
location, _ := url.Parse(stringLocation)
|
||
|
fetch(location)
|
||
|
}
|
||
|
|
||
|
func fetchNoTrace (location *url.URL) {
|
||
|
fetchBack(location, false, false)
|
||
|
}
|
||
|
|
||
|
func fetchSource (location *url.URL) {
|
||
|
fetchBack(location, true, false)
|
||
|
}
|
||
|
|
||
|
func fetchBack (location *url.URL, viewSource bool, addToHistory bool) {
|
||
|
if page.loading { return }
|
||
|
|
||
|
page.loading = true
|
||
|
page.readingInput = false
|
||
|
page.readingUrlInput = false
|
||
|
input.SelectNone()
|
||
|
|
||
|
page.currentUrl = location
|
||
|
if addToHistory {
|
||
|
page.history.Add(location)
|
||
|
}
|
||
|
|
||
|
if location.Scheme == "about" {
|
||
|
switch location.Opaque {
|
||
|
case "skipper":
|
||
|
fetchString(aboutGemtext, page.currentUrl)
|
||
|
case "home":
|
||
|
fetchString(homeGemtext, page.currentUrl)
|
||
|
case "bookmarks":
|
||
|
fetchString(bookmarks.Gemtext(), page.currentUrl)
|
||
|
default:
|
||
|
fetchString (
|
||
|
"# Error\n" +
|
||
|
"That page does not exist.",
|
||
|
page.currentUrl)
|
||
|
}
|
||
|
page.loading = false
|
||
|
redraw()
|
||
|
application.Draw()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
redraw()
|
||
|
application.Draw()
|
||
|
|
||
|
response, err := client.Fetch(location.String())
|
||
|
body := response.Body
|
||
|
|
||
|
defer func () {
|
||
|
if body != nil {
|
||
|
body.Close()
|
||
|
}
|
||
|
} ()
|
||
|
|
||
|
if err != nil {
|
||
|
fetchString (
|
||
|
"# Error\n" +
|
||
|
"There was an error connecting to " +
|
||
|
location.String() + "\n\n" +
|
||
|
"> " + err.Error() + "\n",
|
||
|
page.currentUrl)
|
||
|
response.Status = 0
|
||
|
}
|
||
|
|
||
|
simpleStatus := gemini.SimplifyStatus(response.Status)
|
||
|
|
||
|
if simpleStatus == gemini.StatusRedirect {
|
||
|
page.redirectCounter ++
|
||
|
} else {
|
||
|
page.redirectCounter = 0
|
||
|
}
|
||
|
|
||
|
switch simpleStatus {
|
||
|
case 0:
|
||
|
case gemini.StatusInput:
|
||
|
fetchString("# " + response.Meta, page.currentUrl)
|
||
|
page.input.Reset()
|
||
|
page.input.Obscure = response.Status == 11
|
||
|
page.input.Select()
|
||
|
page.readingInput = true
|
||
|
|
||
|
case gemini.StatusSuccess:
|
||
|
if body != nil {
|
||
|
meta := strings.Split(response.Meta, ";")
|
||
|
var mime string
|
||
|
if len(meta) >= 1 {
|
||
|
mime = meta[0]
|
||
|
}
|
||
|
|
||
|
if viewSource {
|
||
|
setDocument(dom.ParseSource(body, page.currentUrl))
|
||
|
} else if mime == "text/gemini" {
|
||
|
setDocument(dom.ParseDocument(body, page.currentUrl))
|
||
|
} else {
|
||
|
setDocument(dom.ParseSource(body, page.currentUrl))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case gemini.StatusRedirect:
|
||
|
if page.redirectCounter > 8 {
|
||
|
fetchString (fmt.Sprintf (
|
||
|
"# Too many redirects\n" +
|
||
|
"Enough! The site has redirected us too many " +
|
||
|
"times."),
|
||
|
page.currentUrl)
|
||
|
break
|
||
|
}
|
||
|
|
||
|
location, err := url.Parse(response.Meta)
|
||
|
if err != nil {
|
||
|
fetchString (fmt.Sprintf (
|
||
|
"# Error\n" +
|
||
|
"The site responded with an invalid redirect:\n\n" +
|
||
|
"%s",
|
||
|
err.Error()), page.currentUrl)
|
||
|
} else {
|
||
|
page.history.Remove()
|
||
|
defer func () {
|
||
|
go fetchBack(location, viewSource, true)
|
||
|
} ()
|
||
|
}
|
||
|
|
||
|
case gemini.StatusTemporaryFailure, gemini.StatusPermanentFailure:
|
||
|
fetchString (fmt.Sprintf (
|
||
|
"# Error %d\n" +
|
||
|
"The site responded with an error:\n\n" +
|
||
|
"> %s",
|
||
|
response.Status, response.Meta), page.currentUrl)
|
||
|
|
||
|
default:
|
||
|
fetchString (fmt.Sprintf (
|
||
|
"# Unhandled response status %d\n" +
|
||
|
"Skipper doesn't know how to handle this type of " +
|
||
|
"response yet.",
|
||
|
response.Status), page.currentUrl)
|
||
|
}
|
||
|
|
||
|
page.loading = false
|
||
|
redraw()
|
||
|
application.Draw()
|
||
|
}
|
||
|
|
||
|
func fetchString (data string, location *url.URL) {
|
||
|
setDocument(dom.ParseDocument(strings.NewReader(data), location))
|
||
|
}
|
||
|
|
||
|
func setDocument (document *dom.Document) {
|
||
|
page.document = document
|
||
|
page.scroll = 0
|
||
|
application.SetTitle(document.Title() + " - Skipper")
|
||
|
}
|