Thursday, November 29, 2007

Using ruby to get at your mac unix mail

The Problem


I run a mac at home. I use it for music, photos, web development, syncing my iphone, etc. I've got a number of backup jobs and maintenance tasks set up running from cron. When cron runs jobs the output goes into mail messages in /var/mail/<user>. This is where unix mail is kept by default on a mac. You can get at it from the terminal by using the mail command line interface. That's ok but it's not where I look for all my other e-mail. I use gmail for my primary inbox. It would be ideal to have my unix mail post to my gmail account.

The Options


  1. I could set up a full mail server and open POP ot IMAP and have gmail suck over the messages. But, that seems like it could lead to a lot of late nights messing with things like MX DNS records and compiling bits of server code. Also, I'd have to start over when I finally get around to installing OS X 10.5. Ideally, I'd like to do something in my user account that doesn't mess with my machines default config.

  2. I could get procmail at procmail.org and compile it and get it to run just under my user account. Have you looked over the configuration file format of procmail? It's cryptic. I learned it once back in college when I set it up on my university account but there must be something simpler.

  3. I'll just forward the mail! But I can't. Unless you modify your systems settings it can't route mail anywhere but to local accounts. The key to my solution came from looking at the procmail config. To set up procmail on a single user account you use the .forward file to get your mail passed to it. Great! What else can I use .forward for? Well, you can set up your .forward file to pass your mail to a ruby program, any program really, but I'm on the ruby bandwagon at the moment.

The Solution


It's simple, it's straight forward, it doesn't involve building and configuring servers, it uses ruby, what more could you want?

The .forward file


\carl
"|/usr/local/bin/ruby /Users/carl/development/ruby/email/unixmail.rb"

The first line says deliver the mail to the carl user account and don't expand forwarding any further. This makes sure I still get the mail in my unix mailbox. It's not necessary but until I handle the remaining issues listed below I'm being paranoid.

The second line says to pipe the mail message to my ruby script.

The ruby script


unixmail.rb

require 'net/imap'
require 'time'

user = 'xxxxxx@gmail.com'
password = 'xxxxxx'

message = "$quot;
while gets
message << $_
end
imap = Net::IMAP.new('imap.gmail.com', 993, true)
imap.login(user, password)
imap.append('inbox', message, [], Time.now)

That's it. Collect the whole message, login to gmail, put the message.

The Leftovers


As a follow up, I need to get back to this script and make sure it deals well if it can't get to the gmail imap server. Obviously I need to handle the exception raised but haven't decided what I want to do at that point. Or I should look into what mail does if something in the .forward file fails, does it retry?

4 comments:

pate said...

Carl, you're one of the winners for the holiday blogging contest. Shoot me an email and we can coordinate getting your prize to you.

Carlism said...

Originally, I had a line in this script to perform an imap.disconnect. This was causing me some problems and backing up mail in postfix. It seems gmail imap wasn't responding to the disconnect and it was hanging my script.

Anonymous said...

Wouldn't it just be easier to set MAILTO=username@gmail.com at the top of your crontab?

Carlism said...

Ah, yes, that would fix any mail coming from cron. But it wouldn't get any other mail showing up in my mailbox.