From ac770231436f8a17d348a6a0ab934429df3c57d0 Mon Sep 17 00:00:00 2001 From: Feuerfuchs Date: Mon, 18 May 2020 16:12:43 +0200 Subject: WIP: Refactoring --- Makefile | 8 +- assets/startpage.txt | 6 +- internal/port/gemini.go | 176 ++++++++++++++------------------------- internal/port/gopher.go | 138 ++++++++++++++++++++++-------- internal/port/main.go | 69 ++++++++------- internal/port/tpl/_fonts.html | 16 ++++ internal/port/tpl/_header.html | 48 +++++++++++ internal/port/tpl/_modals.html | 24 ++++++ internal/port/tpl/gemini.html | 38 +++++++++ internal/port/tpl/gopher.html | 59 +++++++++++++ internal/port/tpl/startpage.html | 120 ++++---------------------- pkg/libgemini/libgemini.go | 85 +++++++++++-------- pkg/libgopher/libgopher.go | 2 +- 13 files changed, 458 insertions(+), 331 deletions(-) create mode 100644 internal/port/tpl/_fonts.html create mode 100644 internal/port/tpl/_header.html create mode 100644 internal/port/tpl/_modals.html diff --git a/Makefile b/Makefile index 8513d69..58ac412 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,10 @@ dev: build build: clean sassc -t compressed css/main.scss assets/style.css tsc --strict --module none --outFile /dev/stdout js/* | terser --compress --mangle -o assets/main.js -- - pyftsubset fonts/iosevka-term-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-term-ss03-regular.woff' - pyftsubset fonts/iosevka-term-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-term-ss03-regular.woff2' - pyftsubset fonts/iosevka-aile-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-aile-regular.woff' - pyftsubset fonts/iosevka-aile-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-aile-regular.woff2' + #pyftsubset fonts/iosevka-term-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-term-ss03-regular.woff' + #pyftsubset fonts/iosevka-term-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-term-ss03-regular.woff2' + #pyftsubset fonts/iosevka-aile-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-aile-regular.woff' + #pyftsubset fonts/iosevka-aile-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-aile-regular.woff2' go build -o ./port.bin ./cmd/port profile: diff --git a/assets/startpage.txt b/assets/startpage.txt index 46b22c1..3d1de44 100644 --- a/assets/startpage.txt +++ b/assets/startpage.txt @@ -1,6 +1,6 @@ - P R O X Y -- - - - - - for - - - - - - - G O P H E R + G E M I N I + P R O X Y + - - - - - - for - - - - - - + G O P H E R + G E M I N I GETTING STARTED -- diff --git a/internal/port/gemini.go b/internal/port/gemini.go index b10da7d..0d8292c 100644 --- a/internal/port/gemini.go +++ b/internal/port/gemini.go @@ -1,7 +1,6 @@ package port import ( - "bufio" "bytes" "fmt" "html/template" @@ -10,7 +9,6 @@ import ( "mime" "net/http" "net/url" - "regexp" "strings" "golang.org/x/net/html/charset" @@ -21,129 +19,83 @@ import ( "github.com/temoto/robotstxt" ) -type SectionType byte - -const ( - RAW_TEXT = SectionType(0) - REFLOW_TEXT = SectionType(1) - LINK = SectionType(2) -) - -type Section struct { - Type SectionType - Text string - URL template.URL -} - -type templateVariables struct { +type GeminiTemplateVariables struct { Title string - URI string + URL string Assets AssetList - Sections []Section + Sections []GeminiSection + Nav []GeminiNavItem } -var ( - TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m") -) +type GeminiNavItem struct { + Label string + URL string +} -func resolveURI(uri string, baseURL *url.URL) (resolvedURI string) { +type GeminiSection struct { + Type libgemini.GeminiDocSectionType + Text string + URL template.URL + Items []string +} + +func resolveURL(uri string, baseURL *url.URL) (resolvedURL string) { if strings.HasPrefix(uri, "//") { - resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "//") + resolvedURL = "/gemini/" + strings.TrimPrefix(uri, "//") } else if strings.HasPrefix(uri, "gemini://") { - resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "gemini://") + resolvedURL = "/gemini/" + strings.TrimPrefix(uri, "gemini://") } else if strings.HasPrefix(uri, "gopher://") { - resolvedURI = "/gopher/" + strings.TrimPrefix(uri, "gopher://") + resolvedURL = "/gopher/" + strings.TrimPrefix(uri, "gopher://") } else { url, err := url.Parse(uri) if err != nil { return "" } - adjustedURI := baseURL.ResolveReference(url) - path := adjustedURI.Path + adjustedURL := baseURL.ResolveReference(url) + path := adjustedURL.Path if !strings.HasPrefix(path, "/") { path = "/" + path } - if adjustedURI.Scheme == "gemini" { - resolvedURI = "/gemini/" + adjustedURI.Host + path - } else if adjustedURI.Scheme == "gopher" { - resolvedURI = "/gopher/" + adjustedURI.Host + path + if adjustedURL.Scheme == "gemini" { + resolvedURL = "/gemini/" + adjustedURL.Host + path + } else if adjustedURL.Scheme == "gopher" { + resolvedURL = "/gopher/" + adjustedURL.Host + path } else { - resolvedURI = adjustedURI.String() + resolvedURL = adjustedURL.String() } } return } -func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (sections []Section) { +func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (sections []GeminiSection) { baseURL, err := url.Parse(fmt.Sprintf( "gemini://%s/%s", hostport, uri, )) if err != nil { - return []Section{} - } - - skipSection := true - - section := Section{ - Type: RAW_TEXT, + return } - scanner := bufio.NewScanner(body) + unpreppedSections := libgemini.ParseGeminiDocument(body) - for scanner.Scan() { - line := strings.Trim(scanner.Text(), "\r\n") - line = TermEscapeSGRPattern.ReplaceAllString(line, "") - - linkMatch := libgemini.LinkPattern.FindStringSubmatch(line) - if len(linkMatch) != 0 && linkMatch[0] != "" { - curType := section.Type - - if !skipSection { - sections = append(sections, section) - } - - label := linkMatch[2] - if label == "" { - label = linkMatch[1] - } - - sections = append(sections, Section{ - Type: LINK, - Text: label, - URL: template.URL(resolveURI(linkMatch[1], baseURL)), + for _, section := range unpreppedSections { + if section.Type != libgemini.LINK { + sections = append(sections, GeminiSection{ + Type: section.Type, + Text: section.Text, + URL: template.URL(section.URL), + Items: section.Items, }) - - skipSection = false - section = Section{ - Type: curType, - } - } else { - reflowModeMatch := libgemini.ReflowModePattern.FindStringSubmatch(line) - if len(reflowModeMatch) != 0 { - newType := RAW_TEXT - if section.Type == RAW_TEXT { - newType = REFLOW_TEXT - } - - if !skipSection { - sections = append(sections, section) - } - - skipSection = false - section = Section{ - Type: newType, - } - } else { - section.Text = section.Text + "\n" + line - } } - } - if !skipSection { - sections = append(sections, section) + sections = append(sections, GeminiSection{ + Type: section.Type, + Text: section.Text, + URL: template.URL(resolveURL(section.URL, baseURL)), + Items: section.Items, + }) } return @@ -176,12 +128,12 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) if err != nil { - if e := tpl.Execute(w, templateVariables{ + if e := tpl.Execute(w, GeminiTemplateVariables{ Title: title, - URI: hostport, + URL: hostport, Assets: assetList, - Sections: []Section{{ - Type: RAW_TEXT, + Sections: []GeminiSection{{ + Type: libgemini.RAW_TEXT, Text: fmt.Sprintf("Error: %s", err), }}, }); e != nil { @@ -205,12 +157,12 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass ) if err != nil { - if e := tpl.Execute(w, templateVariables{ + if e := tpl.Execute(w, GeminiTemplateVariables{ Title: title, - URI: fmt.Sprintf("%s/%s", hostport, uri), + URL: fmt.Sprintf("%s/%s", hostport, uri), Assets: assetList, - Sections: []Section{{ - Type: RAW_TEXT, + Sections: []GeminiSection{{ + Type: libgemini.RAW_TEXT, Text: fmt.Sprintf("Error: %s", err), }}, }); e != nil { @@ -227,12 +179,12 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass uri, )) if err != nil { - if e := tpl.Execute(w, templateVariables{ + if e := tpl.Execute(w, GeminiTemplateVariables{ Title: title, - URI: fmt.Sprintf("%s/%s", hostport, uri), + URL: fmt.Sprintf("%s/%s", hostport, uri), Assets: assetList, - Sections: []Section{{ - Type: RAW_TEXT, + Sections: []GeminiSection{{ + Type: libgemini.RAW_TEXT, Text: fmt.Sprintf("Error: %s", err), }}, }); e != nil { @@ -242,17 +194,17 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass return } - http.Redirect(w, req, resolveURI(res.Header.Meta, baseURL), http.StatusFound) + http.Redirect(w, req, resolveURL(res.Header.Meta, baseURL), http.StatusFound) return } if int(res.Header.Status/10) != 2 { - if err := tpl.Execute(w, templateVariables{ + if err := tpl.Execute(w, GeminiTemplateVariables{ Title: title, - URI: fmt.Sprintf("%s/%s", hostport, uri), + URL: fmt.Sprintf("%s/%s", hostport, uri), Assets: assetList, - Sections: []Section{{ - Type: RAW_TEXT, + Sections: []GeminiSection{{ + Type: libgemini.RAW_TEXT, Text: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), }}, }); err != nil { @@ -277,20 +229,20 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass writer.Close() } - var sections []Section + var sections []GeminiSection if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { sections = parseGeminiDocument(buf, uri, hostport) } else { - sections = append(sections, Section{ - Type: RAW_TEXT, + sections = append(sections, GeminiSection{ + Type: libgemini.RAW_TEXT, Text: buf.String(), }) } - if err := tpl.Execute(w, templateVariables{ + if err := tpl.Execute(w, GeminiTemplateVariables{ Title: title, - URI: fmt.Sprintf("%s/%s", hostport, uri), + URL: fmt.Sprintf("%s/%s", hostport, uri), Assets: assetList, Sections: sections, }); err != nil { diff --git a/internal/port/gopher.go b/internal/port/gopher.go index abbc4d9..d2283c6 100644 --- a/internal/port/gopher.go +++ b/internal/port/gopher.go @@ -17,16 +17,73 @@ import ( "github.com/temoto/robotstxt" ) -type Item struct { +type gopherTemplateVariables struct { + Title string + URL string + Assets AssetList + Lines []GopherItem + Nav []GopherNavItem + IsPlain bool +} + +type GopherNavItem struct { + Label string + URL string + Current bool +} + +type GopherItem struct { Link template.URL Type string Text string } +func trimLeftChars(s string, n int) string { + m := 0 + for i := range s { + if m >= n { + return s[i:] + } + m++ + } + return s[:0] +} + +func urlToNav(url string) (items []GopherNavItem) { + partialURL := "/gopher" + parts := strings.Split(url, "/") + + for i, part := range parts { + if i == 1 { + partialURL = partialURL + "/1" + part = trimLeftChars(part, 1) + + if part == "" { + continue + } + } else { + partialURL = partialURL + "/" + part + } + + current := false + if i == len(parts)-1 || (len(parts) == 2 && i == 0) { + current = true + } + + items = append(items, GopherNavItem{ + Label: part, + URL: partialURL, + Current: current, + }) + } + + return +} + 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)) + out := make([]GopherItem, len(d.Items)) for i, x := range d.Items { if x.Type == libgopher.INFO && x.Selector == "TITLE" { @@ -34,7 +91,7 @@ func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetL continue } - tr := Item{ + tr := GopherItem{ Text: x.Description, Type: x.Type.String(), } @@ -89,12 +146,12 @@ func renderGopherDirectory(w http.ResponseWriter, tpl *template.Template, assetL } } - return tpl.Execute(w, TemplateVariables{ - Title: title, - URI: fmt.Sprintf("%s/%s", hostport, uri), - Assets: assetList, - Lines: out, - Protocol: "gopher", + return tpl.Execute(w, gopherTemplateVariables{ + Title: title, + URL: fmt.Sprintf("%s/%s", hostport, uri), + Assets: assetList, + Lines: out, + Nav: urlToNav(fmt.Sprintf("%s/%s", hostport, uri)), }) } @@ -130,13 +187,15 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass 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", + if e := tpl.Execute(w, gopherTemplateVariables{ + Title: title, + URL: hostport, + Assets: assetList, + Lines: []GopherItem{{ + Text: fmt.Sprintf("Error: %s", err), + }}, + Nav: urlToNav(hostport), + IsPlain: true, }); e != nil { log.Println("Template error: " + e.Error()) log.Println(err.Error()) @@ -158,13 +217,15 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass ) 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", + if e := tpl.Execute(w, gopherTemplateVariables{ + Title: title, + URL: fmt.Sprintf("%s/%s", hostport, uri), + Assets: assetList, + Lines: []GopherItem{{ + Text: fmt.Sprintf("Error: %s", err), + }}, + Nav: urlToNav(fmt.Sprintf("%s/%s", hostport, uri)), + IsPlain: true, }); e != nil { log.Println("Template error: " + e.Error()) } @@ -178,12 +239,15 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass 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", + if err := tpl.Execute(w, gopherTemplateVariables{ + Title: title, + URL: fmt.Sprintf("%s/%s", hostport, uri), + Assets: assetList, + Lines: []GopherItem{{ + Text: buf.String(), + }}, + Nav: urlToNav(fmt.Sprintf("%s/%s", hostport, uri)), + IsPlain: true, }); err != nil { log.Println("Template error: " + err.Error()) } @@ -200,13 +264,15 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass } } 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", + if e := tpl.Execute(w, gopherTemplateVariables{ + Title: title, + URL: fmt.Sprintf("%s/%s", hostport, uri), + Assets: assetList, + Lines: []GopherItem{{ + Text: fmt.Sprintf("Error: %s", err), + }}, + Nav: urlToNav(fmt.Sprintf("%s/%s", hostport, uri)), + IsPlain: false, }); e != nil { log.Println("Template error: " + e.Error()) log.Println(e.Error()) diff --git a/internal/port/main.go b/internal/port/main.go index 9fa245e..267df44 100644 --- a/internal/port/main.go +++ b/internal/port/main.go @@ -26,23 +26,19 @@ type AssetList struct { PropFontW2 string } -type TemplateVariables struct { - Title string - URI string - Assets AssetList - RawText string - Lines []Item - Error bool - Protocol string +type startTemplateVariables struct { + Title string + URL string + Assets AssetList + Content string } func DefaultHandler(tpl *template.Template, startpagetext string, assetList AssetList) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { - if err := tpl.Execute(w, TemplateVariables{ - Title: "Gopher/Gemini proxy", - Assets: assetList, - RawText: startpagetext, - Protocol: "startpage", + if err := tpl.Execute(w, startTemplateVariables{ + Title: "Gopher/Gemini proxy", + Assets: assetList, + Content: startpagetext, }); err != nil { log.Println("Template error: " + err.Error()) } @@ -118,7 +114,7 @@ func FontHandler(woff2 bool, fontdata []byte) http.HandlerFunc { // a robotstxt.RobotsData struct for testing user agents against // a configurable robots.txt file. func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug bool, vipsconcurrency int) error { - box := packr.New("assets", "../assets") + box := packr.New("assets", "../../assets") // // Robots @@ -205,24 +201,6 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b // // - var templates *template.Template - - var allFiles []string - files, err := ioutil.ReadDir("./tpl") - if err != nil { - fmt.Println(err) - } - for _, file := range files { - filename := file.Name() - if strings.HasSuffix(filename, ".html") { - allFiles = append(allFiles, "./tpl/"+filename) - } - } - - templates, err = template.ParseFiles(allFiles...) - - // - funcMap := template.FuncMap{ "safeHtml": func(s string) template.HTML { return template.HTML(s) @@ -266,9 +244,30 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b // - startpageTpl := templates.Lookup("startpage.html").Funcs(funcMap) - geminiTpl := templates.Lookup("gemini.html").Funcs(funcMap) - gopherTpl := templates.Lookup("gopher.html").Funcs(funcMap) + var templates *template.Template + + var allFiles []string + files, err := ioutil.ReadDir("./internal/port/tpl") + if err != nil { + return err + } + for _, file := range files { + filename := file.Name() + if strings.HasSuffix(filename, ".html") { + allFiles = append(allFiles, "./internal/port/tpl/"+filename) + } + } + + templates, err = template.New("main.html").Funcs(funcMap).ParseFiles(allFiles...) + if err != nil { + return err + } + + // + + startpageTpl := templates.Lookup("startpage.html") + geminiTpl := templates.Lookup("gemini.html") + gopherTpl := templates.Lookup("gopher.html") // // diff --git a/internal/port/tpl/_fonts.html b/internal/port/tpl/_fonts.html new file mode 100644 index 0000000..b56aa22 --- /dev/null +++ b/internal/port/tpl/_fonts.html @@ -0,0 +1,16 @@ + diff --git a/internal/port/tpl/_header.html b/internal/port/tpl/_header.html new file mode 100644 index 0000000..5bcd254 --- /dev/null +++ b/internal/port/tpl/_header.html @@ -0,0 +1,48 @@ +
+
+ {{ .Protocol }}://:// + + {{- if .URL -}} + {{- $page := . -}} + {{- $href := printf "/%s" .Protocol -}} + {{- $uriParts := split .URL "/" -}} + + {{- $uriLast := $uriParts | last -}} + {{- $uriParts = $uriParts | pop -}} + {{- if eq $uriLast "" -}} + {{- $uriLast = $uriParts | last -}} + {{- $uriParts = $uriParts | pop -}} + {{- end -}} + + {{- range $i, $part := $uriParts -}} + {{- if and (eq $page.Protocol "gopher") (eq $i 1) -}} + {{- $href = printf "%s/1" $href -}} + {{- $part = $part | trimLeftChar -}} + {{- if not (eq $part "") -}} + {{- $href = printf "%s/%s" $href $part -}} + /{{ $part }} + {{- end -}} + {{- else -}} + {{- $href = printf "%s/%s" $href . -}} + {{- if ne $i 0 -}} + / + {{- end -}} + {{ . }} + {{- end -}} + {{- end -}} + {{- if ne (len $uriParts) 0 -}} + / + {{- end -}} + {{- if and (eq $page.Protocol "gopher") (eq (len $uriParts) 1) -}} + {{- $uriLast = $uriLast | trimLeftChar -}} + {{- end -}} + {{ $uriLast }} + {{- end -}} +
+
+ {{- if and (not .Lines) (not .Error) (eq .Protocol "gopher") -}} + + {{- end -}} +
+
+
diff --git a/internal/port/tpl/_modals.html b/internal/port/tpl/_modals.html new file mode 100644 index 0000000..3c08d9a --- /dev/null +++ b/internal/port/tpl/_modals.html @@ -0,0 +1,24 @@ + diff --git a/internal/port/tpl/gemini.html b/internal/port/tpl/gemini.html index e69de29..08f1b8e 100644 --- a/internal/port/tpl/gemini.html +++ b/internal/port/tpl/gemini.html @@ -0,0 +1,38 @@ + + + + + + {{ .Title }} - Gemini proxy + + {{- template "_fonts.html" . -}} + + + {{- template "_header.html" . -}} + +
+
+				{{- if .Lines -}}
+					{{- $content := "" -}}
+					{{- range .Lines -}}
+						{{- if ne $content "" -}}
+							{{- $content = printf "%s\n" $content -}}
+						{{- end -}}
+						{{- if .Link -}}
+							{{- $content = printf "%s%s" $content (printf "%s  %s" .Type .Type .Link (.Text | HTMLEscape)) -}}
+						{{- else -}}
+							{{- $content = printf "%s%s" $content (printf "     %s" (.Text | HTMLEscape)) -}}
+						{{- end -}}
+					{{- end -}}
+					{{- $content | safeHtml -}}
+				{{- else -}}
+					{{- .RawText -}}
+				{{- end -}}
+			
+
+ + {{- template "_modals.html" . -}} + + + + diff --git a/internal/port/tpl/gopher.html b/internal/port/tpl/gopher.html index e69de29..c971847 100644 --- a/internal/port/tpl/gopher.html +++ b/internal/port/tpl/gopher.html @@ -0,0 +1,59 @@ + + + + + + {{ .Title }} - Gopher proxy + + {{- template "_fonts.html" . -}} + + +
+
+ gopher://:// + {{- range $i, $item := .Nav -}} + {{- if ne $i 0 -}} + / + {{- end -}} + {{- if .Current -}} + {{ .Label }} + {{- else -}} + {{ .Label }} + {{- end -}} + {{- end -}} +
+
+ {{- if .IsPlain -}} + + {{- end -}} +
+
+
+ +
+
+        {{- $content := "" -}}
+        {{- $page := . -}}
+        {{- range .Lines -}}
+          {{- if ne $content "" -}}
+            {{- $content = printf "%s\n" $content -}}
+          {{- end -}}
+          {{- if $page.IsPlain -}}
+            {{- $content = printf "%s%s" $content (.Text | HTMLEscape) -}}
+          {{- else -}}
+            {{- if .Link -}}
+              {{- $content = printf "%s%s" $content (printf "%s  %s" .Type .Type .Link (.Text | HTMLEscape)) -}}
+            {{- else -}}
+              {{- $content = printf "%s%s" $content (printf "     %s" (.Text | HTMLEscape)) -}}
+            {{- end -}}
+          {{- end -}}
+        {{- end -}}
+        {{- $content | safeHtml -}}
+			
+
+ + {{- template "_modals.html" . -}} + + + + diff --git a/internal/port/tpl/startpage.html b/internal/port/tpl/startpage.html index 8482a6f..cfe519d 100644 --- a/internal/port/tpl/startpage.html +++ b/internal/port/tpl/startpage.html @@ -3,118 +3,28 @@ - {{ .Title }}{{ if ne .Protocol "startpage" }} - {{ .Protocol | title }} proxy{{ end }} + {{ .Title }} - + {{- template "_fonts.html" . -}} - +
-
- {{ .Protocol }}://:// - - {{- if .URI -}} - {{- $page := . -}} - {{- $href := printf "/%s" .Protocol -}} - {{- $uriParts := split .URI "/" -}} - - {{- $uriLast := $uriParts | last -}} - {{- $uriParts = $uriParts | pop -}} - {{- if eq $uriLast "" -}} - {{- $uriLast = $uriParts | last -}} - {{- $uriParts = $uriParts | pop -}} - {{- end -}} + +
+
+
+
- {{- range $i, $part := $uriParts -}} - {{- if and (eq $page.Protocol "gopher") (eq $i 1) -}} - {{- $href = printf "%s/1" $href -}} - {{- $part = $part | trimLeftChar -}} - {{- if not (eq $part "") -}} - {{- $href = printf "%s/%s" $href $part -}} - /{{ $part }} - {{- end -}} - {{- else -}} - {{- $href = printf "%s/%s" $href . -}} - {{- if ne $i 0 -}} - / - {{- end -}} - {{ . }} - {{- end -}} - {{- end -}} - {{- if ne (len $uriParts) 0 -}} - / - {{- end -}} - {{- if and (eq $page.Protocol "gopher") (eq (len $uriParts) 1) -}} - {{- $uriLast = $uriLast | trimLeftChar -}} - {{- end -}} - {{ $uriLast }} - {{- end -}} - -
- {{- if and (not .Lines) (not .Error) (eq .Protocol "gopher") -}} - - {{- end -}} -
-
-
-
-				{{- if .Lines -}}
-					{{- $content := "" -}}
-					{{- range .Lines -}}
-						{{- if ne $content "" -}}
-							{{- $content = printf "%s\n" $content -}}
-						{{- end -}}
-						{{- if .Link -}}
-							{{- $content = printf "%s%s" $content (printf "%s  %s" .Type .Type .Link (.Text | HTMLEscape)) -}}
-						{{- else -}}
-							{{- $content = printf "%s%s" $content (printf "     %s" (.Text | HTMLEscape)) -}}
-						{{- end -}}
-					{{- end -}}
-					{{- $content | safeHtml -}}
-				{{- else -}}
-					{{- .RawText -}}
-				{{- end -}}
+			
+        {{- .Content -}}
 			
- + + {{- template "_modals.html" . -}} + diff --git a/pkg/libgemini/libgemini.go b/pkg/libgemini/libgemini.go index 71012ef..48a8ed0 100644 --- a/pkg/libgemini/libgemini.go +++ b/pkg/libgemini/libgemini.go @@ -6,7 +6,6 @@ import ( "crypto/tls" "errors" "fmt" - "html/template" "io" "mime" "net" @@ -74,19 +73,19 @@ type Response struct { type GeminiDocSectionType byte const ( - RAW_TEXT = SectionType(0) - REFLOW_TEXT = SectionType(1) - LINK = SectionType(2) - HEADING_1 = SectionType(3) - HEADING_2 = SectionType(4) - HEADING_3 = SectionType(5) - LIST = SectionType(6) + RAW_TEXT = GeminiDocSectionType(0) + REFLOW_TEXT = GeminiDocSectionType(1) + LINK = GeminiDocSectionType(2) + HEADING_1 = GeminiDocSectionType(3) + HEADING_2 = GeminiDocSectionType(4) + HEADING_3 = GeminiDocSectionType(5) + LIST = GeminiDocSectionType(6) ) type GeminiDocSection struct { - Type SectionType + Type GeminiDocSectionType Text string - URL template.URL + URL string Items []string } @@ -171,13 +170,13 @@ func ParseHeader(line string) (header *Header, err error) { return } -func ParseGeminiDocument(body *bytes.Buffer) (sections []Section) { +func ParseGeminiDocument(body *bytes.Buffer) (sections []GeminiDocSection) { scanner := bufio.NewScanner(body) reflow := true ignoreSection := true - section := Section{ - Type: REFLOW_TEXT + section := GeminiDocSection{ + Type: REFLOW_TEXT, } for scanner.Scan() { @@ -185,23 +184,23 @@ func ParseGeminiDocument(body *bytes.Buffer) (sections []Section) { line = TermEscapeSGRPattern.ReplaceAllString(line, "") reflowMatch := ReflowModePattern.FindStringSubmatch(line) - if len(heading3Match) != 0 { + if len(reflowMatch) != 0 && reflowMatch[0] != "" { reflow = !reflow continue } if !reflow { if !ignoreSection { - if section.Type != REFLOW_TEXT { + if section.Type != RAW_TEXT { sections = append(sections, section) - section = Section{ - Type: REFLOW_TEXT + section = GeminiDocSection{ + Type: RAW_TEXT, } } } else { ignoreSection = false - section = Section{ - Type: REFLOW_TEXT + section = GeminiDocSection{ + Type: RAW_TEXT, } } @@ -222,80 +221,96 @@ func ParseGeminiDocument(body *bytes.Buffer) (sections []Section) { } ignoreSection = false - section = Section{ + section = GeminiDocSection{ Type: LINK, Text: label, - URL: template.URL(resolveURI(linkMatch[1], baseURL)), + URL: linkMatch[1], } continue } heading3Match := Heading3Pattern.FindStringSubmatch(line) - if len(heading3Match) != 0 { + if len(heading3Match) != 0 && heading3Match[0] != "" { if !ignoreSection { sections = append(sections, section) } ignoreSection = false - section = Section{ + section = GeminiDocSection{ Type: HEADING_3, - Text: heading3Match[1] + Text: heading3Match[1], } continue } heading2Match := Heading2Pattern.FindStringSubmatch(line) - if len(heading2Match) != 0 { + if len(heading2Match) != 0 && heading2Match[0] != "" { if !ignoreSection { sections = append(sections, section) } ignoreSection = false - section = Section{ + section = GeminiDocSection{ Type: HEADING_2, - Text: heading2Match[1] + Text: heading2Match[1], } continue } heading1Match := Heading1Pattern.FindStringSubmatch(line) - if len(heading1Match) != 0 { + if len(heading1Match) != 0 && heading1Match[0] != "" { if !ignoreSection { sections = append(sections, section) } ignoreSection = false - section = Section{ + section = GeminiDocSection{ Type: HEADING_1, - Text: heading1Match[1] + Text: heading1Match[1], } continue } listItemMatch := ListItemPattern.FindStringSubmatch(line) - if len(listItemMatch) != 0 { + if len(listItemMatch) != 0 && listItemMatch[0] != "" { if !ignoreSection { if section.Type != LIST { sections = append(sections, section) - section = Section{ - Type: LIST + section = GeminiDocSection{ + Type: LIST, } } } else { ignoreSection = false - section = Section{ + section = GeminiDocSection{ Type: LIST, } } - section.Items = append(section.Items, listItemMatch[1]) + section.Items = append(section.Items, listItemMatch[1]) continue } + + if !ignoreSection { + if section.Type != REFLOW_TEXT { + sections = append(sections, section) + section = GeminiDocSection{ + Type: REFLOW_TEXT, + } + } + } else { + ignoreSection = false + section = GeminiDocSection{ + Type: REFLOW_TEXT, + } + } + + section.Text = section.Text + "\n" + line } if !ignoreSection { diff --git a/pkg/libgopher/libgopher.go b/pkg/libgopher/libgopher.go index 86d58ff..dcf13ee 100644 --- a/pkg/libgopher/libgopher.go +++ b/pkg/libgopher/libgopher.go @@ -184,7 +184,7 @@ type Response struct { Body io.Reader } -// Get fetches a Gopher resource by URI +// Get fetches a Gopher resource by URL func Get(uri string) (*Response, error) { u, err := url.Parse(uri) if err != nil { -- cgit v1.2.3-70-g09d2