package controllers import ( "fmt" "io" "mime/multipart" "net/http" "unicode/utf8" "git.myrkvi.com/myrkvi/bin/global" "git.myrkvi.com/myrkvi/bin/models" "git.myrkvi.com/myrkvi/bin/utils" "git.myrkvi.com/myrkvi/bin/views/components" "git.myrkvi.com/myrkvi/bin/views/pages" "github.com/alecthomas/chroma/lexers" "github.com/labstack/echo/v4" ) func GetNewHandler(c echo.Context) error { values := c.QueryParams() wantsText := values.Has("text") return utils.RenderComponents(c, http.StatusOK, pages.NewFull(wantsText)) } func PostNewHandler(c echo.Context) error { file, err := c.FormFile("file") text := c.FormValue("text") name := c.FormValue("name") description := c.FormValue("description") lang := c.FormValue("lang") ip := c.RealIP() if tooMany, period := ipHasTooManyUses(ip); tooMany { return utils.RenderErrorToast(c, fmt.Sprintf("This IP address has uploaded too many files in the %s", period), ) } if (file == nil || err != nil) && text == "" { return utils.RenderErrorToast(c, "file or text must be provided") } code, adminCode, err := utils.GenerateCodes() if err != nil { return utils.RenderErrorToast(c, "server-side error occurred") } if file != nil { // Don't bother trying to convert the contents into a string if it is too large. if file.Size > 1024*1024 { return utils.RenderErrorToast(c, "file cannot be larger than 1 MiB") } text, err = getTextFromFile(file) if name == "" { name = file.Filename } if err != nil { return utils.RenderErrorToast(c, "server-side error occurred") } } if len([]byte(text)) > 1024*1024 { return utils.RenderErrorToast(c, "file cannot be larger than 1 MiB") } if !utf8.ValidString(text) { return utils.RenderErrorToast(c, "submitted content must be utf8") } // Determine language from file extension if not set. if lang == "" && name != "" { lexer := lexers.Match(name) if lexer != nil { lang = lexer.Config().Name } } createdFile, err := models.CreateNewBin(global.DB, text, name, description, lang, code, adminCode, ip) if err != nil { return utils.RenderErrorToast(c, "server-side error occurred") } binPartial := pages.BinPartial(createdFile) setTitle := components.SetTitle(name) utils.SetHeader(c, "HX-Push", fmt.Sprintf("/b/%s", code)) return utils.RenderComponents(c, http.StatusOK, binPartial, setTitle, ) } func getTextFromFile(fileHeader *multipart.FileHeader) (text string, err error) { f, err := fileHeader.Open() if err != nil { return } b, err := io.ReadAll(f) if err != nil { return } text = string(b) return } func ipHasTooManyUses(ip string) (tooMany bool, period string) { file := new(models.File) var dayCount int64 global.DB.Model(file). Where("ip = ? AND created_date >= DATETIME('now', '-1 day')", ip). Count(&dayCount) var threeDayCount int64 global.DB.Model(file). Where("ip = ? AND created_date >= DATETIME('now', '-3 days')", ip). Count(&threeDayCount) var weekCount int64 global.DB.Model(file). Where("ip = ? AND created_date >= DATETIME('now', '-7 days')", ip). Count(&weekCount) var monthCount int64 global.DB.Model(file). Where("ip = ? AND created_date >= DATETIME('now', '-1 month')", ip). Count(&monthCount) if dayCount > 20 { return true, "last day" } else if threeDayCount > 50 { return true, "last three days" } else if weekCount > 85 { return true, "last week" } else if monthCount > 300 { return true, "last month" } return false, "" }