Tuesday, May 5, 2009

How to automatically forward email from Exchange without loosing headers

UPDATE: I've now created a service to make this much easier to forward email. If you are interested in this, please have a look at the service's site.

I've got a million email accounts. Every time I start work on a new client site, I get given yet another email account. Its a pain in the butt to manage all of these, so wherever possible I forward the mail onto my main gmail account where it can get filtered, stored and searched easily.

This works great for sites with unix based email, but more often than not my clients use Microsoft Exchange for their mail. You can set up a redirecting rule to forward the email (as long as the server has been set up to allow this), but when it does so it strips off the To: and CC: headers from the message when it sends it on. For example, if Bob Log had sent a message to Me@mycompany.com, SomebodyElse@mycompany.com, when it was forwarded on to my gmail account it would still appear to come from Bob Log but the To: would just be me@gmail.com, not the original recipients. This won't work!

I've toyed with a number of solutions to this problem, including writing a bot that uses EWS to poll the server, re-form the message and send it on, but that requires a bot to run on the client's lan and may not be able to forward on the message as getting access to Exchange to send the message can sometimes be difficult.

I now have a solution that works without requiring any additional software to be running on the client's network. The solution involves forwarding the message to an intermediate account as an attachment (which preserves the headers), filtering the message back out of the attachment at the intermediate account, then sending that message on to gmail in all its original glory. To do this, you require an intermediate email account that you can use purely for the purposes of filtering the mail, which is capable of piping incoming mail to a perl filter script. Generally, these aren't that hard to come by. I happen to have a getting started plan with Cove here in Melbourne which fits the bill nicely. Its free to run (although they do have a $2 setup fee) and its servers are in Australia which is a benefit for me. If you live elsewhere, there are probably other options that would do just as well.

To set it up, there are three steps,

step 1: create the filter script
Below is included a perl script which takes an email address as a parameter, and reads a MIME encoded email on STDIN. It will look for a part with a content-type of message/rfc822 which is an embedded message, and then it will stream that message out to sendmail with the supplied email address paramater as the final destination. This file should be uploaded onto your server somewhere where the mail filter can get hold of it.

I originally wrote a script that used the MIME::Parser perl module, but I've found that most hosting providers don't have that module installed, so it was easier to just do it from scratch. I'm not a perl programmer really, nor have I spent a lot of time on this script, so it definitely could be improved, but it works!
#!/usr/bin/perl

my $recipient = $ARGV[0];
my $boundary = '';
my $endMarker;
my $partType;
my $sendmail = "/usr/sbin/sendmail -oi $recipient";

# Reads a line from STDIN, and makes sure it isn't the EOF
sub fetchLine {
my $txt = <STDIN> or die "Reached end of file prematurely";
chomp $txt;
return $txt;
}

# reads a message part, looking for an rfc822 message.  If it finds one, it
# forwards it on to the recipient.  When it finds the part end, it returns 1
# if there are more parts, or 0 if it is the end of the message
sub parsePart {
my $isMessage = 0;
my $returnCode = -1;

# First, read the headers, looking for a content type
while ((my $text = &fetchLine()) ne '') {
if ($text eq 'Content-Type: message/rfc822') {
$isMessage = 1;
open(SENDMAIL, "|$sendmail") or die "Cannot open $sendmail: $!";
}
}
# Then read the body, streaming it out if it is a message
# End the loop when we find a boundary or the end marker
while ($returnCode == -1) {
$text = &fetchLine();
if ($text eq $boundary) {
$returnCode = 1; # Meaning we still have parts to parse
} elsif ($text eq $endMarker) {
$returnCode = 0; # Meaning we are finished parsing the message
} elsif ($isMessage) {
print SENDMAIL "$text\n";
}
}
if ($isMessage) {
close(SENDMAIL);
}
return $returnCode;
}

# First, Read Headers, looking for the multi-part content type and
# boundary separator
while ((my $text = &fetchLine()) ne '') {
if($text =~ m/^Content-Type: (.*)/i) {
$nextline = &fetchLine();
$nextline =~ m/\s+boundary="(.*)"/i or die "Could not get boundary after content type";
$boundary = "--$1";
$endMarker = "--$1--";
}
# We don't care about any other headers
}

# Check to see that we have the right mimetype and a boundary
die "No boundary found" if $boundary eq '';

# Skip until the first part separator
while ((my $text = &fetchLine()) ne $boundary) {
}

# Parse the message, looking for a part with a type of message/rfc822
while (&parsePart()) {
}

exit 0;

step 2: set up the filter in cpanel (or whatever else you use)
On your server, you now need to set up mail filtering so that any incoming mail from your work account that isn't a bounced message gets sent to your filter script. In cpanel, I did this by setting up an email filter for all mail, which looked something like this:



step 3: enable forwarding of mail in Exchange
Now that you've got your email forwarding filter set up, all that remains is to set up exchange to forward any incoming mail to your filter account. You do this by selecting Rules and Alerts



And then setting up a rule that looks like below:

Be careful with what you put in your rule definition, as some rules are "client only" which means they will only run when outlook is open. As an example, I tried to make it also mark the message as read when it forwards, but that is "client only" which means the rules won't run unless outlook is open :(

