Neeraj Shukla
Neeraj Shukla

Reputation: 1581

How to decode an email with embedded images in Ruby/Rails Action Mailbox

I am using Action Mailbox, to receive emails in my Rails application. When the emails contain images, I am not sure, how to save the email in the database, decode it, and then display it to the user so that it can be displayed like how we see emails in gmail etc.

I have used this code-

  class MyMailbox < ApplicationMailbox
    def process
     mail_content = mail.body.decoded
     post = Post.new(title: mail.subject, content: mail_content)
     post.save
    end
   end

This works well for emails which don't have any images etc. But I want a way to save emails with images. This is apparently to be done by using the different parts of the multipart email, but i am not sure how to proceed.

Finally, I want to display those emails with original look. Let me know how to proceed here. Thanks.

Upvotes: 2

Views: 744

Answers (1)

Caleb Faruki
Caleb Faruki

Reputation: 2735

Before you attempt to handle images in the body of an email, you should first check if the email body is multipart or not:

# handle multipart message
body = mail.parts.present? ? mail.parts[0].body.decoded : mail.decoded

What does a multipart email look like? Something like this:

MIME-Version: 1.0
Date: Mon, 8 Nov 2021 19:43:58 +0100
References: <....mail>
In-Reply-To: <....mail>
Message-ID: <[email protected]>
Subject: Re: Howdy
From: You <[email protected]>
To: Me <[email protected]>
Content-Type: multipart/alternative; boundary="000000000000376c4305d04b60e2"

--000000000000376c4305d04b60e2
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Here is my reply to an original message.

--000000000000376c4305d04b60e2
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div>
  <img src=3D"https://placeholder.img" />
  <img src=3D"https://placeholder.img" />
</div>

--000000000000376c4305d04b60e2--

Note that the Content-Type header of the email contains a boundary which is used to separate the email parts. Now that you can see how each part can be a different content type, you have the proper context to understand how to parse each part.

https://github.com/mikel/mail#reading-a-multipart-email

Rails uses the mail gem, so you should reference their docs to understand full functionality. But in short, you can do the following:

if mail.parts.present? && mail.parts[0].content_type == 'text/html'
  sanitized = Rails::Html::WhiteListSanitizer.new.sanitize(mail.parts[0], tags: ['img'])
  # => "\n    <img src=\"https://placeholder.img\">\n    <img src=\"https://placeholder.img\">\n\n"
  html_doc = Nokogiri::HTML(sanitized)
  html_doc.to_s
  # => "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body>\n<img src=\"https://placeholder.img\">\n  <img src=\"https://placeholder.img\">\n</body></html>\n"
end

To display the original content of the message, just save the part in its entirety. You'll have to either infer the content type store it alongside the message in your database.

Assuming you're saving an HTML message, you can use Nokogiri, which comes with Rails, to generate an HTML document.

Upvotes: 1

Related Questions