diff --git a/display/file.go b/display/file.go new file mode 100644 index 0000000..51a660c --- /dev/null +++ b/display/file.go @@ -0,0 +1,116 @@ +package display + +import ( + "fmt" + "io/ioutil" + "mime" + "net/url" + "os" + "path/filepath" + "strings" + + "github.com/makeworld-the-better-one/amfora/renderer" + "github.com/makeworld-the-better-one/amfora/structs" + "github.com/spf13/viper" +) + +// handleFile handles urls using file:// protocol +func handleFile(u string) (*structs.Page, bool) { + page := &structs.Page{} + + uri, err := url.ParseRequestURI(u) + if err != nil { + Error("File Error", "Cannot parse URI: "+err.Error()) + return page, false + } + fi, err := os.Stat(uri.Path) + if err != nil { + Error("File Error", "Cannot open local file: "+err.Error()) + return page, false + } + + switch mode := fi.Mode(); { + case mode.IsDir(): + return createDirectoryListing(u) + case mode.IsRegular(): + if fi.Size() > viper.GetInt64("a-general.page_max_size") { + Error("File Error", "Cannot open local file, exceeds page max size") + return page, false + } + + mimetype := mime.TypeByExtension(filepath.Ext(uri.Path)) + if strings.HasSuffix(u, ".gmi") || strings.HasSuffix(u, ".gemini") { + mimetype = "text/gemini" + } + + if !strings.HasPrefix(mimetype, "text/") { + Error("File Error", "Cannot open file, not recognized as text.") + return page, false + } + + content, err := ioutil.ReadFile(uri.Path) + if err != nil { + Error("File Error", "Cannot open local file: "+err.Error()) + return page, false + } + + if mimetype == "text/gemini" { + rendered, links := renderer.RenderGemini(string(content), textWidth(), leftMargin(), false) + page = &structs.Page{ + Mediatype: structs.TextGemini, + URL: u, + Raw: string(content), + Content: rendered, + Links: links, + Width: termW, + } + } else { + page = &structs.Page{ + Mediatype: structs.TextPlain, + URL: u, + Raw: string(content), + Content: renderer.RenderPlainText(string(content), leftMargin()), + Links: []string{}, + Width: termW, + } + } + } + return page, true +} + +// createDirectoryListing creates a text/gemini page for a directory +// that lists all the files as links. +func createDirectoryListing(u string) (*structs.Page, bool) { + page := &structs.Page{} + + uri, err := url.ParseRequestURI(u) + if err != nil { + Error("Directory Error", "Cannot parse URI: "+err.Error()) + } + + files, err := ioutil.ReadDir(uri.Path) + if err != nil { + Error("Directory error", "Cannot open local directory: "+err.Error()) + return page, false + } + content := "Index of " + uri.Path + "\n" + content += "=> ../ ../\n" + for _, f := range files { + separator := "" + if f.IsDir() { + separator = "/" + } + content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator) + } + + rendered, links := renderer.RenderGemini(content, textWidth(), leftMargin(), false) + page = &structs.Page{ + Mediatype: structs.TextGemini, + URL: u, + Raw: content, + Content: rendered, + Links: links, + Width: termW, + } + return page, true +} diff --git a/display/private.go b/display/private.go index 8686fd7..67b54cf 100644 --- a/display/private.go +++ b/display/private.go @@ -78,7 +78,9 @@ func reformatPage(p *structs.Page) { case structs.TextGemini: // Links are not recorded because they won't change proxied := true - if strings.HasPrefix(p.URL, "gemini") || strings.HasPrefix(p.URL, "about") { + if strings.HasPrefix(p.URL, "gemini") || + strings.HasPrefix(p.URL, "about") || + strings.HasPrefix(p.URL, "file") { proxied = false } rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), leftMargin(), proxied) @@ -370,7 +372,16 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) { usingProxy = true } - if !strings.HasPrefix(u, "http") && !strings.HasPrefix(u, "gemini") { + if strings.HasPrefix(u, "file") { + page, ok := handleFile(u) + if !ok { + return ret("", false) + } + setPage(t, page) + return ret(u, true) + } + + if !strings.HasPrefix(u, "http") && !strings.HasPrefix(u, "gemini") && !strings.HasPrefix(u, "file") { // Not a Gemini URL if proxy == "" || proxy == "off" { // No proxy available