A colleague of mine recently asked if I knew of any applications that would allow him to save and backup voicemail off his iPhone. He had found The Missing Sync, but frankly $40 is a bit steep just to save voicemail; he didn't need to do anything else.
A quick search turned up an article describing how to save voicemail from a jailbroken iPhone, but nothing so simple for a non-jailbroken phone. The article did, however, drop a clue: voicemails are stored in AMR format.
iPhone backup files are conveniently all stored in a directory named for the phone's ID in "~/Library/Application Support/MobileSync/Backup/". The files all have a .mddata or .mdinfo extension. Could any of them contain an AMR file? According to the AMR format RFC, plain, single-channel AMR files should start with the ASCII string "#!AMR\n". A simple grep revealed that several files in fact contained the AMR header. As it turns out, Apple has politely stored each voicemail in its own, separate file – no file carving is necessary to extract the voicemail.
It's so simple to find the voicemail in an iPhone backup, it can be done with a bash shell one-liner (credit to Seren in the comments for finding a slightly more compatible "find" syntax):
pushd ~/Library/Application\ Support/MobileSync/Backup ; for I in `find . -name *.mddata -exec grep -la '#\!AMR' {} \;` ; do cp $I $OLDPWD/`basename -s mddata $I`amr ; done ; popd
That'll copy all the voicemail for the current user's iPhone(s) to the current directory, named with a .amr extension. (Both QuickTime and iTunes can play .amr files.) Note that this can take a minute or two – so be patient for at least a couple minutes if it looks like nothing is happening.
For those less familiar with bash, I'll break down the one-liner a bit:
pushd ~/Library/Application\ Support/MobileSync/Backup ; \<br />
for I in `find . -name *.mddata -exec grep -la '#\!AMR' {} \;` ; \<br />
do cp $I $OLDPWD/`basename -s mddata $I`amr ; \<br />
done ; \<br />
popd
1. pushd changes the current directory and stores the previous current directory in the $OLDPWD environment variable. The "Application Support" directory has an unfortunate space in it, which makes it a bit tougher to throw around pathnames using it without getting the escaping just right – I didn't want to bother with it.
2. The find command looks for only those files that end in .mddata to save a small amount of time (there are no voicemails in .mdinfo files) and then passes each to grep. grep is used to look for files with the AMR header; the -la options are used to quit looking through a file after the first match (again, this is for a small speed improvement – the AMR header ends with a newline and must be the first thing in an AMR file, which means grep is able to move on quicker on the few files that match) and to treat files as text.
3. Use basename to strip off the .mddata filename extension so we can make a copy of the .mddata file with a .amr extension.
4. End of loop.
5. popd restores the current directory to the original working directory.
There are tons of different ways of doing the same thing; I tried for a little while to find one that was functionally equivalent (using xargs rather than a for-loop and bash substring manipulation rather than basename, etc.) but would fit in a Twitter update, but alas, I'm not a bash expert, and failed. If you can come up with a shorter one-liner, feel free to leave it in the comments. It needs to be general (it can't assume a specific iPhone ID, for example), and needs to assume a very large number of files in the backup directory (such that "grep '#\!AMR' *" would likely fail due to "*" being too long; that happens to be the case for me). Other than that, have fun.