aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdnan Maolood <[email protected]>2021-02-17 00:03:31 -0500
committerAdnan Maolood <[email protected]>2021-02-17 00:08:14 -0500
commitfa7ec1ac870a943fef9d0942fc654c58dfc829b4 (patch)
treef3a7431e6717b749aa94f558f7bd3f87ab9d5b9b
parentfs: Remove leading slash before opening files (diff)
downloadgo-gemini-fa7ec1ac870a943fef9d0942fc654c58dfc829b4.tar.xz
go-gemini-fa7ec1ac870a943fef9d0942fc654c58dfc829b4.zip
fs: Show listing for directories without index files
-rw-r--r--fs.go87
1 files changed, 57 insertions, 30 deletions
diff --git a/fs.go b/fs.go
index 2f931cf..c3e0443 100644
--- a/fs.go
+++ b/fs.go
@@ -1,10 +1,14 @@
package gemini
import (
+ "fmt"
"io"
"io/fs"
"mime"
+ "net/url"
"path"
+ "sort"
+ "strings"
)
func init() {
@@ -39,20 +43,6 @@ func (fs fileServer) ServeGemini(w ResponseWriter, r *Request) {
// the provided name is constructed from user input, it should be sanitized
// before calling ServeFile.
func ServeFile(w ResponseWriter, fsys fs.FS, name string) {
- f, err := openFile(fsys, name)
- if err != nil {
- w.Status(StatusNotFound)
- return
- }
- // Detect mimetype
- ext := path.Ext(name)
- mimetype := mime.TypeByExtension(ext)
- w.Meta(mimetype)
- // Copy file to response writer
- _, _ = io.Copy(w, f)
-}
-
-func openFile(fsys fs.FS, name string) (fs.File, error) {
if name == "/" {
name = "."
} else {
@@ -61,31 +51,68 @@ func openFile(fsys fs.FS, name string) (fs.File, error) {
f, err := fsys.Open(name)
if err != nil {
- return nil, err
+ w.Status(StatusNotFound)
+ return
}
+ defer f.Close()
stat, err := f.Stat()
if err != nil {
- return nil, err
- }
- if stat.Mode().IsRegular() {
- return f, nil
+ w.Status(StatusTemporaryFailure)
+ return
}
if stat.IsDir() {
- // Try opening index.gmi
- f, err := fsys.Open(path.Join(name, "index.gmi"))
- if err != nil {
- return nil, err
+ // Try opening index file
+ index, err := fsys.Open(path.Join(name, "index.gmi"))
+ if err == nil {
+ defer index.Close()
+ istat, err := index.Stat()
+ if err == nil {
+ f = index
+ stat = istat
+ }
}
- stat, err := f.Stat()
- if err != nil {
- return nil, err
+ }
+
+ if stat.IsDir() {
+ // Failed to find index file
+ dirList(w, f)
+ return
+ }
+
+ // Detect mimetype from file extension
+ ext := path.Ext(name)
+ mimetype := mime.TypeByExtension(ext)
+ w.Meta(mimetype)
+ io.Copy(w, f)
+}
+
+func dirList(w ResponseWriter, f fs.File) {
+ var entries []fs.DirEntry
+ var err error
+ d, ok := f.(fs.ReadDirFile)
+ if ok {
+ entries, err = d.ReadDir(-1)
+ }
+ if !ok || err != nil {
+ w.Header(StatusTemporaryFailure, "Error reading directory")
+ return
+ }
+
+ sort.Slice(entries, func(i, j int) bool {
+ return entries[i].Name() < entries[j].Name()
+ })
+
+ for _, entry := range entries {
+ name := entry.Name()
+ if entry.IsDir() {
+ name += "/"
}
- if stat.Mode().IsRegular() {
- return f, nil
+ link := LineLink{
+ Name: name,
+ URL: (&url.URL{Path: name}).EscapedPath(),
}
+ fmt.Fprintln(w, link.String())
}
-
- return nil, fs.ErrNotExist
}