We have used Vonage as our phone provider for a couple years now and have loved the service. One of the many features they add at no extra charge is the ability to have all voicemail messages sent as an email attachment. Unfortunately, they only send these attachments in WAV format, which means they take up a ton of room.
I like to store all voicemails I receive in case I need them later, and so I was looking for a way to shrink those emails down. I found this post describing how to do just that using Perl and LAME to convert the attachments to MP3.
Unfortunately, that script didn’t work quite right for me. I have cleaned it up a bit and posted it here for anyone interested.
You need to call this script from your MTA as an “external filter”. The details of how to do this depend on your implementation. It will read the current email (with WAV attachment) on STDIN, and write the email with an MP3 attachment on STDOUT. It will also change the subject line to include the Caller ID info of the caller.
Up until sometime towards the end of 2005, I used the popular LAME MP3 encoder which is available for most Linux distros. However, Vonage then changed the format of their WAV files to use an algorithm called “CCITT μ-Law”, which LAME is unable to handle. So I switched to a new program called SOX, which bills itself as the “swiss army knife of sound processing programs”.
Requirements:
- SOX
- Perl with MIME::Parser module
As written, this script will convert Vonage WAV files to MP3. Converting to Ogg Vorbis or any other standard instead should be rather simple; simply read the SOX man page or website.
In the event that the incoming email does not contain an attachment, or the attachment is not WAV format, it will be written to STDOUT unchanged. In either case, the return status will be 0. But the best solution is to send only Vonage voicemail emails through this filter. Your MTA software may have a way to do server-side header checks and conditional filtering. Currently, voicemail emails come from an address which ends with “vm.vonage.com”.
This script differs from the one mentioned above in that it uses a tempfile instead of IPC::Open2. I was unable to get IPC::Open2 to work correctly, since it deadlocked when attempting to write to it. According to the author of IPC::Open2, this is a danger in using it with “streamy” programs such as LAME or SOX.
Because this script uses a tempfile for the SOX output before reading it back it, there’s a possibility of someone reading this tempfile while the script is running. This is a security problem if you have untrusted users. This isn’t an issue for me, since this is on a private server, but if it is for you (i.e. you have users which you don’t want listening to your voicemails) then be sure to create the tempfile in a directory which only your MTA has read/write access to (instead of /tmp), and/or use “umask” to ensure that only your MTA can read/write the file.
UPDATE 9/15/2006: I have also written a Perl script to grab Caller ID information from Switchboard.com, which can be used to add this info to your emails as well.
#!/usr/bin/perl
use MIME::Parser;
$parser = new MIME::Parser;
$parser->output_to_core(1);
$email = $parser->parse(\*STDIN);
$time = time();
$tmp_file = "/tmp/wavmailtomp3-$time.$$.tmp.mp3";
$attachment = $email->parts(1);
if (!defined $attachment or $attachment->effective_type() ne 'audio/wav') {
# no attachment or non-WAV attachment; print original mail and quit
$email->print;
exit(0);
}
open(PIPE, "|/usr/bin/sox -t .wav - $tmp_file");
print PIPE $attachment->bodyhandle->as_string;
close PIPE;
if( open(PIPE,"<:bytes",$tmp_file) ) {
if( $fh = $attachment->bodyhandle->open("w") ) {
my $buffer;
while( read(PIPE, $buffer, 10240) > 0 ) { ### read chunks of 10KB at a time
$fh->print($buffer);
}
$fh->close;
} else {
warn "Error opening MIME part for writing: $!";
}
close PIPE;
} else {
warn "Error opening mp3 file $tmp_file: $!";
}
$attachment->head->replace("Content-type","audio/mpeg");
$attachment->head->mime_attr("content-type.name"=>"voicemail.mp3");
$email->parts(0)->bodyhandle->as_string =~ m/From: (.*)/m;
$from = $1 || 'UNKNOWN';
$b=$email->head->replace("Subject","$from -- new voicemail");
$email->sync_headers(Length=>"COMPUTE");
$email->print;
unlink($tmp_file);