mail_dude 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bf22950ae8ba64ae0dc2176e5a5256fba2b7b1e0387ff24b421c3fa656fa205
4
- data.tar.gz: 4f518d5ed49a686666e7af69d0b480fac69767af2c685626b0771fa93e2aebcb
3
+ metadata.gz: 38404e9ab732d38243310741c269a4ec27e02a3076c479c891fe7bd532474d97
4
+ data.tar.gz: c66340b339905d47989e60b49d86073aab27785eae7a9da526fd910914db81ff
5
5
  SHA512:
6
- metadata.gz: 2ed12acd960afb2984cae6b80a707bd7d58a8d2e67f3bade8a238cee89df99ee9f1f989f2a1d67edf3e72d69a18c62380cb7e0abd6e73900ae7317a729225757
7
- data.tar.gz: d8ded531449368b283d0341751331c13cdd1f74ce14207f697f8a971a5b60d02075c1fafde5e8886eff21171abffbebf8934a8540834d2e41b9bd4e5c2bc53dd
6
+ metadata.gz: 3d28003d59309eccded62419a0d28573facd4a027045c9df28eefa7491206f5815bc9906f60cb2631a8ada54921bbf59c9b3c418fa28a89462eca055946ee2ef
7
+ data.tar.gz: 7d8011434a33827a7f795f3bf865b7995dfec41d999aab8ef9cfd8d99e2f8b9d3ecf725c75f74deddbb4702fb9f8353666e66565254ca4e23c21f8e1a89b9be2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3
4
+
5
+ - Fix CID image rendering for Content-ID attachments without inline disposition.
6
+ - Strip attachment payloads from stored raw source when attachment capture is disabled.
7
+
3
8
  ## 0.1.2
4
9
 
5
10
  - Add official Ruby 3.1 support.
@@ -2,6 +2,26 @@
2
2
 
3
3
  module MailDude
4
4
  class AttachmentLocator
5
+ class << self
6
+ def attachment_part?(part)
7
+ !part.multipart? && (part.attachment? || part.filename.present? || inline_renderable_part?(part))
8
+ end
9
+
10
+ def content_disposition(part)
11
+ part.content_disposition.to_s.split(';').first.to_s.downcase
12
+ end
13
+
14
+ def inline_renderable_part?(part)
15
+ inline_part?(part) || part.content_id.present?
16
+ end
17
+
18
+ private
19
+
20
+ def inline_part?(part)
21
+ content_disposition(part) == 'inline' && part.content_id.present?
22
+ end
23
+ end
24
+
5
25
  Attachment = Struct.new(:id, :part, :metadata, keyword_init: true) do
6
26
  def content_type
7
27
  metadata['content_type']
@@ -35,6 +55,14 @@ module MailDude
35
55
  end
36
56
  end
37
57
 
58
+ def attachments_count
59
+ attachment_parts.length
60
+ end
61
+
62
+ def attachments_present?
63
+ attachments_count.positive?
64
+ end
65
+
38
66
  def find(attachment_id)
39
67
  raise AttachmentNotFoundError, 'Attachment not found' unless attachment_id.to_s.match?(/\Aa\d+\z/)
40
68
 
@@ -44,6 +72,8 @@ module MailDude
44
72
 
45
73
  def find_inline_by_cid(content_id)
46
74
  normalized = normalize_content_id(content_id)
75
+ return nil if normalized.blank?
76
+
47
77
  attachments.find { |attachment| attachment.metadata['content_id'] == normalized }
48
78
  end
49
79
 
@@ -64,19 +94,7 @@ module MailDude
64
94
  def attachment_parts
65
95
  return [] unless mail
66
96
 
67
- mail.all_parts.select { |part| attachment_part?(part) }
68
- end
69
-
70
- def attachment_part?(part)
71
- !part.multipart? && (part.attachment? || part.filename.present? || inline_part?(part))
72
- end
73
-
74
- def content_disposition(part)
75
- part.content_disposition.to_s.split(';').first.to_s.downcase
76
- end
77
-
78
- def inline_part?(part)
79
- content_disposition(part) == 'inline' && part.content_id.present?
97
+ mail.all_parts.select { |part| self.class.attachment_part?(part) }
80
98
  end
81
99
 
82
100
  def mail
@@ -96,8 +114,8 @@ module MailDude
96
114
  'filename' => sanitize_filename(part.filename, fallback: "attachment-#{id}"),
97
115
  'content_type' => part.mime_type.presence || 'application/octet-stream',
98
116
  'content_id' => normalize_content_id(part.content_id),
99
- 'disposition' => content_disposition(part).presence || 'attachment',
100
- 'inline' => content_disposition(part) == 'inline',
117
+ 'disposition' => self.class.content_disposition(part).presence || 'attachment',
118
+ 'inline' => self.class.inline_renderable_part?(part),
101
119
  'size_bytes' => decoded_size(part)
102
120
  }
