Receiving Email with Action Mailer

Problem

Contributed by: Christian Romney and Diego Scataglini

You want to receive and process email from within your Rails application.

Solution

Action Mailer makes it simple to process incoming mail. The real trick is getting the mail to your Rails application. For this recipe, you'll need an empty Rails application and your database connectivity preconfigured. Be warned that this recipe requires a little system administration, so make certain you've got shell access into the server, and sufficient permissions to run your Rails application and install software.

If it's not already installed, download, compile, and install getmail. New versions of getmail are released periodically; make sure to get the latest version from .

$ wget http://pyropus.ca/software/getmail/old-versions/getmail-4.6.4.tar.gz
$ tar xvzf getmail-4.6.4.tar.gz
$ cd getmail-4.6.4
$ ./configure
$ make 
$ sudo make install

Next, create or modify a getmailrc file in the .getmail directory under your home folder. Create the directory if it doesn't already exist. Here are the contents of the getmailrc file; replace the server, username, password, and paths with values appropriate for your system:

~/.getmail/getmailrc:

[retriever]
type = MultidropPOP3Retriever server = pop3.example.com
username = yourUserName
password = secret
envelope_recipient = x-envelope-to:1
[destination]
type = MultiDestination destinations = ("[maildir]", "[rails]")
[maildir]
type = Maildir path = /users/home/yourUserName/Maildir/
[rails]
type = MDA_external path = /usr/bin/env arguments = ("RAILS_ENV=production", 
 "sh", "-c", 
 "cd /path/to/your/app; /usr/local/bin/ruby 
 script/runner 'Importer.receive(STDIN.read)'")
[options]
# delete messages on server delete = On

Now, create a crontab that invokes getmail every five minutes:

$ crontab -e

0,5,10,15,20,25,30,35,40,45,50,55 * * * * "getmail"

We'll create a backup of copy of every email in a Maildir folder just in case the Rails importer fails for any reason:

$ mkdir ~/Maildir ~/Maildir/tmp ~/Maildir/new ~/Maildir/cur

With the infrastructure bits completed, you can finally turn your attention to the Rails application. All you need is an Action Mailer class that will handle the import:

$ ruby script/generate mailer Importer

class Importer < ActionMailer::Base 
 def self.receive(email) 
 # Do something interesting with the email here
 end end 

Discussion

The solution uses getmail, an open source fetchmail replacement written in Python that is extremely reliable. It supports external MDA (program) delivery, POP3, IMAP4, Maildir, Mboxrd, domain/multidrop mailboxes, mail filtering and many, many other features.

In the retriever part of getmailrc we used MultidropPOP3Retriever. This is if you want to fetch emails for multiple email addresses and you create a catchall POP3 account (e.g.,*@example.com).

If you have a single email account to fetch, alter the type line in gmailrc file as follows:

[retriever]
type = SimplePOP3Retriever

In this solution, getmail fetches the email messages and delivers them to two recipients. The first delivery creates a backup copy of all emails fetched in a Maildir folder, while the second delivery runs the Rails script/runner, which calls the Importer class. From the moment the Importer class receives the email, you can process it as you would with a normal text or email file. The beauty of using Action Mailer is that the email is parsed for you, and you can access all its properties in a object-oriented way. For example, assuming you've got a Feedback model defined, you can save the salient parts of the incoming mail to your database:

class Importer < ActionMailer::Base 
 def self.receive(email) 
 # Save the received email in our database using
 # the Mail ActiveRecord Model
 Feedback.create(:subject => email.subject, 
 :body => email.body, 
 :sender => email.from,
 :received_at => Time.now) 
 end end

Don't forget that many more things can be delivered through email than just text. Images, MMS, and SMS from a mobile phone can all be delivered through email. For a real-life example, check out the code of Markaboo, an open source project written in Rails that accepts input from emails, SMS, and MMS ().

See Also