flounder

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

commit 99e089d9871ab2102787730f0eb5cfcfc54ba470
parent f0ec25f3b1de5cba69cf8ed647c9391c9b7ea81d
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sat,  5 Dec 2020 17:19:36 -0800

Cleanup and add file size limits

Diffstat:
Mconfig.go | 3++-
Mhttp.go | 35++++++++++++++++++++++++-----------
Mutils.go | 28++++++++++++++++++++++++++--
3 files changed, 52 insertions(+), 14 deletions(-)

diff --git a/config.go b/config.go @@ -18,7 +18,8 @@ type Config struct { GeminiCertStore string CookieStoreKey string OkExtensions []string - MaxFileSize int + MaxFileBytes int + MaxUserBytes int64 TLSCertFile string TLSKeyFile string } diff --git a/http.go b/http.go @@ -47,9 +47,15 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { // serve everything inside static directory if r.URL.Path != "/" { fileName := path.Join(c.TemplatesDirectory, "static", filepath.Clean(r.URL.Path)) - http.ServeFile(w, r, fileName) + _, err := os.Stat(fileName) + if err != nil { + renderDefaultError(w, http.StatusNotFound) + return + } + http.ServeFile(w, r, fileName) // TODO better error handling return } + authd, _, isAdmin := getAuthUser(r) indexFiles, err := getIndexFiles() if err != nil { @@ -80,16 +86,14 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { } func editFileHandler(w http.ResponseWriter, r *http.Request) { - session, _ := SessionStore.Get(r, "cookie-session") - authUser, ok := session.Values["auth_user"].(string) + ok, authUser, _ := getAuthUser(r) if !ok { renderDefaultError(w, http.StatusForbidden) return } fileName := filepath.Clean(r.URL.Path[len("/edit/"):]) - isText := strings.HasPrefix(mime.TypeByExtension(path.Ext(fileName)), "text") - if !isText { - renderError(w, "Bad Request: Not a text file, cannot be edited here", http.StatusBadRequest) // correct status code? + if !strings.HasPrefix(mime.TypeByExtension(path.Ext(fileName)), "text") { + renderError(w, "Bad Request: Not a text file, cannot be edited here", http.StatusBadRequest) return } filePath := path.Join(c.FilesDirectory, authUser, fileName) @@ -101,7 +105,7 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { renderError(w, err.Error(), http.StatusBadRequest) return } - // create directories if dne + // Create directories if dne f, err := os.OpenFile(filePath, os.O_RDONLY, 0644) var fileBytes []byte if os.IsNotExist(err) { @@ -141,7 +145,12 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { } // create directories if dne os.MkdirAll(path.Dir(filePath), os.ModePerm) - err = ioutil.WriteFile(filePath, fileBytes, 0644) + if userHasSpace(authUser, len(fileBytes)) { + err = ioutil.WriteFile(filePath, fileBytes, 0644) + } else { + renderError(w, fmt.Sprintf("Bad Request: Out of file space. Max space: %d.", c.MaxUserBytes), http.StatusBadRequest) + return + } if err != nil { log.Println(err) renderDefaultError(w, http.StatusInternalServerError) @@ -196,12 +205,16 @@ func uploadFilesHandler(w http.ResponseWriter, r *http.Request) { return } defer f.Close() - io.Copy(f, bytes.NewReader(dest)) + if userHasSpace(authUser, c.MaxFileBytes) { // Not quite right + io.Copy(f, bytes.NewReader(dest)) + } else { + renderError(w, fmt.Sprintf("Bad Request: Out of file space. Max space: %d.", c.MaxUserBytes), http.StatusBadRequest) + return + } } http.Redirect(w, r, "/my_site", http.StatusSeeOther) } -// bool whether auth'd, string is auth user func getAuthUser(r *http.Request) (bool, string, bool) { session, _ := SessionStore.Get(r, "cookie-session") user, ok := session.Values["auth_user"].(string) @@ -477,7 +490,7 @@ func userFile(w http.ResponseWriter, r *http.Request) { }{template.HTML(htmlString), favicon, userName + p} t.ExecuteTemplate(w, "user_page.html", data) } else { - http.ServeFile(w, r, fileName) // TODO make errors pretty + http.ServeFile(w, r, fileName) } } diff --git a/utils.go b/utils.go @@ -40,6 +40,29 @@ func timeago(t *time.Time) string { } } +func userHasSpace(user string, newBytes int) bool { + userPath := path.Join(c.FilesDirectory, user) + size, err := dirSize(userPath) + if err != nil || size+int64(newBytes) > c.MaxUserBytes { + return false + } + return true +} + +func dirSize(path string) (int64, error) { + var size int64 + err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + size += info.Size() + } + return err + }) + return size, err +} + /// Perform some checks to make sure the file is OK func checkIfValidFile(filename string, fileBytes []byte) error { if len(filename) == 0 { @@ -58,9 +81,10 @@ func checkIfValidFile(filename string, fileBytes []byte) error { if !found { return fmt.Errorf("Invalid file extension: %s", ext) } - if len(fileBytes) > c.MaxFileSize { - return fmt.Errorf("File too large. File was %d bytes, Max file size is %d", len(fileBytes), c.MaxFileSize) + if len(fileBytes) > c.MaxFileBytes { + return fmt.Errorf("File too large. File was %d bytes, Max file size is %d", len(fileBytes), c.MaxFileBytes) } + // return nil }