Adds buggy but present gemini support

This commit is contained in:
sloumdrone 2019-09-18 20:27:56 -07:00
parent 8a3ddad58e
commit 7e4a32c67a
3 changed files with 141 additions and 42 deletions

View File

@ -14,7 +14,7 @@ import (
"tildegit.org/sloum/bombadillo/cmdparse"
"tildegit.org/sloum/bombadillo/cui"
// "tildegit.org/sloum/bombadillo/gemini"
"tildegit.org/sloum/bombadillo/gemini"
"tildegit.org/sloum/bombadillo/gopher"
"tildegit.org/sloum/bombadillo/http"
"tildegit.org/sloum/bombadillo/telnet"
@ -715,9 +715,35 @@ func (c *client) Visit(url string) {
c.SetHeaderUrl()
c.Draw()
case "gemini":
// TODO send over to gemini request
c.SetMessage("Bombadillo has not mastered Gemini yet, check back soon", false)
c.DrawMessage()
capsule, err := gemini.Visit(u.Host, u.Port, u.Resource)
if err != nil {
c.SetMessage(err.Error(), true)
c.DrawMessage()
return
}
switch capsule.Status {
case 2:
pg := MakePage(u, capsule.Content, capsule.Links)
pg.WrapContent(c.Width)
c.PageState.Add(pg)
c.Scroll(0)
c.ClearMessage()
c.SetHeaderUrl()
c.Draw()
case 3:
c.SetMessage("[3] Redirect. Follow redirect? y or any other key for no", false)
c.DrawMessage()
ch := cui.Getch()
if ch == 'y' || ch == 'Y' {
c.Visit(capsule.Content)
} else {
c.SetMessage("Redirect aborted", false)
c.DrawMessage()
}
}
// c.SetMessage("Bombadillo has not mastered Gemini yet, check back soon", false)
// c.DrawMessage()
case "telnet":
c.SetMessage("Attempting to start telnet session", false)
c.DrawMessage()

View File

@ -3,81 +3,154 @@ package gemini
import (
"crypto/tls"
"fmt"
"net"
"io/ioutil"
// "strings"
"time"
"strconv"
"strings"
// "tildegit.org/sloum/mailcap"
)
type Capsule struct {
MimeMaj string
MimeMin string
Status int
Content string
Links []string
}
//------------------------------------------------\\
// + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\
func Retrieve(host, port, resource string) ([]byte, error) {
nullRes := make([]byte, 0)
timeOut := time.Duration(5) * time.Second
func Retrieve(host, port, resource string) (string, error) {
if host == "" || port == "" {
return nullRes, fmt.Errorf("Incomplete request url")
return "", fmt.Errorf("Incomplete request url")
}
addr := host + ":" + port
conf := &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: true,
}
conn, err := net.DialTimeout("tcp", addr, timeOut)
conn, err := tls.Dial("tcp", addr, conf)
if err != nil {
return nullRes, err
return "", err
}
secureConn := tls.Client(conn, conf)
defer conn.Close()
send := resource + "\n"
send := "gemini://" + addr + "/" + resource + "\r\n"
_, err = secureConn.Write([]byte(send))
_, err = conn.Write([]byte(send))
if err != nil {
return nullRes, err
return "", err
}
result, err := ioutil.ReadAll(conn)
if err != nil {
return nullRes, err
return "", err
}
return result, nil
return string(result), nil
}
func Visit(host, port, resource string) (string, []string, error) {
resp, err := Retrieve(host, port, resource)
func Visit(host, port, resource string) (Capsule, error) {
capsule := MakeCapsule()
rawResp, err := Retrieve(host, port, resource)
if err != nil {
return "", []string{}, err
return capsule, err
}
resp := strings.SplitN(rawResp, "\r\n", 2)
if len(resp) != 2 {
if err != nil {
return capsule, fmt.Errorf("Invalid response from server")
}
}
header := strings.SplitN(resp[0], " ", 2)
if len([]rune(header[0])) != 2 {
header = strings.SplitN(resp[0], "\t", 2)
if len([]rune(header[0])) != 2 {
return capsule, fmt.Errorf("Invalid response format from server")
}
}
body := resp[1]
// TODO break out the header
// header := ""
mime := ""
mimeMaj := mime
mimeMin := mime
// status := ""
content := string(resp)
if mimeMaj == "text" && mimeMin == "gemini" {
// text := string(resp)
// links := []string{}
// TODO parse geminimap from 'content'
} else if mimeMaj == "text" {
// TODO just return the text
} else {
// TODO use mailcap to try and open the file
// Get status code single digit form
capsule.Status, err = strconv.Atoi(string(header[0][0]))
if err != nil {
return capsule, fmt.Errorf("Invalid status response from server")
}
// Parse the meta as needed
var meta string
return content, []string{}, nil
switch capsule.Status {
case 1:
// handle search
return capsule, fmt.Errorf("Gemini input not yet supported")
case 2:
mimeAndCharset := strings.Split(header[1], ";")
meta = mimeAndCharset[0]
minMajMime := strings.Split(meta, "/")
if len(minMajMime) < 2 {
return capsule, fmt.Errorf("Improperly formatted mimetype received from server")
}
capsule.MimeMaj = minMajMime[0]
capsule.MimeMin = minMajMime[1]
if capsule.MimeMaj == "text" && capsule.MimeMin == "gemini" {
rootUrl := fmt.Sprintf("gemini://%s:%s", host, port)
capsule.Content, capsule.Links = parseGemini(body, rootUrl)
} else {
capsule.Content = body
}
return capsule, nil
case 3:
// The client will handle informing the user of a redirect
// and then request the new url
capsule.Content = header[1]
return capsule, nil
case 4:
return capsule, fmt.Errorf("[4] Temporary Failure. %s", header[1])
case 5:
return capsule, fmt.Errorf("[5] Permanent Failure. %s", header[1])
case 6:
return capsule, fmt.Errorf("[6] Client Certificate Required (Not supported by Bombadillo)")
default:
return capsule, fmt.Errorf("Invalid response status from server")
}
}
func parseGemini(b, rootUrl string) (string, []string) {
splitContent := strings.Split(b, "\n")
links := make([]string, 0, 10)
for i, ln := range splitContent {
splitContent[i] = strings.Trim(ln, "\r\n")
if len([]rune(ln)) > 3 && ln[:2] == "=>" {
trimmedSubLn := strings.Trim(ln[2:], "\r\n\t \a")
lineSplit := strings.SplitN(trimmedSubLn, " ", 2)
if len(lineSplit) != 2 {
lineSplit = append(lineSplit, lineSplit[0])
}
lineSplit[0] = strings.Trim(lineSplit[0], "\t\n\r \a")
lineSplit[1] = strings.Trim(lineSplit[1], "\t\n\r \a")
if len(lineSplit[0]) > 0 && lineSplit[0][0] == '/' {
lineSplit[0] = fmt.Sprintf("%s%s", rootUrl, lineSplit[0])
}
links = append(links, lineSplit[0])
linknum := fmt.Sprintf("[%d]", len(links))
splitContent[i] = fmt.Sprintf("%-5s %s", linknum, lineSplit[1])
}
}
return strings.Join(splitContent, "\n"), links
}
func MakeCapsule() Capsule {
return Capsule{"", "", 0, "", make([]string, 0, 5)}
}

2
url.go
View File

@ -97,8 +97,8 @@ func MakeUrl(u string) (Url, error) {
out.DownloadOnly = true
}
} else {
out.Resource = fmt.Sprintf("%s%s", out.Mime, out.Resource)
out.Mime = ""
out.Resource = fmt.Sprintf("%s%s", out.Mime, out.Resource)
}
if out.Scheme == "http" || out.Scheme == "https" {