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