apply.go 12 KB

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