twins/serve_file.go

132 lines
2.7 KiB
Go
Raw Normal View History

2020-11-05 04:18:59 +00:00
package main
import (
"fmt"
"io"
"net"
"net/url"
"os"
"path/filepath"
"sort"
"github.com/h2non/filetype"
)
2020-11-12 17:56:59 +00:00
func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath string) int {
2020-11-05 04:18:59 +00:00
var (
files []os.FileInfo
numDirs int
numFiles int
)
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
} else if path == dirPath {
return nil
}
files = append(files, info)
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
numDirs++
} else {
numFiles++
}
if info.IsDir() {
return filepath.SkipDir
}
return nil
})
if err != nil {
2020-11-12 17:56:59 +00:00
return writeStatus(c, statusTemporaryFailure)
2020-11-05 04:18:59 +00:00
}
// List directories first
sort.Slice(files, func(i, j int) bool {
iDir := files[i].IsDir() || files[i].Mode()&os.ModeSymlink != 0
jDir := files[j].IsDir() || files[j].Mode()&os.ModeSymlink != 0
if iDir != jDir {
return iDir
}
return i < j
})
2020-11-10 04:10:53 +00:00
writeSuccess(c, serve, geminiType, -1)
2020-11-05 04:18:59 +00:00
2020-11-05 20:57:28 +00:00
fmt.Fprintf(c, "# %s%s", request.Path, newLine)
if numDirs == 1 {
c.Write([]byte("1 directory"))
} else {
fmt.Fprintf(c, "%d directories", numDirs)
}
c.Write([]byte(", "))
if numDirs == 1 {
c.Write([]byte("1 file"))
} else {
fmt.Fprintf(c, "%d files", numFiles)
2020-11-05 04:18:59 +00:00
}
2020-11-05 20:57:28 +00:00
c.Write([]byte(newLine + newLine))
2020-11-05 04:18:59 +00:00
if request.Path != "/" {
2020-11-05 20:57:28 +00:00
c.Write([]byte("=> ../ ../" + newLine + newLine))
2020-11-05 04:18:59 +00:00
}
for _, info := range files {
fileName := info.Name()
filePath := url.PathEscape(info.Name())
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
fileName += "/"
filePath += "/"
}
2020-11-05 20:57:28 +00:00
c.Write([]byte("=> " + fileName + " " + filePath + newLine))
2020-11-05 04:18:59 +00:00
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
2020-11-05 20:57:28 +00:00
c.Write([]byte(newLine))
2020-11-05 04:18:59 +00:00
continue
}
modified := "Never"
if !info.ModTime().IsZero() {
modified = info.ModTime().Format("2006-01-02 3:04 PM")
}
2020-11-05 20:57:28 +00:00
c.Write([]byte(modified + " - " + formatFileSize(info.Size()) + newLine + newLine))
2020-11-05 04:18:59 +00:00
}
2020-11-12 17:56:59 +00:00
return statusSuccess
2020-11-05 04:18:59 +00:00
}
2020-11-10 04:10:53 +00:00
func serveFile(c net.Conn, serve *pathConfig, filePath string) {
2020-11-05 04:18:59 +00:00
// Open file
file, _ := os.Open(filePath)
defer file.Close()
2020-11-17 19:31:22 +00:00
// Read content type
var (
buf = make([]byte, 261)
n int
)
contentType := config.Types[filepath.Ext(filePath)]
if contentType == "" {
n, _ = file.Read(buf)
kind, err := filetype.Match(buf[:n])
if err == nil && kind != filetype.Unknown && kind.MIME.Value != "" {
2020-11-10 04:10:53 +00:00
contentType = kind.MIME.Value
2020-11-17 19:31:22 +00:00
} else {
contentType = plainType
2020-11-05 04:18:59 +00:00
}
}
2020-11-17 19:31:22 +00:00
// Read file size
2020-11-10 04:10:53 +00:00
size := int64(-1)
info, err := file.Stat()
if err == nil {
size = info.Size()
}
2020-11-17 19:31:22 +00:00
// Write response header
2020-11-10 04:10:53 +00:00
writeSuccess(c, serve, contentType, size)
2020-11-05 04:18:59 +00:00
2020-11-17 19:31:22 +00:00
// Write file contents
if n > 0 {
c.Write(buf[:n])
}
2020-11-05 04:18:59 +00:00
io.Copy(c, file)
}