#!/usr/bin/php * @category Utility * @filesource * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License * @package interface * @uses Console_Getopt * @uses Console_Color * @version $Id$ (Used by SVN to automatically stamp files with their version #) * @copyright 2007, Carlos Averett */ // Pear require_once ('Pear/PEAR.php'); require_once ('Pear/Console/Getargs.php'); require_once ('Pear/Console/Color.php'); // Register command line options $config = array ( 'extract' => array ( 'short' => 'e', 'min' => 0, 'max' => 0, 'desc' => 'Extract files from [indir]' ), 'compress' => array ( 'short' => 'c', 'min' => 0, 'max' => 0, 'desc' => 'Compress files from [indir] into [outdir]' ), 'indir' => array ( 'short' => 'i', 'min' => 1, 'max' => 1, 'desc' => 'Specify the input directory for compressing or extracting', ), 'outdir' => array ( 'short' => 'o', 'min' => 1, 'max' => 1, 'desc' => 'Specify the output directory for compressing or extracting', ), 'license' => array ( 'short' => 'l', 'max' => 0, 'desc' => 'Display the license for this program' ), 'help' => array ( 'short' => 'h', 'max' => 0, 'desc' => 'Display this help' ) ); // ===== // Handle license seperately // ===== foreach ($_SERVER['argv'] as $arg) { if ($arg == '-l' || $arg == '--license') { // Dump a copy of the LGPL to stdin exit (file_get_contents ('LICENSE')); } } $cmd = $_SERVER['argv'][0]; $args =& Console_Getargs::factory ($config); $header = Console_Color::convert ( "%riPhone Extraction Utility%n\n" . "%r=========================%n\n" . "Example: %p$cmd%n %g--extract%n %g--outdir=%n%b\"out\"%n \\\n" . " %g--indir=%n%b\"~/Library/Application Support/MobileSync/Backup/*\"%n \\\n" . "\n%rOptions:%n\n" ); if (PEAR::isError($args)) { if ($args->getCode() === CONSOLE_GETARGS_ERROR_USER) { echo Console_Getargs::getHelp($config, $header, $args->getMessage()) . "\n"; } else if ($args->getCode() === CONSOLE_GETARGS_HELP) { echo Console_Getargs::getHelp($config, $header)."\n"; } exit (); } // Sanity checking $indir = $args->getValue ('indir'); $outdir = $args->getValue ('outdir'); $compress = $args->getValue ('compress'); $extract = $args->getValue ('extract'); $noError = false; if (!is_dir ($indir)) { usage_error ('Invalid indir.'); } else if (!is_dir ($outdir)) { usage_error ('Invalid indir.'); } else if ((!$compress && !$extract) || ($compress && $extract)) { usage_error ('You must either select compression or extraction.'); } else if ($indir == $outdir) { usage_error ('Indir and outdir must differ.'); } else { $noerror = true; } if (!$noerror) { exit(); } // ===== // Meat of the program // ===== // Make sure we always have the directory with a '/' on the end $indir = dirname ($indir . '/removeme.txt') . '/'; $outdir = dirname ($outdir . '/removeme.txt') . '/'; if ($extract) { if (!file_exists ($indir . 'Manifest.plist')) { fatal_error ('Unable to read Manifest'); } else if (!file_exists ($indir . 'Info.plist')) { fatal_error ('Unable to read Info.plist'); } // Extract the manifest $manifest = file_get_contents ($indir . 'Manifest.plist'); $regex = '/(.*)<\/data>/smiU'; $matches = array (); preg_match_all ($regex, $manifest, $matches); $matches = $matches[1]; if (count ($matches) != 3) { fatal_error ('Invalid Manifest.plist'); } $authdata = trim ($matches[0]); $authsignature = trim ($matches[1]); $data = trim ($matches[2]); // Verify AuthData if (base64_decode ($authdata) != 'Forty Two') { fatal_error ('Invalid Manifest.plist (AuthData)'); } // Verify AuthSignature if (base64_sign ($data) != $authsignature) { fatal_error ('Invalid Manifest.plist (AuthSignature)'); } // Extract the manifest $manifest = base64_decode ($data); if (!$manifest) { fatal_error ('Invalid Manifest.plist (Data)'); } $manifest_file = $outdir . 'manifest.xml'; file_write ($manifest_file, $manifest); bpl2xml ($manifest_file); } function bpl2xml ($filename) { $res = `plutil -convert xml1 $filename`; } function file_write ($filename, $filedata) { $fp = fopen ($filename, 'w'); if (!$fp) { fatal_error ('Unable to open ' . $filename . ' for writing'); } fputs ($fp, $filedata); fclose ($fp); } function base64_sign ($indata) { return base64_encode (pack ('H*', sha1 (base64_decode ($indata)))); } function fatal_error ($message) { exit (Console_Color::convert ("%r$message%n\n")); } function usage_error ($message) { global $args, $config, $header, $first_error; if (!$first_error) { echo Console_Getargs::getHelp($config, $header, Console_Color::convert ("%r$message%n\n")); } else { echo (Console_Color::convert ("%r$message%n\n")); } }