diff options
| -rw-r--r-- | gopherproxy.go | 114 | ||||
| -rw-r--r-- | libgemini.go | 2 | ||||
| -rw-r--r-- | template.go | 3 |
3 files changed, 76 insertions, 43 deletions
diff --git a/gopherproxy.go b/gopherproxy.go index 9a60507..ab8e906 100644 --- a/gopherproxy.go +++ b/gopherproxy.go | |||
| @@ -46,6 +46,31 @@ type AssetList struct { | |||
| 46 | PropFontW2 string | 46 | PropFontW2 string |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | func resolveURI(uri string, baseUrl *url.URL) (resolvedUri string) { | ||
| 50 | if strings.HasPrefix(uri, "//") { | ||
| 51 | resolvedUri = "/gemini/" + strings.TrimPrefix(uri, "//") | ||
| 52 | } else if strings.HasPrefix(uri, "gemini://") { | ||
| 53 | resolvedUri = "/gemini/" + strings.TrimPrefix(uri, "gemini://") | ||
| 54 | } else if strings.HasPrefix(uri, "gopher://") { | ||
| 55 | resolvedUri = "/gopher/" + strings.TrimPrefix(uri, "gopher://") | ||
| 56 | } else { | ||
| 57 | url, err := url.Parse(uri) | ||
| 58 | if err != nil { | ||
| 59 | return "" | ||
| 60 | } | ||
| 61 | adjustedUrl := baseUrl.ResolveReference(url) | ||
| 62 | if adjustedUrl.Scheme == "gemini" { | ||
| 63 | resolvedUri = "/gemini/" + adjustedUrl.Host + adjustedUrl.Path | ||
| 64 | } else if adjustedUrl.Scheme == "gopher" { | ||
| 65 | resolvedUri = "/gopher/" + adjustedUrl.Host + adjustedUrl.Path | ||
| 66 | } else { | ||
| 67 | resolvedUri = adjustedUrl.String() | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | return | ||
| 72 | } | ||
| 73 | |||
| 49 | func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetList AssetList, uri string, hostport string, d gopher.Directory) error { | 74 | func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetList AssetList, uri string, hostport string, d gopher.Directory) error { |
| 50 | var title string | 75 | var title string |
| 51 | 76 | ||
| @@ -109,9 +134,6 @@ func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetL | |||
| 109 | } | 134 | } |
| 110 | 135 | ||
| 111 | func parseGeminiDocument(response *GeminiResponse, uri string, hostport string) (items []Item) { | 136 | func parseGeminiDocument(response *GeminiResponse, uri string, hostport string) (items []Item) { |
| 112 | scanner := bufio.NewScanner(response.Body) | ||
| 113 | scanner.Split(bufio.ScanLines) | ||
| 114 | |||
| 115 | baseUrl, err := url.Parse(fmt.Sprintf( | 137 | baseUrl, err := url.Parse(fmt.Sprintf( |
| 116 | "gemini://%s/%s", | 138 | "gemini://%s/%s", |
| 117 | hostport, | 139 | hostport, |
| @@ -121,6 +143,9 @@ func parseGeminiDocument(response *GeminiResponse, uri string, hostport string) | |||
| 121 | return []Item{} | 143 | return []Item{} |
| 122 | } | 144 | } |
| 123 | 145 | ||
| 146 | scanner := bufio.NewScanner(response.Body) | ||
| 147 | scanner.Split(bufio.ScanLines) | ||
| 148 | |||
| 124 | for scanner.Scan() { | 149 | for scanner.Scan() { |
| 125 | line := strings.Trim(scanner.Text(), "\r\n") | 150 | line := strings.Trim(scanner.Text(), "\r\n") |
| 126 | 151 | ||
| @@ -131,34 +156,10 @@ func parseGeminiDocument(response *GeminiResponse, uri string, hostport string) | |||
| 131 | 156 | ||
| 132 | linkMatch := GeminiLinkPattern.FindStringSubmatch(line) | 157 | linkMatch := GeminiLinkPattern.FindStringSubmatch(line) |
| 133 | if len(linkMatch) != 0 && linkMatch[0] != "" { | 158 | if len(linkMatch) != 0 && linkMatch[0] != "" { |
| 134 | link := linkMatch[1] | ||
| 135 | |||
| 136 | if strings.HasPrefix(link, "//") { | ||
| 137 | link = "/gemini/" + strings.TrimPrefix(link, "//") | ||
| 138 | } else if strings.HasPrefix(link, "gemini://") { | ||
| 139 | link = "/gemini/" + strings.TrimPrefix(link, "gemini://") | ||
| 140 | } else if strings.HasPrefix(link, "gopher://") { | ||
| 141 | link = "/gopher/" + strings.TrimPrefix(link, "gopher://") | ||
| 142 | } else { | ||
| 143 | linkUrl, err := url.Parse(link) | ||
| 144 | if err != nil { | ||
| 145 | continue | ||
| 146 | } | ||
| 147 | adjustedUrl := baseUrl.ResolveReference(linkUrl) | ||
| 148 | if adjustedUrl.Scheme == "gemini" { | ||
| 149 | link = "/gemini/" + adjustedUrl.Host + adjustedUrl.Path | ||
| 150 | } else if adjustedUrl.Scheme == "gopher" { | ||
| 151 | link = "/gopher/" + adjustedUrl.Host + adjustedUrl.Path | ||
| 152 | } else { | ||
| 153 | link = adjustedUrl.String() | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | item.Type = ITEM_TYPE_GEMINI_LINK | 159 | item.Type = ITEM_TYPE_GEMINI_LINK |
| 158 | item.Link = template.URL(link) | 160 | item.Link = template.URL(resolveURI(linkMatch[1], baseUrl)) |
| 159 | if linkMatch[2] != "" { | 161 | item.Text = linkMatch[2] |
| 160 | item.Text = linkMatch[2] | 162 | if item.Text == "" { |
| 161 | } else { | ||
| 162 | item.Text = linkMatch[1] | 163 | item.Text = linkMatch[1] |
| 163 | } | 164 | } |
| 164 | } | 165 | } |
| @@ -342,6 +343,29 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 342 | return | 343 | return |
| 343 | } | 344 | } |
| 344 | 345 | ||
| 346 | if int(res.Header.Status/10) == 3 { | ||
| 347 | baseUrl, err := url.Parse(fmt.Sprintf( | ||
| 348 | "gemini://%s/%s", | ||
| 349 | hostport, | ||
| 350 | uri, | ||
| 351 | )) | ||
| 352 | if err != nil { | ||
| 353 | tpl.Execute(w, struct { | ||
| 354 | Title string | ||
| 355 | URI string | ||
| 356 | Assets AssetList | ||
| 357 | RawText string | ||
| 358 | Lines []Item | ||
| 359 | Error bool | ||
| 360 | Protocol string | ||
| 361 | }{uri, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gemini"}) | ||
| 362 | return | ||
| 363 | } | ||
| 364 | |||
| 365 | http.Redirect(w, req, resolveURI(res.Header.Meta, baseUrl), http.StatusFound) | ||
| 366 | return | ||
| 367 | } | ||
| 368 | |||
| 345 | if int(res.Header.Status/10) != 2 { | 369 | if int(res.Header.Status/10) != 2 { |
| 346 | tpl.Execute(w, struct { | 370 | tpl.Execute(w, struct { |
| 347 | Title string | 371 | Title string |
| @@ -355,18 +379,24 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 355 | return | 379 | return |
| 356 | } | 380 | } |
| 357 | 381 | ||
| 358 | if strings.HasPrefix(res.Header.Meta, MIME_GEMINI) { | 382 | if strings.HasPrefix(res.Header.Meta, "text/") { |
| 359 | items := parseGeminiDocument(res, uri, hostport) | 383 | // TODO: Handle encoding |
| 360 | tpl.Execute(w, struct { | 384 | // https://stackoverflow.com/questions/32518432/how-to-convert-from-an-encoding-to-utf-8-in-go |
| 361 | Title string | 385 | |
| 362 | URI string | 386 | if strings.HasPrefix(res.Header.Meta, MIME_GEMINI) { |
| 363 | Assets AssetList | 387 | items := parseGeminiDocument(res, uri, hostport) |
| 364 | RawText string | 388 | tpl.Execute(w, struct { |
| 365 | Lines []Item | 389 | Title string |
| 366 | Error bool | 390 | URI string |
| 367 | Protocol string | 391 | Assets AssetList |
| 368 | }{uri, fmt.Sprintf("%s/%s", hostport, uri), assetList, "", items, false, "gemini"}) | 392 | RawText string |
| 369 | } else if strings.HasPrefix(res.Header.Meta, "text/") { | 393 | Lines []Item |
| 394 | Error bool | ||
| 395 | Protocol string | ||
| 396 | }{uri, fmt.Sprintf("%s/%s", hostport, uri), assetList, "", items, false, "gemini"}) | ||
| 397 | return | ||
| 398 | } | ||
| 399 | |||
| 370 | buf := new(bytes.Buffer) | 400 | buf := new(bytes.Buffer) |
| 371 | buf.ReadFrom(res.Body) | 401 | buf.ReadFrom(res.Body) |
| 372 | tpl.Execute(w, struct { | 402 | tpl.Execute(w, struct { |
diff --git a/libgemini.go b/libgemini.go index 05321ef..56e1463 100644 --- a/libgemini.go +++ b/libgemini.go | |||
| @@ -49,7 +49,7 @@ var ( | |||
| 49 | HeaderPattern = regexp.MustCompile("^(\\d\\d)[ \\t]+(.*)$") | 49 | HeaderPattern = regexp.MustCompile("^(\\d\\d)[ \\t]+(.*)$") |
| 50 | MimeTypePattern = regexp.MustCompile("^[-\\w.]+/[-\\w.]+") | 50 | MimeTypePattern = regexp.MustCompile("^[-\\w.]+/[-\\w.]+") |
| 51 | MimeCharsetPattern = regexp.MustCompile("charset=([^ ;]+)") | 51 | MimeCharsetPattern = regexp.MustCompile("charset=([^ ;]+)") |
| 52 | GeminiLinkPattern = regexp.MustCompile("^=>[ \\t]*([^ ]+)(?:[ \\t]+(.*))?$") | 52 | GeminiLinkPattern = regexp.MustCompile("^=>[ \\t]*([^ \\t]+)(?:[ \\t]+(.*))?$") |
| 53 | ) | 53 | ) |
| 54 | 54 | ||
| 55 | type GeminiHeader struct { | 55 | type GeminiHeader struct { |
diff --git a/template.go b/template.go index b7d1b1f..6a9c6fe 100644 --- a/template.go +++ b/template.go | |||
| @@ -58,6 +58,9 @@ var tpltext = `<!doctype html> | |||
| 58 | {{- if ne (len $uriParts) 0 -}} | 58 | {{- if ne (len $uriParts) 0 -}} |
| 59 | <span class="location__slash">/</span> | 59 | <span class="location__slash">/</span> |
| 60 | {{- end -}} | 60 | {{- end -}} |
| 61 | {{- if and (eq $page.Protocol "gopher") (eq (len $uriParts) 1) -}} | ||
| 62 | {{- $uriLast = $uriLast | trimLeftChar -}} | ||
| 63 | {{- end -}} | ||
| 61 | <span class="location__uripart">{{ $uriLast }}</span> | 64 | <span class="location__uripart">{{ $uriLast }}</span> |
| 62 | </div> | 65 | </div> |
| 63 | <div class="actions"> | 66 | <div class="actions"> |