103
121
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MailDude
4
+ class AttachmentScrubber
5
+ RAW_SOURCE_OMITTED = "MailDude omitted raw message source because attachment capture is disabled.\n"
6
+
7
+ def initialize(mail)
8
+ @mail = mail
9
+ end
10
+
11
+ def raw_source
12
+ source = mail&.to_s
13
+ return RAW_SOURCE_OMITTED if source.blank?
14
+
15
+ sanitized = Mail.read_from_string(source)
16
+ remove_attachments!(sanitized)
17
+ sanitized.to_s
18
+ rescue StandardError
19
+ RAW_SOURCE_OMITTED
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :mail
25
+
26
+ def remove_attachments!(message)
27
+ return remove_single_part_attachment!(message) unless message.multipart?
28
+
29
+ message.parts.recursive_delete_if { |part| AttachmentLocator.attachment_part?(part) }
30
+ end
31
+
32
+ def remove_single_part_attachment!(message)
33
+ message.body = '' if AttachmentLocator.attachment_part?(message)
34
+ end
35
+ end
36
+ end
@@ -41,7 +41,7 @@ module MailDude
41
41
  sender: presenter.sender_summary,
42
42
  recipients: presenter.recipient_summary,
43
43
  captured_at: presenter.captured_at_label,
44
- attachments_count: presenter.attachments.length,
44
+ attachments_count: presenter.attachment_count,
45
45
  attachment_count_label: presenter.attachment_count_label,
46
46
  mailer_label: presenter.mailer_label
47
47
  }
@@ -80,12 +80,12 @@ module MailDude
80
80
  raw_source.split(/\r?\n\r?\n/, 2).first.to_s
81
81
  end
82
82
 
83
- def has_attachments?
84
- attachments.any?
85
- end
83
+ def has_attachments? = metadata_value('has_attachments') == true || attachments.any?
84
+
85
+ def attachment_count = metadata_value('attachments_count').presence&.to_i || attachments.length
86
86
 
87
87
  def attachment_count_label
88
- count = attachments.length
88
+ count = attachment_count
89
89
  "#{count} #{'attachment'.pluralize(count)}"
90
90
  end
91
91
 
@@ -35,8 +35,8 @@ module MailDude
35
35
  'mailer_action' => internal_header(INTERNAL_ACTION_HEADER),
36
36
  'has_html' => part_present?('text/html'),
37
37
  'has_text' => part_present?('text/plain'),
38
- 'has_attachments' => attachments.any?,
39
- 'attachments_count' => attachments.length,
38
+ 'has_attachments' => attachment_locator.attachments_present?,
39
+ 'attachments_count' => attachment_locator.attachments_count,
40
40
  'attachments' => attachments,
41
41
  'size_bytes' => raw_source.bytesize
42
42
  }
@@ -55,7 +55,11 @@ module MailDude
55
55
  end
56
56
 
57
57
  def attachments
58
- @attachments ||= AttachmentLocator.new(mail).attachments.map(&:metadata)
58
+ @attachments ||= attachment_locator.attachments.map(&:metadata)
59
+ end
60
+
61
+ def attachment_locator
62
+ @attachment_locator ||= AttachmentLocator.new(mail)
59
63
  end
60
64
 
61
65
  def content_type
@@ -35,11 +35,17 @@ module MailDude
35
35
  private
36
36
 
37
37
  def build_record(mail, id: generate_id, captured_at: Time.now.utc)
38
- raw_source = mail.to_s
38
+ raw_source = raw_source_for(mail)
39
39
  metadata = MessageSerializer.new(mail, id: id, captured_at: captured_at, raw_source: raw_source).metadata
40
40
  MessageRecord.new(id: id, metadata: metadata, raw_source: raw_source)
41
41
  end
42
42
 
43
+ def raw_source_for(mail)
44
+ return mail.to_s if MailDude.configuration.capture_attachments
45
+
46
+ AttachmentScrubber.new(mail).raw_source
47
+ end
48
+
43
49
  def generate_id
44
50
  time = Time.now.utc
45
51
  "#{time.strftime('%Y%m%dT%H%M%S')}#{format('%06d', time.usec)}Z-#{SecureRandom.hex(8)}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MailDude
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
data/lib/mail_dude.rb CHANGED
@@ -17,6 +17,7 @@ require_relative 'mail_dude/pagination'
17
17
  require_relative 'mail_dude/message_serializer'
18
18
  require_relative 'mail_dude/message_presenter'
19
19
  require_relative 'mail_dude/attachment_locator'
20
+ require_relative 'mail_dude/attachment_scrubber'
20
21
  require_relative 'mail_dude/html_body_renderer'
21
22
  require_relative 'mail_dude/message_broadcast'
22
23
  require_relative 'mail_dude/stores/base'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_dude
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - MailDude contributors
@@ -184,6 +184,7 @@ files:
184
184
  - lib/generators/mail_dude/templates/initializer.tt
185
185
  - lib/mail_dude.rb
186
186
  - lib/mail_dude/attachment_locator.rb
187
+ - lib/mail_dude/attachment_scrubber.rb
187
188
  - lib/mail_dude/configuration.rb
188
189
  - lib/mail_dude/dashboard.rb
189
190
  - lib/mail_dude/delivery_method.rb