apply.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. package optionsparser
  2. import (
  3. "fmt"
  4. "log/slog"
  5. "slices"
  6. "strconv"
  7. "time"
  8. "github.com/imgproxy/imgproxy/v3/imagetype"
  9. "github.com/imgproxy/imgproxy/v3/options"
  10. "github.com/imgproxy/imgproxy/v3/options/keys"
  11. "github.com/imgproxy/imgproxy/v3/processing"
  12. "github.com/imgproxy/imgproxy/v3/vips/color"
  13. )
  14. func applyWidthOption(o *options.Options, args []string) error {
  15. return parsePositiveInt(o, keys.Width, args...)
  16. }
  17. func applyHeightOption(o *options.Options, args []string) error {
  18. return parsePositiveInt(o, keys.Height, args...)
  19. }
  20. func applyMinWidthOption(o *options.Options, args []string) error {
  21. return parsePositiveInt(o, keys.MinWidth, args...)
  22. }
  23. func applyMinHeightOption(o *options.Options, args []string) error {
  24. return parsePositiveInt(o, keys.MinHeight, args...)
  25. }
  26. func applyEnlargeOption(o *options.Options, args []string) error {
  27. return parseBool(o, keys.Enlarge, args...)
  28. }
  29. func applyExtendOption(o *options.Options, args []string) error {
  30. return parseExtend(o, keys.PrefixExtend, args)
  31. }
  32. func applyExtendAspectRatioOption(o *options.Options, args []string) error {
  33. return parseExtend(o, keys.PrefixExtendAspectRatio, args)
  34. }
  35. func applySizeOption(o *options.Options, args []string) (err error) {
  36. if err = ensureMaxArgs("size", args, 7); err != nil {
  37. return
  38. }
  39. if len(args) >= 1 && len(args[0]) > 0 {
  40. if err = applyWidthOption(o, args[0:1]); err != nil {
  41. return
  42. }
  43. }
  44. if len(args) >= 2 && len(args[1]) > 0 {
  45. if err = applyHeightOption(o, args[1:2]); err != nil {
  46. return
  47. }
  48. }
  49. if len(args) >= 3 && len(args[2]) > 0 {
  50. if err = applyEnlargeOption(o, args[2:3]); err != nil {
  51. return
  52. }
  53. }
  54. if len(args) >= 4 && len(args[3]) > 0 {
  55. if err = applyExtendOption(o, args[3:]); err != nil {
  56. return
  57. }
  58. }
  59. return nil
  60. }
  61. func applyResizingTypeOption(o *options.Options, args []string) error {
  62. return parseFromMap(o, keys.ResizingType, processing.ResizeTypes, args...)
  63. }
  64. func applyResizeOption(o *options.Options, args []string) error {
  65. if err := ensureMaxArgs("resize", args, 8); err != nil {
  66. return err
  67. }
  68. if len(args[0]) > 0 {
  69. if err := applyResizingTypeOption(o, args[0:1]); err != nil {
  70. return err
  71. }
  72. }
  73. if len(args) > 1 {
  74. if err := applySizeOption(o, args[1:]); err != nil {
  75. return err
  76. }
  77. }
  78. return nil
  79. }
  80. func applyZoomOption(o *options.Options, args []string) error {
  81. if err := ensureMaxArgs("zoom", args, 2); err != nil {
  82. return err
  83. }
  84. if err := parsePositiveNonZeroFloat(o, keys.ZoomWidth, args[0]); err != nil {
  85. return err
  86. }
  87. if len(args) < 2 {
  88. o.CopyValue(keys.ZoomWidth, keys.ZoomHeight)
  89. return nil
  90. }
  91. if err := parsePositiveNonZeroFloat(o, keys.ZoomHeight, args[1]); err != nil {
  92. return err
  93. }
  94. return nil
  95. }
  96. func applyDprOption(o *options.Options, args []string) error {
  97. return parsePositiveNonZeroFloat(o, keys.Dpr, args...)
  98. }
  99. func applyGravityOption(o *options.Options, args []string) error {
  100. return parseGravity(o, keys.Gravity, processing.CropGravityTypes, args...)
  101. }
  102. func applyCropOption(o *options.Options, args []string) error {
  103. if err := parsePositiveFloat(o, keys.CropWidth, args[0]); err != nil {
  104. return err
  105. }
  106. if len(args) > 1 {
  107. if err := parsePositiveFloat(o, keys.CropHeight, args[1]); err != nil {
  108. return err
  109. }
  110. }
  111. if len(args) > 2 {
  112. return parseGravity(o, keys.CropGravity, processing.CropGravityTypes, args[2:]...)
  113. }
  114. return nil
  115. }
  116. func applyPaddingOption(o *options.Options, args []string) error {
  117. if err := ensureMaxArgs("padding", args, 4); err != nil {
  118. return err
  119. }
  120. if len(args) > 0 && len(args[0]) > 0 {
  121. if err := parsePositiveInt(o, keys.PaddingTop, args[0]); err != nil {
  122. return err
  123. }
  124. }
  125. if len(args) > 1 && len(args[1]) > 0 {
  126. if err := parsePositiveInt(o, keys.PaddingRight, args[1]); err != nil {
  127. return err
  128. }
  129. } else {
  130. o.CopyValue(keys.PaddingTop, keys.PaddingRight)
  131. }
  132. if len(args) > 2 && len(args[2]) > 0 {
  133. if err := parsePositiveInt(o, keys.PaddingBottom, args[2]); err != nil {
  134. return err
  135. }
  136. } else {
  137. o.CopyValue(keys.PaddingTop, keys.PaddingBottom)
  138. }
  139. if len(args) > 3 && len(args[3]) > 0 {
  140. if err := parsePositiveInt(o, keys.PaddingLeft, args[3]); err != nil {
  141. return err
  142. }
  143. } else {
  144. o.CopyValue(keys.PaddingRight, keys.PaddingLeft)
  145. }
  146. return nil
  147. }
  148. func applyTrimOption(o *options.Options, args []string) error {
  149. if err := ensureMaxArgs("trim", args, 4); err != nil {
  150. return err
  151. }
  152. nArgs := len(args)
  153. if len(args[0]) > 0 {
  154. if err := parseFloat(o, keys.TrimThreshold, args[0]); err != nil {
  155. return err
  156. }
  157. } else {
  158. o.Delete(keys.TrimThreshold)
  159. }
  160. if nArgs > 1 && len(args[1]) > 0 {
  161. if err := parseHexRGBColor(o, keys.TrimColor, args[1]); err != nil {
  162. return err
  163. }
  164. } else {
  165. o.Delete(keys.TrimColor)
  166. }
  167. if nArgs > 2 && len(args[2]) > 0 {
  168. if err := parseBool(o, keys.TrimEqualHor, args[2]); err != nil {
  169. return err
  170. }
  171. }
  172. if nArgs > 3 && len(args[3]) > 0 {
  173. if err := parseBool(o, keys.TrimEqualVer, args[3]); err != nil {
  174. return err
  175. }
  176. }
  177. return nil
  178. }
  179. func applyRotateOption(o *options.Options, args []string) error {
  180. if err := parseInt(o, keys.Rotate, args...); err != nil {
  181. return err
  182. }
  183. if options.Get(o, keys.Rotate, 0)%90 != 0 {
  184. return newOptionArgumentError("Rotation angle must be a multiple of 90")
  185. }
  186. return nil
  187. }
  188. func applyQualityOption(o *options.Options, args []string) error {
  189. return parseQualityInt(o, keys.Quality, args...)
  190. }
  191. func applyFormatQualityOption(o *options.Options, args []string) error {
  192. argsLen := len(args)
  193. if len(args)%2 != 0 {
  194. return newOptionArgumentError("Missing %s for: %s", keys.PrefixFormatQuality, args[argsLen-1])
  195. }
  196. for i := 0; i < argsLen; i += 2 {
  197. f, ok := imagetype.GetTypeByName(args[i])
  198. if !ok {
  199. return newOptionArgumentError("Invalid image format: %s", args[i])
  200. }
  201. if err := parseQualityInt(o, keys.FormatQuality(f), args[i+1]); err != nil {
  202. return err
  203. }
  204. }
  205. return nil
  206. }
  207. func applyMaxBytesOption(o *options.Options, args []string) error {
  208. return parsePositiveInt(o, keys.MaxBytes, args...)
  209. }
  210. func applyBackgroundOption(o *options.Options, args []string) error {
  211. switch len(args) {
  212. case 1:
  213. if len(args[0]) == 0 {
  214. o.Delete(keys.Background)
  215. return nil
  216. }
  217. if err := parseHexRGBColor(o, keys.Background, args[0]); err != nil {
  218. return err
  219. }
  220. case 3:
  221. var c color.RGB
  222. if r, err := strconv.ParseUint(args[0], 10, 8); err == nil && r <= 255 {
  223. c.R = uint8(r)
  224. } else {
  225. return newInvalidArgumentError(keys.Background+".R", args[0], "number in range 0-255")
  226. }
  227. if g, err := strconv.ParseUint(args[1], 10, 8); err == nil && g <= 255 {
  228. c.G = uint8(g)
  229. } else {
  230. return newInvalidArgumentError(keys.Background+".G", args[1], "number in range 0-255")
  231. }
  232. if b, err := strconv.ParseUint(args[2], 10, 8); err == nil && b <= 255 {
  233. c.B = uint8(b)
  234. } else {
  235. return newInvalidArgumentError(keys.Background+".B", args[2], "number in range 0-255")
  236. }
  237. o.Set(keys.Background, c)
  238. default:
  239. return newInvalidArgsError(keys.Background, args)
  240. }
  241. return nil
  242. }
  243. func applyBlurOption(o *options.Options, args []string) error {
  244. return parsePositiveNonZeroFloat(o, keys.Blur, args...)
  245. }
  246. func applySharpenOption(o *options.Options, args []string) error {
  247. return parsePositiveNonZeroFloat(o, keys.Sharpen, args...)
  248. }
  249. func applyPixelateOption(o *options.Options, args []string) error {
  250. return parsePositiveInt(o, keys.Pixelate, args...)
  251. }
  252. func applyWatermarkOption(o *options.Options, args []string) error {
  253. if err := ensureMaxArgs("watermark", args, 7); err != nil {
  254. return err
  255. }
  256. if err := parseOpacityFloat(o, keys.WatermarkOpacity, args[0]); err != nil {
  257. return err
  258. }
  259. if len(args) > 1 && len(args[1]) > 0 {
  260. if _, err := parseGravityType(
  261. o, keys.WatermarkPosition, processing.WatermarkGravityTypes, args[1],
  262. ); err != nil {
  263. return err
  264. }
  265. }
  266. if len(args) > 2 && len(args[2]) > 0 {
  267. if err := parseFloat(o, keys.WatermarkXOffset, args[2]); err != nil {
  268. return err
  269. }
  270. }
  271. if len(args) > 3 && len(args[3]) > 0 {
  272. if err := parseFloat(o, keys.WatermarkYOffset, args[3]); err != nil {
  273. return err
  274. }
  275. }
  276. if len(args) > 4 && len(args[4]) > 0 {
  277. if err := parsePositiveNonZeroFloat(o, keys.WatermarkScale, args[4]); err == nil {
  278. return err
  279. }
  280. }
  281. return nil
  282. }
  283. func applyFormatOption(o *options.Options, args []string) error {
  284. if err := ensureMaxArgs(keys.Format, args, 1); err != nil {
  285. return err
  286. }
  287. if f, ok := imagetype.GetTypeByName(args[0]); ok {
  288. o.Set(keys.Format, f)
  289. } else {
  290. return newInvalidArgumentError(keys.Format, args[0], "supported image format")
  291. }
  292. return nil
  293. }
  294. func applyCacheBusterOption(o *options.Options, args []string) error {
  295. if err := ensureMaxArgs(keys.CacheBuster, args, 1); err != nil {
  296. return err
  297. }
  298. o.Set(keys.CacheBuster, args[0])
  299. return nil
  300. }
  301. func applySkipProcessingFormatsOption(o *options.Options, args []string) error {
  302. for _, format := range args {
  303. if f, ok := imagetype.GetTypeByName(format); ok {
  304. options.AppendToSlice(o, keys.SkipProcessing, f)
  305. } else {
  306. return newOptionArgumentError("Invalid image format in %s: %s", keys.SkipProcessing, format)
  307. }
  308. }
  309. return nil
  310. }
  311. func applyRawOption(o *options.Options, args []string) error {
  312. return parseBool(o, keys.Raw, args...)
  313. }
  314. func applyFilenameOption(o *options.Options, args []string) error {
  315. if err := ensureMaxArgs(keys.Filename, args, 2); err != nil {
  316. return err
  317. }
  318. if len(args) > 1 && len(args[1]) > 0 {
  319. if encoded, _ := strconv.ParseBool(args[1]); encoded {
  320. return parseBase64String(o, keys.Filename, args[0])
  321. }
  322. }
  323. o.Set(keys.Filename, args[0])
  324. return nil
  325. }
  326. func applyExpiresOption(o *options.Options, args []string) error {
  327. if err := ensureMaxArgs(keys.Expires, args, 1); err != nil {
  328. return err
  329. }
  330. timestamp, err := strconv.ParseInt(args[0], 10, 64)
  331. if err != nil {
  332. return newInvalidArgumentError(keys.Expires, args[0], "unix timestamp")
  333. }
  334. if timestamp > 0 && timestamp < time.Now().Unix() {
  335. return newOptionArgumentError("Expired URL")
  336. }
  337. o.Set(keys.Expires, time.Unix(timestamp, 0))
  338. return nil
  339. }
  340. func applyStripMetadataOption(o *options.Options, args []string) error {
  341. return parseBool(o, keys.StripMetadata, args...)
  342. }
  343. func applyKeepCopyrightOption(o *options.Options, args []string) error {
  344. return parseBool(o, keys.KeepCopyright, args...)
  345. }
  346. func applyStripColorProfileOption(o *options.Options, args []string) error {
  347. return parseBool(o, keys.StripColorProfile, args...)
  348. }
  349. func applyAutoRotateOption(o *options.Options, args []string) error {
  350. return parseBool(o, keys.AutoRotate, args...)
  351. }
  352. func applyEnforceThumbnailOption(o *options.Options, args []string) error {
  353. return parseBool(o, keys.EnforceThumbnail, args...)
  354. }
  355. func applyReturnAttachmentOption(o *options.Options, args []string) error {
  356. return parseBool(o, keys.ReturnAttachment, args...)
  357. }
  358. func applyMaxSrcResolutionOption(p *Parser, o *options.Options, args []string) error {
  359. if err := p.IsSecurityOptionsAllowed(); err != nil {
  360. return err
  361. }
  362. return parseResolution(o, keys.MaxSrcResolution, args...)
  363. }
  364. func applyMaxSrcFileSizeOption(p *Parser, o *options.Options, args []string) error {
  365. if err := p.IsSecurityOptionsAllowed(); err != nil {
  366. return err
  367. }
  368. return parseInt(o, keys.MaxSrcFileSize, args...)
  369. }
  370. func applyMaxAnimationFramesOption(p *Parser, o *options.Options, args []string) error {
  371. if err := p.IsSecurityOptionsAllowed(); err != nil {
  372. return err
  373. }
  374. return parsePositiveNonZeroInt(o, keys.MaxAnimationFrames, args...)
  375. }
  376. func applyMaxAnimationFrameResolutionOption(p *Parser, o *options.Options, args []string) error {
  377. if err := p.IsSecurityOptionsAllowed(); err != nil {
  378. return err
  379. }
  380. return parseResolution(o, keys.MaxAnimationFrameResolution, args...)
  381. }
  382. func applyMaxResultDimensionOption(p *Parser, o *options.Options, args []string) error {
  383. if err := p.IsSecurityOptionsAllowed(); err != nil {
  384. return err
  385. }
  386. return parseInt(o, keys.MaxResultDimension, args...)
  387. }
  388. func applyPresetOption(p *Parser, o *options.Options, args []string, usedPresets ...string) error {
  389. for _, preset := range args {
  390. if pr, ok := p.presets[preset]; ok {
  391. if slices.Contains(usedPresets, preset) {
  392. slog.Warn(fmt.Sprintf("Recursive preset usage is detected: %s", preset))
  393. continue
  394. }
  395. options.AppendToSlice(o, keys.UsedPresets, preset)
  396. if err := p.applyURLOptions(o, pr, true, append(usedPresets, preset)...); err != nil {
  397. return err
  398. }
  399. } else {
  400. return newOptionArgumentError("Unknown preset: %s", preset)
  401. }
  402. }
  403. return nil
  404. }