I spent the weekend getting a little application to play nicely with emails being sent to it. As receiving emails with Rails is something that is not written about as much as sending emails, I thought I would share my solution.
This tutorial will show you how to receive emails into your application, store them in the database and forward the contents to multiple recipients.
The first problem of course is getting the emails into your application. I have root access to my server, so my solution uses Postfix. If you don’t have root access, you can use POP and there are a number of examples out there. On with the instructions…
As I am hosting a number of domains on the same box, I set up my main.cf file as follows:
virtual_alias_domains = mydomain.com
virtual_alias_maps = hash:/etc/postfix/virtual
I then set up my virtual file (which you may need to create) with a single line:
@mydomain.com rails_mailer
This will intercept any emails sent to mydomain.com and pass them to the rails_mailer alias, which is created in your alias file. Mine is located in /etc/aliases: yours might be in /etc/postfix/aliases. Add to the bottom of your aliases file:
rails_mailer: "|curl -F mail='<-' http://mydomain.com/emails"
This uses curl to post the mail as form data to the given url. The email contents will be stored inside the mail parameter. The pipe character at the start indicates that this is a shell command and not a directory.
To apply all these changes:
sudo postmap /etc/postfix/virtual
sudo newaliases
sudo /etc/init.d/postfix reload
To check that this is working, change into your /var/log directory and use:
tail -f mail.info
Then send an email to any address @mydomain.com and you should see it received in your mail log and passed to your Rails app. If you look in your app’s logs, you should see it spitting out a 500 error as there is no code to handle the email…yet.
On to the Ruby. Create an Emails controller and define a create method:
def create
if params[:mail]
Notifier.receive(params[:mail])
end
end
This receives the contents of params[:mail] and passes it to the receive method of our notifier model.
To deal with Rails’ cross-site scripting protection, you need to add to the top of your controller:
skip_before_filter :verify_authenticity_token
Next, generate a mailer called notifier. In notifier.rb, define a method called receive(email). Using this method name automatically invokes the power of TMail. My receive method is as follows. You will need to change this to make sure you pull the information you need from the email:
def receive(email)
event_name = email.to
event_name = event_name.to_s.sub('@mydomain.com', '')
event = Event.find_by_tidy_name(event_name)
body = email.body
address = Array.new
event.attendees.each do |attendee|
address << attendee.email.to_s
end
from = email.from.to_s
new_email = event.emails.new
new_email.from = email.friendly_from
new_email.body = body
new_email.save
Notifier.deliver_event_email(event, from, address, body)
end
Before running through this code, I should explain that when people use the app, they receive a dedicated email address in format myeventname@mydomain.com.
This code does two things. Firstly, it extracts the following data from the email: the name of the event, the body of the email and who the email is from. It then constructs a new email that belongs to the event and saves to the database. It then invokes another method in our notifier.rb to forward the email body to the individuals who are attending the event.
def event_email(event, from, address, body)
setup_email(from)
@bcc = address
@subject += "#{event.name}"
@body[:body] = body
end
In terms of the application that I wrote this for, I hope to launch it within the next week. If you’ve got any questions or run into any problems, leave a comment and I’ll try and help.