From 4bf44b16562335b3d09b6df0150521bb5b5f776f Mon Sep 17 00:00:00 2001 From: Feuerfuchs Date: Mon, 18 May 2020 12:12:43 +0200 Subject: WIP: Refactoring --- internal/port/gopher.go | 217 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 internal/port/gopher.go (limited to 'internal/port/gopher.go') diff --git a/internal/port/gopher.go b/internal/port/gopher.go new file mode 100644 index 0000000..ebeb213 --- /dev/null +++ b/internal/port/gopher.go @@ -0,0 +1,217 @@ +package port + +import ( + "bytes" + "fmt" + "html/template" + "io" + "log" + "net" + "net/http" + "net/url" + "strings" + + "git.vulpes.one/Feuerfuchs/port/port/libgopher" + + "github.com/davidbyttow/govips/pkg/vips" + "github.com/temoto/robotstxt" +) + +type Item struct { + Link template.URL + Type string + Text string +} + +func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetList AssetList, uri string, hostport string, d libgopher.Directory) error { + var title string + + out := make([]Item, len(d.Items)) + + for i, x := range d.Items { + if x.Type == libgopher.INFO && x.Selector == "TITLE" { + title = x.Description + continue + } + + tr := Item{ + Text: x.Description, + Type: x.Type.String(), + } + + if x.Type == libgopher.INFO { + out[i] = tr + continue + } + + if strings.HasPrefix(x.Selector, "URL:") || strings.HasPrefix(x.Selector, "/URL:") { + link := strings.TrimPrefix(strings.TrimPrefix(x.Selector, "/"), "URL:") + if strings.HasPrefix(link, "gemini://") { + link = fmt.Sprintf( + "/gemini/%s", + strings.TrimPrefix(link, "gemini://"), + ) + } else if strings.HasPrefix(link, "gopher://") { + link = fmt.Sprintf( + "/gopher/%s", + strings.TrimPrefix(link, "gopher://"), + ) + } + tr.Link = template.URL(link) + } else { + var linkHostport string + if x.Port != "70" { + linkHostport = net.JoinHostPort(x.Host, x.Port) + } else { + linkHostport = x.Host + } + + path := url.PathEscape(x.Selector) + path = strings.Replace(path, "%2F", "/", -1) + tr.Link = template.URL( + fmt.Sprintf( + "/gopher/%s/%s%s", + linkHostport, + string(byte(x.Type)), + path, + ), + ) + } + + out[i] = tr + } + + if title == "" { + if uri != "" { + title = fmt.Sprintf("%s/%s", hostport, uri) + } else { + title = hostport + } + } + + return tpl.Execute(w, TemplateVariables{ + Title: title, + URI: fmt.Sprintf("%s/%s", hostport, uri), + Assets: assetList, + Lines: out, + Protocol: "gopher", + }) +} + +// GopherHandler returns a Handler that proxies requests +// to the specified Gopher server as denoated by the first argument +// to the request path and renders the content using the provided template. +// The optional robots parameters points to a robotstxt.RobotsData struct +// to test user agents against a configurable robotst.txt file. +func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + agent := req.UserAgent() + path := strings.TrimPrefix(req.URL.Path, "/gopher/") + + if robotsdata != nil && robotsdebug && !robotsdata.TestAgent(path, agent) { + log.Printf("UserAgent %s ignored robots.txt", agent) + } + + parts := strings.Split(path, "/") + hostport := parts[0] + + if len(hostport) == 0 { + http.Redirect(w, req, "/", http.StatusFound) + return + } + + title := hostport + + var qs string + + if req.URL.RawQuery != "" { + qs = fmt.Sprintf("?%s", url.QueryEscape(req.URL.RawQuery)) + } + + uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) + if err != nil { + if e := tpl.Execute(w, TemplateVariables{ + Title: title, + URI: hostport, + Assets: assetList, + RawText: fmt.Sprintf("Error: %s", err), + Error: true, + Protocol: "gopher", + }); e != nil { + log.Println("Template error: " + e.Error()) + log.Println(err.Error()) + } + return + } + + if uri != "" { + title = fmt.Sprintf("%s/%s", hostport, uri) + } + + res, err := libgopher.Get( + fmt.Sprintf( + "gopher://%s/%s%s", + hostport, + uri, + qs, + ), + ) + + if err != nil { + if e := tpl.Execute(w, TemplateVariables{ + Title: title, + URI: fmt.Sprintf("%s/%s", hostport, uri), + Assets: assetList, + RawText: fmt.Sprintf("Error: %s", err), + Error: true, + Protocol: "gopher", + }); e != nil { + log.Println("Template error: " + e.Error()) + } + return + } + + if res.Body != nil { + if len(parts) < 2 { + io.Copy(w, res.Body) + } else if strings.HasPrefix(parts[1], "0") && !strings.HasSuffix(uri, ".xml") && !strings.HasSuffix(uri, ".asc") { + buf := new(bytes.Buffer) + buf.ReadFrom(res.Body) + + if err := tpl.Execute(w, TemplateVariables{ + Title: title, + URI: fmt.Sprintf("%s/%s", hostport, uri), + Assets: assetList, + RawText: buf.String(), + Protocol: "gopher", + }); err != nil { + log.Println("Template error: " + err.Error()) + } + } else if strings.HasPrefix(parts[1], "T") { + _, _, err = vips.NewTransform(). + Load(res.Body). + ResizeStrategy(vips.ResizeStrategyAuto). + ResizeWidth(160). + Quality(75). + Output(w). + Apply() + } else { + io.Copy(w, res.Body) + } + } else { + if err := renderGopherDirectory(w, tpl, assetList, uri, hostport, res.Dir); err != nil { + if e := tpl.Execute(w, TemplateVariables{ + Title: title, + URI: fmt.Sprintf("%s/%s", hostport, uri), + Assets: assetList, + RawText: fmt.Sprintf("Error: %s", err), + Error: true, + Protocol: "gopher", + }); e != nil { + log.Println("Template error: " + e.Error()) + log.Println(e.Error()) + } + } + } + } +} -- cgit v1.3.1