diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/port/gemini.go | 176 | ||||
| -rw-r--r-- | internal/port/gopher.go | 2 | ||||
| -rw-r--r-- | internal/port/main.go | 2 |
3 files changed, 140 insertions, 40 deletions
diff --git a/internal/port/gemini.go b/internal/port/gemini.go index f9b0b97..b10da7d 100644 --- a/internal/port/gemini.go +++ b/internal/port/gemini.go | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | package port | 1 | package port |
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "bufio" | ||
| 4 | "bytes" | 5 | "bytes" |
| 5 | "fmt" | 6 | "fmt" |
| 6 | "html/template" | 7 | "html/template" |
| @@ -15,11 +16,32 @@ import ( | |||
| 15 | "golang.org/x/net/html/charset" | 16 | "golang.org/x/net/html/charset" |
| 16 | "golang.org/x/text/transform" | 17 | "golang.org/x/text/transform" |
| 17 | 18 | ||
| 18 | "git.vulpes.one/Feuerfuchs/port/port/libgemini" | 19 | "git.vulpes.one/Feuerfuchs/port/pkg/libgemini" |
| 19 | 20 | ||
| 20 | "github.com/temoto/robotstxt" | 21 | "github.com/temoto/robotstxt" |
| 21 | ) | 22 | ) |
| 22 | 23 | ||
| 24 | type SectionType byte | ||
| 25 | |||
| 26 | const ( | ||
| 27 | RAW_TEXT = SectionType(0) | ||
| 28 | REFLOW_TEXT = SectionType(1) | ||
| 29 | LINK = SectionType(2) | ||
| 30 | ) | ||
| 31 | |||
| 32 | type Section struct { | ||
| 33 | Type SectionType | ||
| 34 | Text string | ||
| 35 | URL template.URL | ||
| 36 | } | ||
| 37 | |||
| 38 | type templateVariables struct { | ||
| 39 | Title string | ||
| 40 | URI string | ||
| 41 | Assets AssetList | ||
| 42 | Sections []Section | ||
| 43 | } | ||
| 44 | |||
| 23 | var ( | 45 | var ( |
| 24 | TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m") | 46 | TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m") |
| 25 | ) | 47 | ) |
| @@ -53,6 +75,80 @@ func resolveURI(uri string, baseURL *url.URL) (resolvedURI string) { | |||
| 53 | return | 75 | return |
| 54 | } | 76 | } |
| 55 | 77 | ||
| 78 | func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (sections []Section) { | ||
| 79 | baseURL, err := url.Parse(fmt.Sprintf( | ||
| 80 | "gemini://%s/%s", | ||
| 81 | hostport, | ||
| 82 | uri, | ||
| 83 | )) | ||
| 84 | if err != nil { | ||
| 85 | return []Section{} | ||
| 86 | } | ||
| 87 | |||
| 88 | skipSection := true | ||
| 89 | |||
| 90 | section := Section{ | ||
| 91 | Type: RAW_TEXT, | ||
| 92 | } | ||
| 93 | |||
| 94 | scanner := bufio.NewScanner(body) | ||
| 95 | |||
| 96 | for scanner.Scan() { | ||
| 97 | line := strings.Trim(scanner.Text(), "\r\n") | ||
| 98 | line = TermEscapeSGRPattern.ReplaceAllString(line, "") | ||
| 99 | |||
| 100 | linkMatch := libgemini.LinkPattern.FindStringSubmatch(line) | ||
| 101 | if len(linkMatch) != 0 && linkMatch[0] != "" { | ||
| 102 | curType := section.Type | ||
| 103 | |||
| 104 | if !skipSection { | ||
| 105 | sections = append(sections, section) | ||
| 106 | } | ||
| 107 | |||
| 108 | label := linkMatch[2] | ||
| 109 | if label == "" { | ||
| 110 | label = linkMatch[1] | ||
| 111 | } | ||
| 112 | |||
| 113 | sections = append(sections, Section{ | ||
| 114 | Type: LINK, | ||
| 115 | Text: label, | ||
| 116 | URL: template.URL(resolveURI(linkMatch[1], baseURL)), | ||
| 117 | }) | ||
| 118 | |||
| 119 | skipSection = false | ||
| 120 | section = Section{ | ||
| 121 | Type: curType, | ||
| 122 | } | ||
| 123 | } else { | ||
| 124 | reflowModeMatch := libgemini.ReflowModePattern.FindStringSubmatch(line) | ||
| 125 | if len(reflowModeMatch) != 0 { | ||
| 126 | newType := RAW_TEXT | ||
| 127 | if section.Type == RAW_TEXT { | ||
| 128 | newType = REFLOW_TEXT | ||
| 129 | } | ||
| 130 | |||
| 131 | if !skipSection { | ||
| 132 | sections = append(sections, section) | ||
| 133 | } | ||
| 134 | |||
| 135 | skipSection = false | ||
| 136 | section = Section{ | ||
| 137 | Type: newType, | ||
| 138 | } | ||
| 139 | } else { | ||
| 140 | section.Text = section.Text + "\n" + line | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | if !skipSection { | ||
| 146 | sections = append(sections, section) | ||
| 147 | } | ||
| 148 | |||
| 149 | return | ||
| 150 | } | ||
| 151 | |||
| 56 | func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { | 152 | func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { |
| 57 | return func(w http.ResponseWriter, req *http.Request) { | 153 | return func(w http.ResponseWriter, req *http.Request) { |
| 58 | agent := req.UserAgent() | 154 | agent := req.UserAgent() |
| @@ -80,13 +176,14 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 80 | 176 | ||
| 81 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | 177 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) |
| 82 | if err != nil { | 178 | if err != nil { |
| 83 | if e := tpl.Execute(w, TemplateVariables{ | 179 | if e := tpl.Execute(w, templateVariables{ |
| 84 | Title: title, | 180 | Title: title, |
| 85 | URI: hostport, | 181 | URI: hostport, |
| 86 | Assets: assetList, | 182 | Assets: assetList, |
| 87 | RawText: fmt.Sprintf("Error: %s", err), | 183 | Sections: []Section{{ |
| 88 | Error: true, | 184 | Type: RAW_TEXT, |
| 89 | Protocol: "gemini", | 185 | Text: fmt.Sprintf("Error: %s", err), |
| 186 | }}, | ||
| 90 | }); e != nil { | 187 | }); e != nil { |
| 91 | log.Println("Template error: " + e.Error()) | 188 | log.Println("Template error: " + e.Error()) |
| 92 | log.Println(err.Error()) | 189 | log.Println(err.Error()) |
| @@ -108,13 +205,14 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 108 | ) | 205 | ) |
| 109 | 206 | ||
| 110 | if err != nil { | 207 | if err != nil { |
| 111 | if e := tpl.Execute(w, TemplateVariables{ | 208 | if e := tpl.Execute(w, templateVariables{ |
| 112 | Title: title, | 209 | Title: title, |
| 113 | URI: fmt.Sprintf("%s/%s", hostport, uri), | 210 | URI: fmt.Sprintf("%s/%s", hostport, uri), |
| 114 | Assets: assetList, | 211 | Assets: assetList, |
| 115 | RawText: fmt.Sprintf("Error: %s", err), | 212 | Sections: []Section{{ |
| 116 | Error: true, | 213 | Type: RAW_TEXT, |
| 117 | Protocol: "gemini", | 214 | Text: fmt.Sprintf("Error: %s", err), |
| 215 | }}, | ||
| 118 | }); e != nil { | 216 | }); e != nil { |
| 119 | log.Println("Template error: " + e.Error()) | 217 | log.Println("Template error: " + e.Error()) |
| 120 | log.Println(err.Error()) | 218 | log.Println(err.Error()) |
| @@ -129,13 +227,14 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 129 | uri, | 227 | uri, |
| 130 | )) | 228 | )) |
| 131 | if err != nil { | 229 | if err != nil { |
| 132 | if e := tpl.Execute(w, TemplateVariables{ | 230 | if e := tpl.Execute(w, templateVariables{ |
| 133 | Title: title, | 231 | Title: title, |
| 134 | URI: fmt.Sprintf("%s/%s", hostport, uri), | 232 | URI: fmt.Sprintf("%s/%s", hostport, uri), |
| 135 | Assets: assetList, | 233 | Assets: assetList, |
| 136 | RawText: fmt.Sprintf("Error: %s", err), | 234 | Sections: []Section{{ |
| 137 | Error: true, | 235 | Type: RAW_TEXT, |
| 138 | Protocol: "gemini", | 236 | Text: fmt.Sprintf("Error: %s", err), |
| 237 | }}, | ||
| 139 | }); e != nil { | 238 | }); e != nil { |
| 140 | log.Println("Template error: " + e.Error()) | 239 | log.Println("Template error: " + e.Error()) |
| 141 | log.Println(err.Error()) | 240 | log.Println(err.Error()) |
| @@ -148,13 +247,14 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 148 | } | 247 | } |
| 149 | 248 | ||
| 150 | if int(res.Header.Status/10) != 2 { | 249 | if int(res.Header.Status/10) != 2 { |
| 151 | if err := tpl.Execute(w, TemplateVariables{ | 250 | if err := tpl.Execute(w, templateVariables{ |
| 152 | Title: title, | 251 | Title: title, |
| 153 | URI: fmt.Sprintf("%s/%s", hostport, uri), | 252 | URI: fmt.Sprintf("%s/%s", hostport, uri), |
| 154 | Assets: assetList, | 253 | Assets: assetList, |
| 155 | RawText: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), | 254 | Sections: []Section{{ |
| 156 | Error: true, | 255 | Type: RAW_TEXT, |
| 157 | Protocol: "gemini", | 256 | Text: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), |
| 257 | }}, | ||
| 158 | }); err != nil { | 258 | }); err != nil { |
| 159 | log.Println("Template error: " + err.Error()) | 259 | log.Println("Template error: " + err.Error()) |
| 160 | } | 260 | } |
| @@ -177,24 +277,22 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 177 | writer.Close() | 277 | writer.Close() |
| 178 | } | 278 | } |
| 179 | 279 | ||
| 180 | var ( | 280 | var sections []Section |
| 181 | rawText string | ||
| 182 | items []Item | ||
| 183 | ) | ||
| 184 | 281 | ||
| 185 | if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { | 282 | if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { |
| 186 | items = parseGeminiDocument(buf, uri, hostport) | 283 | sections = parseGeminiDocument(buf, uri, hostport) |
| 187 | } else { | 284 | } else { |
| 188 | rawText = buf.String() | 285 | sections = append(sections, Section{ |
| 286 | Type: RAW_TEXT, | ||
| 287 | Text: buf.String(), | ||
| 288 | }) | ||
| 189 | } | 289 | } |
| 190 | 290 | ||
| 191 | if err := tpl.Execute(w, TemplateVariables{ | 291 | if err := tpl.Execute(w, templateVariables{ |
| 192 | Title: title, | 292 | Title: title, |
| 193 | URI: fmt.Sprintf("%s/%s", hostport, uri), | 293 | URI: fmt.Sprintf("%s/%s", hostport, uri), |
| 194 | Assets: assetList, | 294 | Assets: assetList, |
| 195 | Lines: items, | 295 | Sections: sections, |
| 196 | RawText: rawText, | ||
| 197 | Protocol: "gemini", | ||
| 198 | }); err != nil { | 296 | }); err != nil { |
| 199 | log.Println("Template error: " + err.Error()) | 297 | log.Println("Template error: " + err.Error()) |
| 200 | } | 298 | } |
diff --git a/internal/port/gopher.go b/internal/port/gopher.go index ebeb213..abbc4d9 100644 --- a/internal/port/gopher.go +++ b/internal/port/gopher.go | |||
| @@ -11,7 +11,7 @@ import ( | |||
| 11 | "net/url" | 11 | "net/url" |
| 12 | "strings" | 12 | "strings" |
| 13 | 13 | ||
| 14 | "git.vulpes.one/Feuerfuchs/port/port/libgopher" | 14 | "git.vulpes.one/Feuerfuchs/port/pkg/libgopher" |
| 15 | 15 | ||
| 16 | "github.com/davidbyttow/govips/pkg/vips" | 16 | "github.com/davidbyttow/govips/pkg/vips" |
| 17 | "github.com/temoto/robotstxt" | 17 | "github.com/temoto/robotstxt" |
diff --git a/internal/port/main.go b/internal/port/main.go index 5cdd794..9fa245e 100644 --- a/internal/port/main.go +++ b/internal/port/main.go | |||
| @@ -205,6 +205,8 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b | |||
| 205 | // | 205 | // |
| 206 | // | 206 | // |
| 207 | 207 | ||
| 208 | var templates *template.Template | ||
| 209 | |||
| 208 | var allFiles []string | 210 | var allFiles []string |
| 209 | files, err := ioutil.ReadDir("./tpl") | 211 | files, err := ioutil.ReadDir("./tpl") |
| 210 | if err != nil { | 212 | if err != nil { |