Once you've got that set up, you can test. Send a message to your work email and see if it makes it through. If anything goes wrong, it should bounce with a message telling you what went wrong. One thing I did notice though is that if I send the message from gmail itself, it tends to ignore the message when it gets forwarded through as it already has a copy (in sent), so I send test messages from an alternate account just to be safe.

Okay, so in summary, its possible to forward messages on from exchange, but it requires a man in the middle to extract the message contents, and its a bit fidlly. If you like this tip, let me know.

Sunday, May 3, 2009

Could I use my iPhone to work on?

I'm an IT consultant. As a result, I spend the vast majority of my time at work doing one of the following
  1. Reading or Composing Email
  2. Reading or writing Word Documents
  3. Editing our corporate Wiki
  4. Researching stuff (or skiving off) on the Web
  5. Looking at Microsoft Project plans
  6. Very occasionally coding... very occasionally
To perform these tasks, I lug around a quite heavy laptop. Its not a particularly special laptop, but it does the job. I would like to exchange it for something lighter and easier to work with in order to save my back, especially when I ride to work. I originally thought about getting a netbook. They seem to fit the bill nicely, except for a couple of annoying things:
  1. The screen is a tad small to be using all day long
  2. The keyboard could be considered small to be using all day long.
  3. They don't really have enough grunt to do coding
The first two problems can easily be solved by using an external keyboard, mouse, and screen. I always work in offices, so it generally isn't hard to find something that I can appropriate for the purposes of working while there. The third problem is a little more tricky. One way I've thought about solving this problem is using remote desktop to a server. Given that I generally need a server to code on anyway (I do enterprise SOA work), this seems to make sense. I simply log into the server (either via ssh or VNC/RDP) and I can do anything I would have originally wanted to do on my laptop, albeit with a little more lag. A RDP server would also allow me to get to those windows only tasks that I occasionally need to do without needing to bloat my netbook with software

This sounds great, and I might just do it, but why should I carry a wee little laptop around if I'm never going to use it as a laptop. I'll use my iPhone when I'm on the road, and plug the netbook into a KVM when I'm in an office. Why not just use the iPhone? I love my iPhone, and I carry it with me everywhere. It can do most of what I need to do just as well as a netbook, but it suffers from the smallness problems even worse than a netbook does. Why couldn't Apple make a docking station for the iPhone which allows it to work with an external keyboard, mouse and screen? That way, I can carry my phone around with me, get to work, plug it into the docking station, and work directly on my phone.

I think the docking station would need the following features to be successful:
  1. Be relatively small, so that it can be transported if necessary
  2. Provide charge to the iPhone while operating
  3. Have the following connectors:
    1. 1x Power - I would prefer an integrated transformer, but a wall wart would work too
    2. 3x USB - one for keyboard, one for mouse, one spare for something else...
    3. 1x DisplayPort, or DVI, or whatever to connect up a monitor
    4. 1x Ethernet port
    5. 2x Speakers
    6. Audio jacks for external speakers and mic
    7. RCA/Composite video out, so that it can do everything a current iPod dock does
    8. IR receiver for those cute little remotes.
    9. Possibly a phone handset to allow it to be used as a phone while docked. Perhaps just a jack to allow a handset (or hands free kit) to be plugged in
  4. Provide at least 1920x1200 resolution screen - this would probably involve improving the graphics card of the iPhone
  5. Be capable of receiving calls while in the dock. If the user removes the phone from the dock to receive a call, the user's session should be saved so that he can pick up where he left off when it is re-docked. Likewise, if I pull it out of the dock in the evening, take it home, and dock it again, my session should pop straight back up
  6. Be capable of running faster (at a higher clock speed) when plugged into power. iPhones are deliberately left running at a low clock speed to conserve battery power, but when plugged in they could easily ramp up.
I realise that iPhone apps, as they currently stand, would not be suitable for use on a large screen, but they could be adapted. Alternatively, the phone side and the desktop side could be kept largely separate, and there could be dedicated desktop applications (just a port of the normal os x version) along side the mobile versions. They would still need to synchronise app data (bookmarks for example), but that wouldn't be difficult to achieve.

I don't think this is an especially original idea. I know that other people have thought about doing it for ages. I just wish we could convince Apple to produce it as a product. Here's how I think we do it: tie ins to .mac. .mac is a good service, but most people don't want to fork out for what they can get for free elsewhere. If the iPhone plus had better integration with .mac it would make it a much more compelling offering. iDisk is a perfect example. Devices with limited storage need online storage. voilla!

Ahh apple, I doubt you will ever read this, but if you do, please make this device! I'll buy two. Lots of people I know will buy them. It'll be awesome.

Saturday, May 2, 2009

Tasmanian Holiday 2009

Melissa and I just got back from our holiday in Tasmania. We spent 5 nights in Cradle Mountain, 2 in Launceston, and traveled on the Spirit of Tasmania. As long as you don't mind loosing a night, the Spirit of Tasmania is not a bad way of travelling. Below are some blurry iphonecam shots from the trip