142 lines
3.5 KiB
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()
|
|
}
|
|
|