inline_images.rb 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class HtmlSanitizer
  3. module Scrubber
  4. class InlineImages < Base
  5. attr_reader :attachments_inline, :prefix
  6. def initialize(prefix = SecureRandom.uuid) # rubocop:disable Lint/MissingSuper
  7. @direction = :top_down
  8. @attachments_inline = []
  9. @prefix = prefix
  10. end
  11. def scrub(node)
  12. return CONTINUE if node.name != 'img'
  13. if node['src'] && node['src'] =~ %r{^(data:image/(jpeg|png);base64,.+?)$}i
  14. process_inline_image(node, $1)
  15. elsif node['data-user-avatar'] && node['src'] && node['src'] =~ %r{users/image/(.+)}
  16. process_user_avatar_image(node, $1)
  17. end
  18. STOP
  19. end
  20. private
  21. def inline_image_data(src)
  22. return if src.blank?
  23. matchdata = src.match %r{^(data:image/(jpeg|png);base64,.+?)$}i
  24. return if !matchdata
  25. matchdata[0]
  26. end
  27. def process_inline_image(node, data)
  28. cid = generate_cid
  29. attachment = parse_inline_image(data, cid)
  30. @attachments_inline.push attachment
  31. node['src'] = "cid:#{cid}"
  32. end
  33. def process_user_avatar_image(node, image_hash)
  34. return if hash.blank?
  35. cid = generate_cid
  36. file = avatar_file(image_hash)
  37. return if file.nil?
  38. @attachments_inline.push attachment(file.content, file.filename, cid, mime_type: file.preferences['Mime-Type'], content_type: file.preferences['Content-Type'])
  39. node['src'] = "cid:#{cid}"
  40. end
  41. def parse_inline_image(data, cid)
  42. file_attributes = ImageHelper.data_url_attributes(data)
  43. filename = "image#{@attachments_inline.length + 1}.#{file_attributes[:file_extention]}"
  44. attachment(
  45. file_attributes[:content],
  46. filename,
  47. cid,
  48. mime_type: file_attributes[:mime_type],
  49. )
  50. end
  51. def attachment(content, filename, cid, mime_type: nil, content_type: nil)
  52. {
  53. data: content,
  54. filename: filename,
  55. preferences: {
  56. 'Content-Type' => content_type || mime_type,
  57. 'Mime-Type' => mime_type || content_type,
  58. 'Content-ID' => cid,
  59. 'Content-Disposition' => 'inline',
  60. }
  61. }
  62. end
  63. def generate_cid
  64. "#{prefix}.#{SecureRandom.uuid}@#{fqdn}"
  65. end
  66. def fqdn
  67. @fqdn ||= Setting.get('fqdn')
  68. end
  69. def avatar_file(image_hash)
  70. Avatar.get_by_hash(image_hash)
  71. rescue
  72. nil
  73. end
  74. end
  75. end
  76. end