diff --git a/handler/app/internal/remote/remote.go b/handler/app/internal/remote/remote.go index 8136c054..1390e214 100644 --- a/handler/app/internal/remote/remote.go +++ b/handler/app/internal/remote/remote.go @@ -96,9 +96,9 @@ func (got *DemozooLink) Download(c echo.Context, db *sql.DB, downloadDir string) got.Pouet = prod.PouetProd() got.YouTube = prod.YouTubeVideo() y, m, d := prod.Released() - got.IssuedYear = int16(y) - got.IssuedMonth = int16(m) - got.IssuedDay = int16(d) + got.IssuedYear = y + got.IssuedMonth = m + got.IssuedDay = d r1, r2 := prod.Groups() got.Releaser1 = r1 got.Releaser2 = r2 @@ -290,9 +290,9 @@ func (got *PouetLink) Download(c echo.Context, db *sql.DB, downloadDir string) e got.Demozoo = i } y, m, d := prod.Released() - got.IssuedYear = int16(y) - got.IssuedMonth = int16(m) - got.IssuedDay = int16(d) + got.IssuedYear = y + got.IssuedMonth = m + got.IssuedDay = d r1, r2 := prod.Releasers() got.Releaser1 = r1 got.Releaser2 = r2 diff --git a/handler/htmx/transfer.go b/handler/htmx/transfer.go index 0dbc0845..0cecf25e 100644 --- a/handler/htmx/transfer.go +++ b/handler/htmx/transfer.go @@ -11,6 +11,7 @@ import ( "fmt" "html" "io" + "math" "mime/multipart" "net/http" "os" @@ -405,7 +406,7 @@ func (prod Submission) Submit( //nolint:cyclop,funlen return c.String(http.StatusInternalServerError, "error, "+prod.String()+" submit logger is nil") } - id, err := sanitizeID(c, name, prod.String()) + id, idInt32, err := sanitizeID(c, name, prod.String()) if err != nil { return err } @@ -454,13 +455,13 @@ func (prod Submission) Submit( //nolint:cyclop,funlen // see Download in handler/app/internal/remote/remote.go switch prod { case Demozoo: - if err := app.GetDemozoo(c, db, int(id), unid, downloadDir); err != nil { + if err := app.GetDemozoo(c, db, int(idInt32), unid, downloadDir); err != nil { logger.Error(err) html += fmt.Sprintf(`

error, the %s download failed

`, prod.String()) return c.String(http.StatusServiceUnavailable, html) } case Pouet: - if err := app.GetPouet(c, db, int(id), unid, downloadDir); err != nil { + if err := app.GetPouet(c, db, int(idInt32), unid, downloadDir); err != nil { logger.Error(err) html += fmt.Sprintf(`

error, the %s download failed

`, prod.String()) return c.String(http.StatusServiceUnavailable, html) @@ -470,11 +471,13 @@ func (prod Submission) Submit( //nolint:cyclop,funlen return c.String(http.StatusOK, html) } -func sanitizeID(c echo.Context, name, prod string) (int64, error) { +// sanitizeID validates the production ID and ensures that it is a valid numeric value. +// The ID returned as both an int64 and int32 value. +func sanitizeID(c echo.Context, name, prod string) (int64, int32, error) { sid := c.Param("id") id, err := strconv.ParseInt(sid, 10, 64) if err != nil { - return 0, c.String(http.StatusNotAcceptable, + return 0, 0, c.String(http.StatusNotAcceptable, "The "+name+" production ID must be a numeric value, "+sid) } var sanity uint64 @@ -485,10 +488,13 @@ func sanitizeID(c echo.Context, name, prod string) (int64, error) { sanity = pouet.Sanity } if id < 1 || id > int64(sanity) { - return 0, c.String(http.StatusNotAcceptable, + return 0, 0, c.String(http.StatusNotAcceptable, "The "+name+" production ID is invalid, "+sid) } - return id, nil + if id > math.MaxInt32 { + return id, 0, nil + } + return id, int32(id), nil } func UploadPreview(c echo.Context, previewDir, thumbnailDir string) error { diff --git a/internal/demozoo/demozoo.go b/internal/demozoo/demozoo.go index 65830433..1752d457 100644 --- a/internal/demozoo/demozoo.go +++ b/internal/demozoo/demozoo.go @@ -463,31 +463,8 @@ func Find(uri string) GroupID { } // Released returns the production's release date as date_issued_ year, month, day values. -func (p Production) Released() ( //nolint:nonamedreturns - year int, month int, day int, -) { - dates := strings.Split(p.ReleaseDate, "-") - const ( - y = 0 - m = 1 - d = 2 - ymd = 3 - ym = 2 - yyyy = 1 - ) - switch len(dates) { - case ymd: - year, _ = strconv.Atoi(dates[y]) - month, _ = strconv.Atoi(dates[m]) - day, _ = strconv.Atoi(dates[d]) - case ym: - year, _ = strconv.Atoi(dates[y]) - month, _ = strconv.Atoi(dates[m]) - case yyyy: - year, _ = strconv.Atoi(dates[y]) - default: - } - return year, month, day +func (p Production) Released() (int16, int16, int16) { + return helper.Released(p.ReleaseDate) } // Groups returns the first two names in the production that have is_group as true. diff --git a/internal/helper/string.go b/internal/helper/string.go index edd4a260..36d4bbb8 100644 --- a/internal/helper/string.go +++ b/internal/helper/string.go @@ -282,6 +282,38 @@ func PageCount(sum, limit int) uint { return uint(x) } +// Released returns a string release date as year, month, day int16 values. +// The string is expected to be in the format "2024-07-15" or "2024-07" or "2024". +func Released(s string) (int16, int16, int16) { + dates := strings.Split(s, "-") // "2024-07-15" + l := len(dates) + if l == 0 || l > 3 { + return 0, 0, 0 + } + const ( + y = 0 + m = 1 + d = 2 + ) + var year, month, day int16 + if yv, _ := strconv.ParseInt(dates[y], 10, 16); yv > 0 && yv <= math.MaxInt16 { + year = int16(yv) + } + if l < m+1 { + return year, 0, 0 + } + if mv, _ := strconv.ParseInt(dates[m], 10, 16); mv > 0 && mv <= 12 { + month = int16(mv) + } + if l < d+1 { + return year, month, 0 + } + if dv, _ := strconv.ParseInt(dates[d], 10, 16); dv > 0 && dv <= 31 { + day = int16(dv) + } + return year, month, day +} + // ReverseInt reverses an integer. func ReverseInt(i int) (int, error) { // credit: Wade73 diff --git a/internal/helper/string_test.go b/internal/helper/string_test.go index 8e871c12..d80a53fb 100644 --- a/internal/helper/string_test.go +++ b/internal/helper/string_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/Defacto2/server/internal/helper" + "github.com/Defacto2/server/internal/pouet" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -328,3 +329,49 @@ func TestTitleize(t *testing.T) { s = helper.Titleize("hello world, how are you?") assert.Equal(t, "Hello World, How Are You?", s) } + +func TestReleased(t *testing.T) { + t.Parallel() + tests := []struct { + name string + releaseDate string + expectedYear int16 + expectedMonth int16 + expectedDay int16 + }{ + { + name: "Valid release date", + releaseDate: "2024-07-15", + expectedYear: 2024, + expectedMonth: 7, + expectedDay: 15, + }, + { + name: "Valid release date", + releaseDate: "2024-07", + expectedYear: 2024, + expectedMonth: 7, + expectedDay: 0, + }, + { + name: "Invalid release date", + releaseDate: "2024-07-15-01", + expectedYear: 0, + expectedMonth: 0, + expectedDay: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + p := pouet.Production{ + ReleaseDate: tt.releaseDate, + } + year, month, day := p.Released() + assert.Equal(t, tt.expectedYear, year) + assert.Equal(t, tt.expectedMonth, month) + assert.Equal(t, tt.expectedDay, day) + }) + } +} diff --git a/internal/pouet/pouet.go b/internal/pouet/pouet.go index 069e0b5f..be29948f 100644 --- a/internal/pouet/pouet.go +++ b/internal/pouet/pouet.go @@ -131,32 +131,8 @@ func (p Production) Releasers() (string, string) { } // Released returns the production's release date as date_issued_ year, month, day values. -func (p Production) Released() ( //nolint:nonamedreturns - year int, month int, day int, -) { - // "2024-07-15" - dates := strings.Split(p.ReleaseDate, "-") - const ( - y = 0 - m = 1 - d = 2 - ymd = 3 - ym = 2 - yyyy = 1 - ) - switch len(dates) { - case ymd: - year, _ = strconv.Atoi(dates[y]) - month, _ = strconv.Atoi(dates[m]) - day, _ = strconv.Atoi(dates[d]) - case ym: - year, _ = strconv.Atoi(dates[y]) - month, _ = strconv.Atoi(dates[m]) - case yyyy: - year, _ = strconv.Atoi(dates[y]) - default: - } - return year, month, day +func (p Production) Released() (int16, int16, int16) { + return helper.Released(p.ReleaseDate) } // PlatformType parses the Pouet "platform" and "type" data