diff --git a/internal/gtserror/error.go b/internal/gtserror/error.go index a39b5475c..bd83a8dc8 100644 --- a/internal/gtserror/error.go +++ b/internal/gtserror/error.go @@ -40,8 +40,22 @@ const ( notRelevantKey spamKey notPermittedKey + limitReachedKey ) +// LimitReached indicates that this error was caused by +// some kind of limit being reached, e.g. media upload limit. +func LimitReached(err error) bool { + _, ok := errors.Value(err, limitReachedKey).(struct{}) + return ok +} + +// SetLimitReached will wrap the given error to store a "limit reached" +// flag, returning wrapped error. See LimitReached() for example use-cases. +func SetLimitReached(err error) error { + return errors.WithValue(err, limitReachedKey, struct{}{}) +} + // IsUnretrievable indicates that a call to retrieve a resource // (account, status, attachment, etc) could not be fulfilled, either // because it was not found locally, or because some prerequisite diff --git a/internal/media/util.go b/internal/media/util.go index 22121a546..538d6f572 100644 --- a/internal/media/util.go +++ b/internal/media/util.go @@ -29,6 +29,7 @@ import ( "codeberg.org/gruf/go-bytesize" "codeberg.org/gruf/go-iotools" "codeberg.org/gruf/go-mimetypes" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" ) // file represents one file @@ -143,8 +144,9 @@ func drainToTmp(rc io.ReadCloser) (string, error) { // Check to see if limit was reached, // (produces more useful error messages). - if lr != nil && !iotools.AtEOF(lr.R) { - return path, fmt.Errorf("reached read limit %s", bytesize.Size(limit)) + if lr != nil && lr.N <= 0 { + err := fmt.Errorf("reached read limit %s", bytesize.Size(limit)) + return path, gtserror.SetLimitReached(err) } return path, nil diff --git a/internal/processing/common/media.go b/internal/processing/common/media.go index 7baf30345..7957470cd 100644 --- a/internal/processing/common/media.go +++ b/internal/processing/common/media.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" @@ -51,11 +52,18 @@ func (p *Processor) StoreLocalMedia( // Immediately trigger write to storage. attachment, err := processing.Load(ctx) - if err != nil { - const text = "error processing emoji" + switch { + case gtserror.LimitReached(err): + limit := config.GetMediaLocalMaxSize() + text := fmt.Sprintf("local media size limit reached: %s", limit) + return nil, gtserror.NewErrorUnprocessableEntity(err, text) + + case err != nil: + const text = "error processing media" err := gtserror.Newf("error processing media: %w", err) return nil, gtserror.NewErrorUnprocessableEntity(err, text) - } else if attachment.Type == gtsmodel.FileTypeUnknown { + + case attachment.Type == gtsmodel.FileTypeUnknown: text := fmt.Sprintf("could not process %s type media", attachment.File.ContentType) return nil, gtserror.NewErrorUnprocessableEntity(errors.New(text), text) } @@ -86,9 +94,15 @@ func (p *Processor) StoreLocalEmoji( return nil, gtserror.NewErrorInternalError(err) } - // Immediately write to storage. + // Immediately trigger write to storage. emoji, err := processing.Load(ctx) - if err != nil { + switch { + case gtserror.LimitReached(err): + limit := config.GetMediaEmojiLocalMaxSize() + text := fmt.Sprintf("local emoji size limit reached: %s", limit) + return nil, gtserror.NewErrorUnprocessableEntity(err, text) + + case err != nil: const text = "error processing emoji" err := gtserror.Newf("error processing emoji %s: %w", shortcode, err) return nil, gtserror.NewErrorUnprocessableEntity(err, text)