flounder

A simple gemini site builder
Log | Files | Refs | README | LICENSE

commit 0fdda65d16572274c9045ea127794d9be2ccf66b
parent de84128d6a9cc08765098317fba37afad12177ef
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sun, 27 Dec 2020 21:51:19 -0800

Improve istext detection

Now reads bytes in addition to mimetype -- allows xml, etc files to be
edited

Diffstat:
Mhttp.go | 3+--
Mmain.go | 3+--
Mutils.go | 36++++++++++++++++++++++++++++++++++++
3 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/http.go b/http.go @@ -13,7 +13,6 @@ import ( "io" "io/ioutil" "log" - "mime" "net/http" "os" "path" @@ -104,8 +103,8 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { return } fileName := filepath.Clean(r.URL.Path[len("/edit/"):]) - isText := strings.HasPrefix(mime.TypeByExtension(path.Ext(fileName)), "text") filePath := path.Join(c.FilesDirectory, user.Username, fileName) + isText := isTextFile(filePath) if r.Method == "GET" { err := checkIfValidFile(filePath, nil) diff --git a/main.go b/main.go @@ -10,7 +10,6 @@ import ( "io/ioutil" "log" mathrand "math/rand" - "mime" "os" "path" "path/filepath" @@ -37,7 +36,7 @@ type File struct { // also folders func fileFromPath(fullPath string) File { info, _ := os.Stat(fullPath) creatorFolder := getCreator(fullPath) - isText := strings.HasPrefix(mime.TypeByExtension(path.Ext(fullPath)), "text") // Not perfect + isText := isTextFile(fullPath) updatedTime := info.ModTime() return File{ Name: getLocalPath(fullPath), diff --git a/utils.go b/utils.go @@ -4,13 +4,49 @@ import ( "archive/zip" "fmt" "io" + "mime" "os" "path" "path/filepath" "strings" "time" + "unicode/utf8" ) +// Check if it is a text file, first by checking mimetype, then by reading bytes +// Stolen from https://github.com/golang/tools/blob/master/godoc/util/util.go +func isTextFile(fullPath string) bool { + isText := strings.HasPrefix(mime.TypeByExtension(path.Ext(fullPath)), "text") + if isText { + return true + } + const max = 1024 // at least utf8.UTFMax + s := make([]byte, 1024) + f, err := os.Open(fullPath) + if os.IsNotExist(err) { + return true // for the purposes of editing, we return true + } + n, err := f.Read(s) + s = s[0:n] + if err != nil { + return false + } + f.Close() + + for i, c := range string(s) { + if i+utf8.UTFMax > len(s) { + // last char may be incomplete - ignore + break + } + if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' { + fmt.Println("a") + // decoding error or control character - not a text file + return false + } + } + return true +} + func isGemini(filename string) bool { extension := path.Ext(filename) return extension == ".gmi" || extension == ".gemini"