123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- package optionsparser
- import (
- "fmt"
- "log/slog"
- "slices"
- "strconv"
- "time"
- "github.com/imgproxy/imgproxy/v3/imagetype"
- "github.com/imgproxy/imgproxy/v3/options"
- "github.com/imgproxy/imgproxy/v3/options/keys"
- "github.com/imgproxy/imgproxy/v3/processing"
- "github.com/imgproxy/imgproxy/v3/vips/color"
- )
- func applyWidthOption(o *options.Options, args []string) error {
- return parsePositiveInt(o, keys.Width, args...)
- }
- func applyHeightOption(o *options.Options, args []string) error {
- return parsePositiveInt(o, keys.Height, args...)
- }
- func applyMinWidthOption(o *options.Options, args []string) error {
- return parsePositiveInt(o, keys.MinWidth, args...)
- }
- func applyMinHeightOption(o *options.Options, args []string) error {
- return parsePositiveInt(o, keys.MinHeight, args...)
- }
- func applyEnlargeOption(o *options.Options, args []string) error {
- return parseBool(o, keys.Enlarge, args...)
- }
- func applyExtendOption(o *options.Options, args []string) error {
- return parseExtend(o, keys.PrefixExtend, args)
- }
- func applyExtendAspectRatioOption(o *options.Options, args []string) error {
- return parseExtend(o, keys.PrefixExtendAspectRatio, args)
- }
- func applySizeOption(o *options.Options, args []string) (err error) {
- if err = ensureMaxArgs("size", args, 7); err != nil {
- return
- }
- if len(args) >= 1 && len(args[0]) > 0 {
- if err = applyWidthOption(o, args[0:1]); err != nil {
- return
- }
- }
- if len(args) >= 2 && len(args[1]) > 0 {
- if err = applyHeightOption(o, args[1:2]); err != nil {
- return
- }
- }
- if len(args) >= 3 && len(args[2]) > 0 {
- if err = applyEnlargeOption(o, args[2:3]); err != nil {
- return
- }
- }
- if len(args) >= 4 && len(args[3]) > 0 {
- if err = applyExtendOption(o, args[3:]); err != nil {
- return
- }
- }
- return nil
- }
- func applyResizingTypeOption(o *options.Options, args []string) error {
- return parseFromMap(o, keys.ResizingType, processing.ResizeTypes, args...)
- }
- func applyResizeOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs("resize", args, 8); err != nil {
- return err
- }
- if len(args[0]) > 0 {
- if err := applyResizingTypeOption(o, args[0:1]); err != nil {
- return err
- }
- }
- if len(args) > 1 {
- if err := applySizeOption(o, args[1:]); err != nil {
- return err
- }
- }
- return nil
- }
- func applyZoomOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs("zoom", args, 2); err != nil {
- return err
- }
- if err := parsePositiveNonZeroFloat(o, keys.ZoomWidth, args[0]); err != nil {
- return err
- }
- if len(args) < 2 {
- o.CopyValue(keys.ZoomWidth, keys.ZoomHeight)
- return nil
- }
- if err := parsePositiveNonZeroFloat(o, keys.ZoomHeight, args[1]); err != nil {
- return err
- }
- return nil
- }
- func applyDprOption(o *options.Options, args []string) error {
- return parsePositiveNonZeroFloat(o, keys.Dpr, args...)
- }
- func applyGravityOption(o *options.Options, args []string) error {
- return parseGravity(o, keys.Gravity, processing.CropGravityTypes, args...)
- }
- func applyCropOption(o *options.Options, args []string) error {
- if err := parsePositiveFloat(o, keys.CropWidth, args[0]); err != nil {
- return err
- }
- if len(args) > 1 {
- if err := parsePositiveFloat(o, keys.CropHeight, args[1]); err != nil {
- return err
- }
- }
- if len(args) > 2 {
- return parseGravity(o, keys.CropGravity, processing.CropGravityTypes, args[2:]...)
- }
- return nil
- }
- func applyPaddingOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs("padding", args, 4); err != nil {
- return err
- }
- if len(args) > 0 && len(args[0]) > 0 {
- if err := parsePositiveInt(o, keys.PaddingTop, args[0]); err != nil {
- return err
- }
- }
- if len(args) > 1 && len(args[1]) > 0 {
- if err := parsePositiveInt(o, keys.PaddingRight, args[1]); err != nil {
- return err
- }
- } else {
- o.CopyValue(keys.PaddingTop, keys.PaddingRight)
- }
- if len(args) > 2 && len(args[2]) > 0 {
- if err := parsePositiveInt(o, keys.PaddingBottom, args[2]); err != nil {
- return err
- }
- } else {
- o.CopyValue(keys.PaddingTop, keys.PaddingBottom)
- }
- if len(args) > 3 && len(args[3]) > 0 {
- if err := parsePositiveInt(o, keys.PaddingLeft, args[3]); err != nil {
- return err
- }
- } else {
- o.CopyValue(keys.PaddingRight, keys.PaddingLeft)
- }
- return nil
- }
- func applyTrimOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs("trim", args, 4); err != nil {
- return err
- }
- nArgs := len(args)
- if len(args[0]) > 0 {
- if err := parseFloat(o, keys.TrimThreshold, args[0]); err != nil {
- return err
- }
- } else {
- o.Delete(keys.TrimThreshold)
- }
- if nArgs > 1 && len(args[1]) > 0 {
- if err := parseHexRGBColor(o, keys.TrimColor, args[1]); err != nil {
- return err
- }
- } else {
- o.Delete(keys.TrimColor)
- }
- if nArgs > 2 && len(args[2]) > 0 {
- if err := parseBool(o, keys.TrimEqualHor, args[2]); err != nil {
- return err
- }
- }
- if nArgs > 3 && len(args[3]) > 0 {
- if err := parseBool(o, keys.TrimEqualVer, args[3]); err != nil {
- return err
- }
- }
- return nil
- }
- func applyRotateOption(o *options.Options, args []string) error {
- if err := parseInt(o, keys.Rotate, args...); err != nil {
- return err
- }
- if options.Get(o, keys.Rotate, 0)%90 != 0 {
- return newOptionArgumentError("Rotation angle must be a multiple of 90")
- }
- return nil
- }
- func applyQualityOption(o *options.Options, args []string) error {
- return parseQualityInt(o, keys.Quality, args...)
- }
- func applyFormatQualityOption(o *options.Options, args []string) error {
- argsLen := len(args)
- if len(args)%2 != 0 {
- return newOptionArgumentError("Missing %s for: %s", keys.PrefixFormatQuality, args[argsLen-1])
- }
- for i := 0; i < argsLen; i += 2 {
- f, ok := imagetype.GetTypeByName(args[i])
- if !ok {
- return newOptionArgumentError("Invalid image format: %s", args[i])
- }
- if err := parseQualityInt(o, keys.FormatQuality(f), args[i+1]); err != nil {
- return err
- }
- }
- return nil
- }
- func applyMaxBytesOption(o *options.Options, args []string) error {
- return parsePositiveInt(o, keys.MaxBytes, args...)
- }
- func applyBackgroundOption(o *options.Options, args []string) error {
- switch len(args) {
- case 1:
- if len(args[0]) == 0 {
- o.Delete(keys.Background)
- return nil
- }
- if err := parseHexRGBColor(o, keys.Background, args[0]); err != nil {
- return err
- }
- case 3:
- var c color.RGB
- if r, err := strconv.ParseUint(args[0], 10, 8); err == nil && r <= 255 {
- c.R = uint8(r)
- } else {
- return newInvalidArgumentError(keys.Background+".R", args[0], "number in range 0-255")
- }
- if g, err := strconv.ParseUint(args[1], 10, 8); err == nil && g <= 255 {
- c.G = uint8(g)
- } else {
- return newInvalidArgumentError(keys.Background+".G", args[1], "number in range 0-255")
- }
- if b, err := strconv.ParseUint(args[2], 10, 8); err == nil && b <= 255 {
- c.B = uint8(b)
- } else {
- return newInvalidArgumentError(keys.Background+".B", args[2], "number in range 0-255")
- }
- o.Set(keys.Background, c)
- default:
- return newInvalidArgsError(keys.Background, args)
- }
- return nil
- }
- func applyBlurOption(o *options.Options, args []string) error {
- return parsePositiveNonZeroFloat(o, keys.Blur, args...)
- }
- func applySharpenOption(o *options.Options, args []string) error {
- return parsePositiveNonZeroFloat(o, keys.Sharpen, args...)
- }
- func applyPixelateOption(o *options.Options, args []string) error {
- return parsePositiveInt(o, keys.Pixelate, args...)
- }
- func applyWatermarkOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs("watermark", args, 7); err != nil {
- return err
- }
- if err := parseOpacityFloat(o, keys.WatermarkOpacity, args[0]); err != nil {
- return err
- }
- if len(args) > 1 && len(args[1]) > 0 {
- if _, err := parseGravityType(
- o, keys.WatermarkPosition, processing.WatermarkGravityTypes, args[1],
- ); err != nil {
- return err
- }
- }
- if len(args) > 2 && len(args[2]) > 0 {
- if err := parseFloat(o, keys.WatermarkXOffset, args[2]); err != nil {
- return err
- }
- }
- if len(args) > 3 && len(args[3]) > 0 {
- if err := parseFloat(o, keys.WatermarkYOffset, args[3]); err != nil {
- return err
- }
- }
- if len(args) > 4 && len(args[4]) > 0 {
- if err := parsePositiveNonZeroFloat(o, keys.WatermarkScale, args[4]); err == nil {
- return err
- }
- }
- return nil
- }
- func applyFormatOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs(keys.Format, args, 1); err != nil {
- return err
- }
- if f, ok := imagetype.GetTypeByName(args[0]); ok {
- o.Set(keys.Format, f)
- } else {
- return newInvalidArgumentError(keys.Format, args[0], "supported image format")
- }
- return nil
- }
- func applyCacheBusterOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs(keys.CacheBuster, args, 1); err != nil {
- return err
- }
- o.Set(keys.CacheBuster, args[0])
- return nil
- }
- func applySkipProcessingFormatsOption(o *options.Options, args []string) error {
- for _, format := range args {
- if f, ok := imagetype.GetTypeByName(format); ok {
- options.AppendToSlice(o, keys.SkipProcessing, f)
- } else {
- return newOptionArgumentError("Invalid image format in %s: %s", keys.SkipProcessing, format)
- }
- }
- return nil
- }
- func applyRawOption(o *options.Options, args []string) error {
- return parseBool(o, keys.Raw, args...)
- }
- func applyFilenameOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs(keys.Filename, args, 2); err != nil {
- return err
- }
- if len(args) > 1 && len(args[1]) > 0 {
- if encoded, _ := strconv.ParseBool(args[1]); encoded {
- return parseBase64String(o, keys.Filename, args[0])
- }
- }
- o.Set(keys.Filename, args[0])
- return nil
- }
- func applyExpiresOption(o *options.Options, args []string) error {
- if err := ensureMaxArgs(keys.Expires, args, 1); err != nil {
- return err
- }
- timestamp, err := strconv.ParseInt(args[0], 10, 64)
- if err != nil {
- return newInvalidArgumentError(keys.Expires, args[0], "unix timestamp")
- }
- if timestamp > 0 && timestamp < time.Now().Unix() {
- return newOptionArgumentError("Expired URL")
- }
- o.Set(keys.Expires, time.Unix(timestamp, 0))
- return nil
- }
- func applyStripMetadataOption(o *options.Options, args []string) error {
- return parseBool(o, keys.StripMetadata, args...)
- }
- func applyKeepCopyrightOption(o *options.Options, args []string) error {
- return parseBool(o, keys.KeepCopyright, args...)
- }
- func applyStripColorProfileOption(o *options.Options, args []string) error {
- return parseBool(o, keys.StripColorProfile, args...)
- }
- func applyAutoRotateOption(o *options.Options, args []string) error {
- return parseBool(o, keys.AutoRotate, args...)
- }
- func applyEnforceThumbnailOption(o *options.Options, args []string) error {
- return parseBool(o, keys.EnforceThumbnail, args...)
- }
- func applyReturnAttachmentOption(o *options.Options, args []string) error {
- return parseBool(o, keys.ReturnAttachment, args...)
- }
- func applyMaxSrcResolutionOption(p *Parser, o *options.Options, args []string) error {
- if err := p.IsSecurityOptionsAllowed(); err != nil {
- return err
- }
- return parseResolution(o, keys.MaxSrcResolution, args...)
- }
- func applyMaxSrcFileSizeOption(p *Parser, o *options.Options, args []string) error {
- if err := p.IsSecurityOptionsAllowed(); err != nil {
- return err
- }
- return parseInt(o, keys.MaxSrcFileSize, args...)
- }
- func applyMaxAnimationFramesOption(p *Parser, o *options.Options, args []string) error {
- if err := p.IsSecurityOptionsAllowed(); err != nil {
- return err
- }
- return parsePositiveNonZeroInt(o, keys.MaxAnimationFrames, args...)
- }
- func applyMaxAnimationFrameResolutionOption(p *Parser, o *options.Options, args []string) error {
- if err := p.IsSecurityOptionsAllowed(); err != nil {
- return err
- }
- return parseResolution(o, keys.MaxAnimationFrameResolution, args...)
- }
- func applyMaxResultDimensionOption(p *Parser, o *options.Options, args []string) error {
- if err := p.IsSecurityOptionsAllowed(); err != nil {
- return err
- }
- return parseInt(o, keys.MaxResultDimension, args...)
- }
- func applyPresetOption(p *Parser, o *options.Options, args []string, usedPresets ...string) error {
- for _, preset := range args {
- if pr, ok := p.presets[preset]; ok {
- if slices.Contains(usedPresets, preset) {
- slog.Warn(fmt.Sprintf("Recursive preset usage is detected: %s", preset))
- continue
- }
- options.AppendToSlice(o, keys.UsedPresets, preset)
- if err := p.applyURLOptions(o, pr, true, append(usedPresets, preset)...); err != nil {
- return err
- }
- } else {
- return newOptionArgumentError("Unknown preset: %s", preset)
- }
- }
- return nil
- }
|