diff options
author | Feuerfuchs <git@feuerfuchs.dev> | 2020-05-18 12:12:43 +0200 |
---|---|---|
committer | Feuerfuchs <git@feuerfuchs.dev> | 2020-05-18 12:12:43 +0200 |
commit | 4bf44b16562335b3d09b6df0150521bb5b5f776f (patch) | |
tree | 576723e6dc9f9db48d0892f7ec354a11b973aef4 /internal/port/gopher.go | |
parent | Added 2 more glyphs (diff) | |
download | gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.tar.gz gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.tar.bz2 gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.zip |
WIP: Refactoring
Diffstat (limited to 'internal/port/gopher.go')
-rw-r--r-- | internal/port/gopher.go | 217 |
1 files changed, 217 insertions, 0 deletions
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 @@ | |||
1 | package port | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "fmt" | ||
6 | "html/template" | ||
7 | "io" | ||
8 | "log" | ||
9 | "net" | ||
10 | "net/http" | ||
11 | "net/url" | ||
12 | "strings" | ||
13 | |||
14 | "git.vulpes.one/Feuerfuchs/port/port/libgopher" | ||
15 | |||
16 | "github.com/davidbyttow/govips/pkg/vips" | ||
17 | "github.com/temoto/robotstxt" | ||
18 | ) | ||
19 | |||
20 | type Item struct { | ||
21 | Link template.URL | ||
22 | Type string | ||
23 | Text string | ||
24 | } | ||
25 | |||
26 | func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetList AssetList, uri string, hostport string, d libgopher.Directory) error { | ||
27 | var title string | ||
28 | |||
29 | out := make([]Item, len(d.Items)) | ||
30 | |||
31 | for i, x := range d.Items { | ||
32 | if x.Type == libgopher.INFO && x.Selector == "TITLE" { | ||
33 | title = x.Description | ||
34 | continue | ||
35 | } | ||
36 | |||
37 | tr := Item{ | ||
38 | Text: x.Description, | ||
39 | Type: x.Type.String(), | ||
40 | } | ||
41 | |||
42 | if x.Type == libgopher.INFO { | ||
43 | out[i] = tr | ||
44 | continue | ||
45 | } | ||
46 | |||
47 | if strings.HasPrefix(x.Selector, "URL:") || strings.HasPrefix(x.Selector, "/URL:") { | ||
48 | link := strings.TrimPrefix(strings.TrimPrefix(x.Selector, "/"), "URL:") | ||
49 | if strings.HasPrefix(link, "gemini://") { | ||
50 | link = fmt.Sprintf( | ||
51 | "/gemini/%s", | ||
52 | strings.TrimPrefix(link, "gemini://"), | ||
53 | ) | ||
54 | } else if strings.HasPrefix(link, "gopher://") { | ||
55 | link = fmt.Sprintf( | ||
56 | "/gopher/%s", | ||
57 | strings.TrimPrefix(link, "gopher://"), | ||
58 | ) | ||
59 | } | ||
60 | tr.Link = template.URL(link) | ||
61 | } else { | ||
62 | var linkHostport string | ||
63 | if x.Port != "70" { | ||
64 | linkHostport = net.JoinHostPort(x.Host, x.Port) | ||
65 | } else { | ||
66 | linkHostport = x.Host | ||
67 | } | ||
68 | |||
69 | path := url.PathEscape(x.Selector) | ||
70 | path = strings.Replace(path, "%2F", "/", -1) | ||
71 | tr.Link = template.URL( | ||
72 | fmt.Sprintf( | ||
73 | "/gopher/%s/%s%s", | ||
74 | linkHostport, | ||
75 | string(byte(x.Type)), | ||
76 | path, | ||
77 | ), | ||
78 | ) | ||
79 | } | ||
80 | |||
81 | out[i] = tr | ||
82 | } | ||
83 | |||
84 | if title == "" { | ||
85 | if uri != "" { | ||
86 | title = fmt.Sprintf("%s/%s", hostport, uri) | ||
87 | } else { | ||
88 | title = hostport | ||
89 | } | ||
90 | } | ||
91 | |||
92 | return tpl.Execute(w, TemplateVariables{ | ||
93 | Title: title, | ||
94 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
95 | Assets: assetList, | ||
96 | Lines: out, | ||
97 | Protocol: "gopher", | ||
98 | }) | ||
99 | } | ||
100 | |||
101 | // GopherHandler returns a Handler that proxies requests | ||
102 | // to the specified Gopher server as denoated by the first argument | ||
103 | // to the request path and renders the content using the provided template. | ||
104 | // The optional robots parameters points to a robotstxt.RobotsData struct | ||
105 | // to test user agents against a configurable robotst.txt file. | ||
106 | func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { | ||
107 | return func(w http.ResponseWriter, req *http.Request) { | ||
108 | agent := req.UserAgent() | ||
109 | path := strings.TrimPrefix(req.URL.Path, "/gopher/") | ||
110 | |||
111 | if robotsdata != nil && robotsdebug && !robotsdata.TestAgent(path, agent) { | ||
112 | log.Printf("UserAgent %s ignored robots.txt", agent) | ||
113 | } | ||
114 | |||
115 | parts := strings.Split(path, "/") | ||
116 | hostport := parts[0] | ||
117 | |||
118 | if len(hostport) == 0 { | ||
119 | http.Redirect(w, req, "/", http.StatusFound) | ||
120 | return | ||
121 | } | ||
122 | |||
123 | title := hostport | ||
124 | |||
125 | var qs string | ||
126 | |||
127 | if req.URL.RawQuery != "" { | ||
128 | qs = fmt.Sprintf("?%s", url.QueryEscape(req.URL.RawQuery)) | ||
129 | } | ||
130 | |||
131 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | ||
132 | if err != nil { | ||
133 | if e := tpl.Execute(w, TemplateVariables{ | ||
134 | Title: title, | ||
135 | URI: hostport, | ||
136 | Assets: assetList, | ||
137 | RawText: fmt.Sprintf("Error: %s", err), | ||
138 | Error: true, | ||
139 | Protocol: "gopher", | ||
140 | }); e != nil { | ||
141 | log.Println("Template error: " + e.Error()) | ||
142 | log.Println(err.Error()) | ||
143 | } | ||
144 | return | ||
145 | } | ||
146 | |||
147 | if uri != "" { | ||
148 | title = fmt.Sprintf("%s/%s", hostport, uri) | ||
149 | } | ||
150 | |||
151 | res, err := libgopher.Get( | ||
152 | fmt.Sprintf( | ||
153 | "gopher://%s/%s%s", | ||
154 | hostport, | ||
155 | uri, | ||
156 | qs, | ||
157 | ), | ||
158 | ) | ||
159 | |||
160 | if err != nil { | ||
161 | if e := tpl.Execute(w, TemplateVariables{ | ||
162 | Title: title, | ||
163 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
164 | Assets: assetList, | ||
165 | RawText: fmt.Sprintf("Error: %s", err), | ||
166 | Error: true, | ||
167 | Protocol: "gopher", | ||
168 | }); e != nil { | ||
169 | log.Println("Template error: " + e.Error()) | ||
170 | } | ||
171 | return | ||
172 | } | ||
173 | |||
174 | if res.Body != nil { | ||
175 | if len(parts) < 2 { | ||
176 | io.Copy(w, res.Body) | ||
177 | } else if strings.HasPrefix(parts[1], "0") && !strings.HasSuffix(uri, ".xml") && !strings.HasSuffix(uri, ".asc") { | ||
178 | buf := new(bytes.Buffer) | ||
179 | buf.ReadFrom(res.Body) | ||
180 | |||
181 | if err := tpl.Execute(w, TemplateVariables{ | ||
182 | Title: title, | ||
183 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
184 | Assets: assetList, | ||
185 | RawText: buf.String(), | ||
186 | Protocol: "gopher", | ||
187 | }); err != nil { | ||
188 | log.Println("Template error: " + err.Error()) | ||
189 | } | ||
190 | } else if strings.HasPrefix(parts[1], "T") { | ||
191 | _, _, err = vips.NewTransform(). | ||
192 | Load(res.Body). | ||
193 | ResizeStrategy(vips.ResizeStrategyAuto). | ||
194 | ResizeWidth(160). | ||
195 | Quality(75). | ||
196 | Output(w). | ||
197 | Apply() | ||
198 | } else { | ||
199 | io.Copy(w, res.Body) | ||
200 | } | ||
201 | } else { | ||
202 | if err := renderGopherDirectory(w, tpl, assetList, uri, hostport, res.Dir); err != nil { | ||
203 | if e := tpl.Execute(w, TemplateVariables{ | ||
204 | Title: title, | ||
205 | URI: fmt.Sprintf("%s/%s", hostport, uri), | ||
206 | Assets: assetList, | ||
207 | RawText: fmt.Sprintf("Error: %s", err), | ||
208 | Error: true, | ||
209 | Protocol: "gopher", | ||
210 | }); e != nil { | ||
211 | log.Println("Template error: " + e.Error()) | ||
212 | log.Println(e.Error()) | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | } | ||