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