]> Sergey Matveev's repositories - godlighty.git/blob - dirlist.go
ab631bb9a7d5b919b7f553678f1744929327ff0d
[godlighty.git] / dirlist.go
1 /*
2 godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server
3 Copyright (C) 2021-2023 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package godlighty
19
20 import (
21         _ "embed"
22         "html/template"
23         "io/fs"
24         "os"
25         "path"
26         "sort"
27         "time"
28
29         "github.com/dustin/go-humanize"
30 )
31
32 const MTimeFmt = "2006-01-02 15:04:05Z"
33
34 var (
35         //go:embed dirlist.tmpl
36         DirListTmplRaw string
37         DirListTmpl    = template.Must(template.New("dirlist").Parse(DirListTmplRaw))
38 )
39
40 type ByName []fs.DirEntry
41
42 func (a ByName) Len() int {
43         return len(a)
44 }
45
46 func (a ByName) Swap(i, j int) {
47         a[i], a[j] = a[j], a[i]
48 }
49
50 func (a ByName) Less(i, j int) bool {
51         return a[i].Name() < a[j].Name()
52 }
53
54 type DirListFile struct {
55         Idx     int
56         Name    string
57         Size    string
58         IsDir   bool
59         Type    string
60         ModTime string
61         Symlink string
62 }
63
64 func dirList(
65         cfg *HostCfg,
66         dir, pth string,
67         entries []os.DirEntry,
68         readme string,
69 ) (*os.File, error) {
70         sort.Sort(ByName(entries))
71         files := make([]DirListFile, 0, len(entries))
72         for i, entry := range entries {
73                 fi, err := entry.Info()
74                 if err != nil {
75                         return nil, err
76                 }
77                 file := DirListFile{
78                         Idx:     i,
79                         Name:    fi.Name(),
80                         Size:    humanize.IBytes(uint64(fi.Size())),
81                         ModTime: fi.ModTime().UTC().Truncate(time.Second).Format(MTimeFmt),
82                 }
83                 if fi.IsDir() {
84                         file.IsDir = true
85                 } else {
86                         file.Type = mediaType(file.Name, cfg.MIMEs)
87                 }
88                 if (entry.Type() & fs.ModeSymlink) > 0 {
89                         file.Symlink, _ = os.Readlink(path.Join(pth, fi.Name()))
90                 }
91                 files = append(files, file)
92         }
93         fd, err := os.CreateTemp("", "godlist-dirlist-")
94         if err != nil {
95                 return nil, err
96         }
97         os.Remove(fd.Name())
98         var root string
99         if dir == "/" {
100                 dir = ""
101                 root = "/"
102         }
103         err = DirListTmpl.Execute(fd, struct {
104                 Root   string
105                 Dir    string
106                 Files  []DirListFile
107                 Readme string
108         }{
109                 Root:   root,
110                 Dir:    dir,
111                 Files:  files,
112                 Readme: readme,
113         })
114         if err != nil {
115                 fd.Close()
116                 return nil, err
117         }
118         return fd, err
119 }