From 38ff3807b3b97da22006b5bdcf03fdfaaa4b0582 Mon Sep 17 00:00:00 2001 From: tjpcc Date: Thu, 7 Sep 2023 12:36:17 -0600 Subject: all the gopher CGI handlers to support gophernicus behaviors --- gopher/gophermap/extended.go | 47 +++++++++-- gopher/gophermap/extended_test.go | 6 +- gopher/gophermap/listdir.go | 91 ++++++++++------------ .../gophermap/testdata/customlist_output.gophermap | 4 +- gopher/gophermap/testdata/file4 | 0 gopher/gophermap/testdata/file4.txt | 0 gopher/gophermap/testdata/subdir/gophertag | 1 + gopher/gophermap/testdata/subdir2/gophermap | 1 + gopher/response.go | 2 +- 9 files changed, 91 insertions(+), 61 deletions(-) delete mode 100644 gopher/gophermap/testdata/file4 create mode 100644 gopher/gophermap/testdata/file4.txt create mode 100644 gopher/gophermap/testdata/subdir/gophertag create mode 100644 gopher/gophermap/testdata/subdir2/gophermap (limited to 'gopher') diff --git a/gopher/gophermap/extended.go b/gopher/gophermap/extended.go index d9fedd0..a0360fc 100644 --- a/gopher/gophermap/extended.go +++ b/gopher/gophermap/extended.go @@ -7,10 +7,12 @@ import ( "net/url" "os" "path/filepath" + "sort" "strconv" "strings" "tildegit.org/tjp/sliderule/gopher" + "tildegit.org/tjp/sliderule/internal" "tildegit.org/tjp/sliderule/internal/types" ) @@ -139,6 +141,8 @@ const ( UserListType types.Status = '~' // VHostListType generates a listing of virtual hosts. + // + // It is not supported in sliderule. VHostListType types.Status = '%' // InclusionType causes another gophermap to be included at this location. @@ -151,8 +155,16 @@ const ( EndDocType types.Status = '.' ) +type FileSystemSettings struct { + ParseExtended bool + Exec bool + ListUsers bool + DirMaps []string + DirTag string +} + // Compatible builds a standards-compliant gophermap from the current extended menu. -func (edoc ExtendedMapDocument) Compatible(cwd string) (gopher.MapDocument, string, error) { +func (edoc ExtendedMapDocument) Compatible(cwd string, settings FileSystemSettings) (gopher.MapDocument, string, error) { doc := gopher.MapDocument{} title := "" @@ -172,9 +184,32 @@ func (edoc ExtendedMapDocument) Compatible(cwd string) (gopher.MapDocument, stri return nil, "", InvalidLine(num) } extensions[from] = types.Status(to[0]) - case UserListType: //TODO - return nil, "", errors.New("User listings '~' are not supported") - case VHostListType: //TODO + case UserListType: + if !settings.ListUsers { + doc = append(doc, gopher.MapItem{ + Type: gopher.InfoMessageType, + Display: "~", + Selector: edoc.Location.Path, + Hostname: edoc.Location.Hostname(), + Port: edoc.Location.Port(), + }) + } + + users, err := internal.ListUsersWithHomeSubdir("public_gopher", 4) + if err != nil { + return nil, "", err + } + sort.Strings(users) + for _, user := range users { + doc = append(doc, gopher.MapItem{ + Type: gopher.MenuType, + Display: "~" + user, + Selector: "/~" + user, + Hostname: edoc.Location.Hostname(), + Port: edoc.Location.Port(), + }) + } + case VHostListType: return nil, "", errors.New("Virtual host listings '%' are not supported") case InclusionType: location := filepath.Join(cwd, item.Selector) @@ -183,13 +218,13 @@ func (edoc ExtendedMapDocument) Compatible(cwd string) (gopher.MapDocument, stri return nil, "", err } - lines, _, err := subEdoc.Compatible(filepath.Dir(location)) + lines, _, err := subEdoc.Compatible(filepath.Dir(location), settings) if err != nil { return nil, "", err } doc = append(doc, lines...) case DirListType: - dirlist, err := listDir(cwd, edoc.Location, hidden, extensions) + dirlist, err := listDir(cwd, edoc.Location, settings, hidden, extensions) if err != nil { return nil, "", err } diff --git a/gopher/gophermap/extended_test.go b/gopher/gophermap/extended_test.go index e956df1..2d9e9b1 100644 --- a/gopher/gophermap/extended_test.go +++ b/gopher/gophermap/extended_test.go @@ -31,7 +31,11 @@ func TestExtendedDoc(t *testing.T) { t.Fatal(err) } - doc, _, err := edoc.Compatible("testdata") + doc, _, err := edoc.Compatible("testdata", FileSystemSettings{ + ParseExtended: true, + DirMaps: []string{"gophermap"}, + DirTag: "gophertag", + }) if err != nil { t.Fatal(err) } diff --git a/gopher/gophermap/listdir.go b/gopher/gophermap/listdir.go index 8d66277..a2c5214 100644 --- a/gopher/gophermap/listdir.go +++ b/gopher/gophermap/listdir.go @@ -6,6 +6,7 @@ import ( "os" "path" "path/filepath" + "slices" "strings" "tildegit.org/tjp/sliderule/gopher" @@ -13,11 +14,11 @@ import ( ) // ListDir builds a gopher menu representing the contents of a directory. -func ListDir(dir string, location *url.URL) (gopher.MapDocument, error) { - return listDir(dir, location, nil, nil) +func ListDir(dir string, location *url.URL, settings FileSystemSettings) (gopher.MapDocument, error) { + return listDir(dir, location, settings, nil, nil) } -func listDir(dir string, location *url.URL, hidden map[string]struct{}, extensions map[string]types.Status) (gopher.MapDocument, error) { +func listDir(dir string, location *url.URL, settings FileSystemSettings, hidden map[string]struct{}, extensions map[string]types.Status) (gopher.MapDocument, error) { contents, err := os.ReadDir(dir) if err != nil { return nil, err @@ -28,55 +29,36 @@ func listDir(dir string, location *url.URL, hidden map[string]struct{}, extensio for _, entry := range contents { name := entry.Name() - if _, ok := hidden[name]; ok || name == "gophermap" { + inf, err := entry.Info() + if err != nil { + return nil, err + } + if inf.Mode()&4 == 0 { + continue + } + + if _, ok := hidden[name]; ok || slices.Contains(settings.DirMaps, name) { continue } var code types.Status - ext := strings.TrimPrefix(filepath.Ext(name), ".") if entry.IsDir() { code = gopher.MenuType - } else if c, ok := extensions[ext]; ok && ext != "" { - code = c } else { - switch ext { - case "gophermap": - code = gopher.MenuType - case "exe", "bin", "out": - code = gopher.BinaryFileType - case "gif": - code = gopher.GifFileType - case "jpg", "jpeg", "tif", "tiff": - code = gopher.ImageFileType - case "bmp": - code = gopher.BitmapType - case "mp4", "mov", "avi", "wmv", "webm": - code = gopher.MovieFileType - case "pcm", "aiff", "mp3", "aac", "ogg", "wma", "flac", "alac": - code = gopher.SoundFileType - case "doc", "docx", "odt", "fodt": - code = gopher.DocumentType - case "html": - code = gopher.HTMLType - case "png": - code = gopher.PngImageFileType - case "rtf": - code = gopher.RtfDocumentType - case "wav": - code = gopher.WavSoundFileType - case "pdf": - code = gopher.PdfDocumentType - case "xml", "atom": - code = gopher.XmlDocumentType - default: - code = gopher.TextFileType + ext := strings.TrimPrefix(filepath.Ext(name), ".") + if c, ok := extensions[ext]; ok { + code = c + } else if c, ok := extensions[name]; ok { + code = c + } else { + code = gopher.GuessItemType(name) } } doc = append(doc, gopher.MapItem{ Type: code, - Display: displayName(dir, entry), + Display: displayName(dir, entry, settings), Selector: path.Join(path.Dir(location.Path), name), Hostname: location.Hostname(), Port: location.Port(), @@ -86,32 +68,37 @@ func listDir(dir string, location *url.URL, hidden map[string]struct{}, extensio return doc, nil } -func displayName(dir string, entry os.DirEntry) string { +func displayName(dir string, entry os.DirEntry, settings FileSystemSettings) string { fname := entry.Name() + fullpath := filepath.Join(dir, fname) - // if is a gophermap, use !title or filename - if strings.HasSuffix(fname, ".gophermap") { - if title := gophermapTitle(dir, fname); title != "" { + if entry.Type().IsRegular() && settings.ParseExtended && (strings.HasSuffix(fname, ".gophermap") || slices.Contains(settings.DirMaps, fname)) { + if title := gophermapTitle(fullpath); title != "" { return title } - return fname } if entry.IsDir() { - if tag := tagTitle(dir, fname); tag != "" { - return tag + if settings.DirTag != "" { + if tag := tagTitle(filepath.Join(fullpath, settings.DirTag)); tag != "" { + return tag + } } - if title := gophermapTitle(dir, filepath.Join(fname, "gophermap")); title != "" { - return title + if settings.ParseExtended { + for _, mapname := range settings.DirMaps { + if title := gophermapTitle(filepath.Join(fullpath, mapname)); title != "" { + return title + } + } } } return fname } -func gophermapTitle(dir, name string) string { - file, err := os.Open(filepath.Join(dir, name)) +func gophermapTitle(path string) string { + file, err := os.Open(path) if err != nil { return "" } @@ -131,8 +118,8 @@ func gophermapTitle(dir, name string) string { return strings.TrimRight(line[1:], "\r\n") } -func tagTitle(parent, name string) string { - file, err := os.Open(filepath.Join(parent, name, "gophertag")) +func tagTitle(path string) string { + file, err := os.Open(path) if err != nil { return "" } diff --git a/gopher/gophermap/testdata/customlist_output.gophermap b/gopher/gophermap/testdata/customlist_output.gophermap index 1a96644..e5cc99d 100644 --- a/gopher/gophermap/testdata/customlist_output.gophermap +++ b/gopher/gophermap/testdata/customlist_output.gophermap @@ -8,5 +8,7 @@ i /customlist.gophermap localhost.localdomain 70 1This is the document title /customlist.gophermap localhost.localdomain 70 1customlist_output.gophermap /customlist_output.gophermap localhost.localdomain 70 9file3.executablething /file3.executablething localhost.localdomain 70 -0file4 /file4 localhost.localdomain 70 +0file4.txt /file4.txt localhost.localdomain 70 +1subdir title /subdir localhost.localdomain 70 +1subdir2 title /subdir2 localhost.localdomain 70 . diff --git a/gopher/gophermap/testdata/file4 b/gopher/gophermap/testdata/file4 deleted file mode 100644 index e69de29..0000000 diff --git a/gopher/gophermap/testdata/file4.txt b/gopher/gophermap/testdata/file4.txt new file mode 100644 index 0000000..e69de29 diff --git a/gopher/gophermap/testdata/subdir/gophertag b/gopher/gophermap/testdata/subdir/gophertag new file mode 100644 index 0000000..73a248e --- /dev/null +++ b/gopher/gophermap/testdata/subdir/gophertag @@ -0,0 +1 @@ +subdir title diff --git a/gopher/gophermap/testdata/subdir2/gophermap b/gopher/gophermap/testdata/subdir2/gophermap new file mode 100644 index 0000000..bba9538 --- /dev/null +++ b/gopher/gophermap/testdata/subdir2/gophermap @@ -0,0 +1 @@ +!subdir2 title diff --git a/gopher/response.go b/gopher/response.go index 1ad7f1d..46f9db0 100644 --- a/gopher/response.go +++ b/gopher/response.go @@ -121,7 +121,7 @@ func Error(err error) *MapItem { // File builds a minimal response delivering a file's contents. // -// Meta is nil and Status is 0 in this response. +// Meta is nil in this response. func File(status types.Status, contents io.Reader) *types.Response { return &types.Response{Status: status, Body: contents} } -- cgit v1.2.3