2020-11-05 05:18:59 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-12-04 01:45:20 +01:00
|
|
|
"bytes"
|
2020-11-05 05:18:59 +01:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/h2non/filetype"
|
|
|
|
)
|
|
|
|
|
2020-12-04 01:45:20 +01:00
|
|
|
func buildDirList(request *url.URL, dirPath string) ([]byte, error) {
|
2020-11-05 05:18:59 +01:00
|
|
|
var (
|
2020-12-04 01:45:20 +01:00
|
|
|
b = &bytes.Buffer{}
|
2020-11-05 05:18:59 +01:00
|
|
|
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-12-04 01:45:20 +01:00
|
|
|
return nil, err
|
2020-11-05 05:18:59 +01: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-12-04 01:45:20 +01:00
|
|
|
fmt.Fprintf(b, "# %s%s", request.Path, newLine)
|
2020-11-05 19:00:32 +01:00
|
|
|
if numDirs == 1 {
|
2020-12-04 01:45:20 +01:00
|
|
|
b.Write([]byte("1 directory"))
|
2020-11-05 19:00:32 +01:00
|
|
|
} else {
|
2020-12-04 01:45:20 +01:00
|
|
|
fmt.Fprintf(b, "%d directories", numDirs)
|
2020-11-05 19:00:32 +01:00
|
|
|
}
|
2020-12-04 01:45:20 +01:00
|
|
|
b.Write([]byte(", "))
|
2020-11-05 19:00:32 +01:00
|
|
|
if numDirs == 1 {
|
2020-12-04 01:45:20 +01:00
|
|
|
b.Write([]byte("1 file"))
|
2020-11-05 19:00:32 +01:00
|
|
|
} else {
|
2020-12-04 01:45:20 +01:00
|
|
|
fmt.Fprintf(b, "%d files", numFiles)
|
2020-11-05 05:18:59 +01:00
|
|
|
}
|
2020-12-04 01:45:20 +01:00
|
|
|
b.Write([]byte(newLine + newLine))
|
2020-11-05 05:18:59 +01:00
|
|
|
|
|
|
|
if request.Path != "/" {
|
2020-12-04 01:45:20 +01:00
|
|
|
b.Write([]byte("=> ../ ../" + newLine + newLine))
|
2020-11-05 05:18:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, info := range files {
|
|
|
|
fileName := info.Name()
|
|
|
|
filePath := url.PathEscape(info.Name())
|
|
|
|
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
|
|
|
fileName += "/"
|
|
|
|
filePath += "/"
|
|
|
|
}
|
|
|
|
|
2020-12-04 01:45:20 +01:00
|
|
|
b.Write([]byte("=> " + fileName + " " + filePath + newLine))
|
2020-11-05 05:18:59 +01:00
|
|
|
|
|
|
|
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
2020-12-04 01:45:20 +01:00
|
|
|
b.Write([]byte(newLine))
|
2020-11-05 05:18:59 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
modified := "Never"
|
|
|
|
if !info.ModTime().IsZero() {
|
|
|
|
modified = info.ModTime().Format("2006-01-02 3:04 PM")
|
|
|
|
}
|
2020-12-04 01:45:20 +01:00
|
|
|
b.Write([]byte(modified + " - " + formatFileSize(info.Size()) + newLine + newLine))
|
2020-11-05 05:18:59 +01:00
|
|
|
}
|
2020-12-04 01:45:20 +01:00
|
|
|
|
|
|
|
return b.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath string) int {
|
|
|
|
dirList, err := buildDirList(request, dirPath)
|
|
|
|
if err != nil {
|
|
|
|
return writeStatus(c, statusTemporaryFailure)
|
|
|
|
}
|
|
|
|
|
|
|
|
writeSuccess(c, serve, geminiType, -1)
|
|
|
|
c.Write(dirList)
|
2020-11-12 18:56:59 +01:00
|
|
|
return statusSuccess
|
2020-11-05 05:18:59 +01:00
|
|
|
}
|
|
|
|
|
2020-11-10 05:10:53 +01:00
|
|
|
func serveFile(c net.Conn, serve *pathConfig, filePath string) {
|
2020-11-05 05:18:59 +01:00
|
|
|
// Open file
|
|
|
|
file, _ := os.Open(filePath)
|
|
|
|
defer file.Close()
|
|
|
|
|
2020-11-17 20:31:22 +01: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 05:10:53 +01:00
|
|
|
contentType = kind.MIME.Value
|
2020-11-17 20:31:22 +01:00
|
|
|
} else {
|
|
|
|
contentType = plainType
|
2020-11-05 05:18:59 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-17 20:31:22 +01:00
|
|
|
|
|
|
|
// Read file size
|
2020-11-10 05:10:53 +01:00
|
|
|
size := int64(-1)
|
|
|
|
info, err := file.Stat()
|
|
|
|
if err == nil {
|
|
|
|
size = info.Size()
|
2020-11-05 05:41:11 +01:00
|
|
|
}
|
2020-11-17 20:31:22 +01:00
|
|
|
|
|
|
|
// Write response header
|
2020-11-10 05:10:53 +01:00
|
|
|
writeSuccess(c, serve, contentType, size)
|
2020-11-05 05:18:59 +01:00
|
|
|
|
2020-11-17 20:31:22 +01:00
|
|
|
// Write file contents
|
|
|
|
if n > 0 {
|
|
|
|
c.Write(buf[:n])
|
|
|
|
}
|
2020-11-05 05:18:59 +01:00
|
|
|
io.Copy(c, file)
|
|
|
|
}
|