goldberry/gopher.go

142 lines
3.5 KiB
Go

package main
import (
"fmt"
"io/ioutil"
"net"
"net/url"
"path"
"strings"
"time"
)
var GopherTimeout time.Duration = time.Duration(settings.TimeOutSeconds) * time.Second
func RetrieveGopher(u *url.URL) (string, error) {
conn, err := net.DialTimeout("tcp", u.Host, time.Duration(settings.TimeOutSeconds) * time.Second)
if err != nil {
return ConvertErrorToHTML(50, "Unable to connect to host"), nil
}
gophertype := '1'
send := "/"
if len(u.Path) > 2 {
send = u.Path[2:]
gophertype = rune(u.Path[1])
} else if len(u.Path) == 2 {
gophertype = rune(u.Path[1])
}
if u.RawQuery != "" {
send = send + "?" + u.RawQuery
}
_, err = conn.Write([]byte(send+"\n"))
if err != nil {
return ConvertErrorToHTML(50, "Unable to make request to host; connection closed"), nil
}
result, err := ioutil.ReadAll(conn)
if err != nil {
return ConvertErrorToHTML(50, "Unable to read response from host"), nil
}
return ParseGopherBody(string(result), u, gophertype)
}
func EditHTMLFile(html string) string {
bodyStart := strings.Index(html, "<body")
if bodyStart > -1 {
html = html[bodyStart:]
bodyStartEnd := strings.Index(html, ">")
if bodyStartEnd > -1 {
html = html[bodyStartEnd+1:]
}
}
bodyEnd := strings.Index(html, "</body>")
if bodyEnd > -1 {
html = html[:bodyEnd]
}
html = strings.ReplaceAll(html, "<script", `<span style="display: none;"`)
html = strings.ReplaceAll(html, "</script", "</span")
return html
}
func ParseGopherBody(body string, u *url.URL, gophertype rune) (string, error) {
switch gophertype {
case 'h':
// - Make robust enough to handle URL links
// - Open in system browser?
// - Remove all scripts and all head info/body tags
return EditHTMLFile(body), nil
case '0':
return strings.ReplaceAll(body, "\n", "<br>\n"), nil
case '1':
return ParseGopherMap(body), nil
case '7':
return RenderSearchForm("Search/query?", u)
case 'g':
return MakeImage(body, "image/gif"), nil
case 'I':
ft := strings.ToLower(path.Ext(u.Path))
mime := "image/jpeg"
switch ft {
case ".png":
mime = "image/png"
case ".tif", ".tiff":
mime = "image/tiff"
case ".bmp":
mime = "image/bmp"
case ".gif":
mime = "image/gif"
case ".ico":
mime = "image/vnd.microsoft.icon"
case ".webp":
mime = "image/webp"
}
return MakeImage(body, mime), nil
// Make this more robust/support png at least
default:
appCtrl.SaveFile(body)
return "", fmt.Errorf("File Saved")
}
return "", nil
}
func ParseGopherMap(gophermap string) string {
var out strings.Builder
linkFormat := "<a href=\"gopher://%s:%s/%c%s\" class=\"gopher type-%c\">%s</a><br>\n"
urlLinkFormat := "<a href=\"%s\" class=\"gopher type-W\">%s</a><br>\n"
splitMap := strings.Split(gophermap, "\n")
for _, ln := range splitMap {
ln = strings.TrimSpace(ln)
lnSplit := strings.Split(ln, "\t")
if len(lnSplit) < 4 {
continue
}
gophertype := rune(lnSplit[0][0])
msg := " "
if len(lnSplit[0]) > 1 {
msg = lnSplit[0][1:]
}
// Once query support is added to the client remove '7'
if gophertype == 't' || gophertype == '2' || gophertype == 'T' {
continue
}
if gophertype == 'i' {
out.WriteString(`<pre class="gopher">`)
out.WriteString(msg)
out.WriteString("</pre>\n")
} else {
if strings.HasPrefix(strings.ToUpper(lnSplit[1]), "URL:") && len(lnSplit[1]) > 4 {
out.WriteString(fmt.Sprintf(urlLinkFormat, lnSplit[1][4:], msg))
} else {
out.WriteString(fmt.Sprintf(linkFormat, lnSplit[2], lnSplit[3], gophertype, lnSplit[1], gophertype, msg))
}
}
}
return out.String()
}