diff --git a/lib/email/sender.rb b/lib/email/sender.rb index 3ca23cc6ff4..43f473889b5 100644 --- a/lib/email/sender.rb +++ b/lib/email/sender.rb @@ -435,12 +435,18 @@ module Email # Due to mail gem magic, @message.text_part and @message.html_part still # refer to the same objects. # + # Most imporantly, we need to specify the boundary for the multipart/mixed + # part of the email, otherwise we can end up with an email that appears to + # be empty with the entire body attached as a single attachment, and some + # mail parsers consider the entire email as a preamble/epilogue. + # + # c.f. https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html def fix_parts_after_attachments! has_attachments = @message.attachments.present? has_alternative_renderings = @message.html_part.present? && @message.text_part.present? if has_attachments && has_alternative_renderings - @message.content_type = "multipart/mixed" + @message.content_type = "multipart/mixed; boundary=\"#{@message.body.boundary}\"" html_part = @message.html_part @message.html_part = nil diff --git a/spec/lib/email/sender_spec.rb b/spec/lib/email/sender_spec.rb index 211da031705..830865a068c 100644 --- a/spec/lib/email/sender_spec.rb +++ b/spec/lib/email/sender_spec.rb @@ -681,6 +681,9 @@ RSpec.describe Email::Sender do Email::Sender.new(summary, "digest").send + expect(summary.content_type).to eq( + "multipart/mixed; boundary=\"#{summary.body.boundary}\"", + ) expect(summary.attachments.map(&:filename)).to include( *[@secure_image, @secure_image_2, @secure_image_3].map(&:original_filename), )