#!/usr/bin/perl -w # # # unravel.perl # # # Use at your own risk, blah blah blah, seriously. # # # Redistribution allowed, but you must include this text: # # # Questions/comments/patches to masque /at/ pobox.com BEGIN { die "\nThis script is for Mac OS X only.\n\n" unless $^O eq 'darwin'; } use strict; use constant DEBUG => 0; use Data::Dumper; use File::Path qw:mkpath:; use Foundation; $|++; # This should be a heredoc, but I'm tired and this has grown slowly. warn "unravel v1: Expands a directory of iPhone files to ~/iPhone/Backup, or directories of your choosing.\n"; warn "Questions/comments/patches to masque".chr(64)."pobox.com\n"; warn "This is intended to be run from WITHIN your iPhone's backup directory.\n"; warn "Most likely, that's ~/Library/Application Support/MobileSync/SomeReallyLongStringHere.\nThis is a quick hack, not a full-featured application.\n\nUse at YOUR OWN RISK. No whining.\n\n"; my $backup_directory = shift || '.'; my $output_directory = shift || $ENV{HOME}.'/iPhone/Backup'; parse_and_write( get_file_list($backup_directory) ); # ...that's all, folks. # # sub get_file_list # Takes a path argument, returns an arrayref chock full of .mdbackup files. Hopefully. sub get_file_list { my @backup_files = <$backup_directory/*.mdbackup>; warn "Files: " . Dumper \@backup_files if DEBUG; die "No iPhone files found at $backup_directory.\n" unless @backup_files; return \@backup_files; } # # sub parse_and_write # Takes a listref argument. Does a bunch of irritatingly simple things. # Outputs (or updates without deletion) a tree of backed up files from your phone. sub parse_and_write { my $file_count; # Purely for your entertainment. my $files = shift; ref $files eq "ARRAY" or die "parse_and_write requires a single arrayref argument."; for my $pfile (@$files) { my $plist = NSDictionary->dictionaryWithContentsOfFile_($pfile); die "'$pfile' failed to load as a plist file" unless defined $pfile and $pfile; my $path = $plist->objectForKey_("Path")->UTF8String or die "No output path for $pfile!"; my $data = $plist->objectForKey_("Data"); warn "Processing $pfile, writing to $output_directory/$path...\n" if DEBUG; prep_path($path); open ( CONTENTS, ">", "$output_directory/$path" ) or die "Unable to open $output_directory/$path for writing: $!\n"; print $plist->description()->UTF8String if DEBUG > 1; # Okay, there's GOT to be a better way to do this. If someone's got the magic # $data->getMagicAlreadyPackedRawBinaryGoodness call up their sleeve, let me know. my $buffer = $data->description()->UTF8String; $buffer =~ s/^<(.*)>$/$1/; # Slow and clunky, but hey. $buffer =~ tr/ //d; $buffer = pack "H*", $buffer; print CONTENTS $buffer; close CONTENTS; $file_count++; print "."; } print "\n\n$file_count files expanded to $output_directory.\n"; } # # End sub parse_and_write sub prep_path { my $path = shift; $path =~ s|/[^/]+$||; mkpath "$output_directory/$path" unless -d "$output_directory/$path"; # Will croak on failure. } # # End sub prep_path