Skip to content

Instantly share code, notes, and snippets.

@sugyan
Created April 21, 2011 15:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sugyan/934815 to your computer and use it in GitHub Desktop.
Save sugyan/934815 to your computer and use it in GitHub Desktop.
#!/usr/bin/env perl
use strict;
use warnings;
use File::HomeDir;
use Path::Class 'dir';
my $home = File::HomeDir->my_home;
my @dir = sort {
$b->stat->mtime <=> $a->stat->mtime
} dir("$home/Library/Application Support/MobileSync/Backup")->children;
my $mbdb = process_mbdb_file($dir[0]->file('Manifest.mbdb'));
my $mbdx = process_mbdx_file($dir[0]->file('Manifest.mbdx'));
my $dbfile;
for my $key (keys %{ $mbdb }) {
$dbfile = $dir[0]->file($mbdx->{$mbdb->{$key}{start_offset}})->stringify;
}
die unless $dbfile;
print "dbfile: $dbfile\n";
sub process_mbdb_file {
my ($mbdb) = @_;
my $fh = $mbdb->openr;
$fh->binmode;
my $buffer;
$fh->read($buffer, 4);
die if $buffer ne 'mbdb';
$fh->read($buffer, 2);
my $offset = 6;
my $data = +{};
while ($offset < $mbdb->stat->size) {
my $fileinfo = +{};
$fileinfo->{start_offset} = $offset;
$fileinfo->{domain} = getstring($fh, \$offset);
$fileinfo->{filename} = getstring($fh, \$offset);
$fileinfo->{linktarget} = getstring($fh, \$offset);
$fileinfo->{datahash} = getstring($fh, \$offset);
$fileinfo->{unknown1} = getstring($fh, \$offset);
$fileinfo->{mode} = getint($fh, 2, \$offset);
$fileinfo->{unknown2} = getint($fh, 4, \$offset);
$fileinfo->{unknown3} = getint($fh, 4, \$offset);
$fileinfo->{userid} = getint($fh, 4, \$offset);
$fileinfo->{groupid} = getint($fh, 4, \$offset);
$fileinfo->{mtime} = getint($fh, 4, \$offset);
$fileinfo->{atime} = getint($fh, 4, \$offset);
$fileinfo->{ctime} = getint($fh, 4, \$offset);
$fileinfo->{filelen} = getint($fh, 8, \$offset);
$fileinfo->{flag} = getint($fh, 1, \$offset);
$fileinfo->{numprops} = getint($fh, 1, \$offset);
$fileinfo->{properties} = +{};
for (1 .. $fileinfo->{numprops}) {
my $key = getstring($fh, \$offset);
my $value = getstring($fh, \$offset);
$fileinfo->{properties}{$key} = $value;
}
# 必要なのはこれが含まれているものだけ
if ($fileinfo->{filename} eq 'Library/Caches/locationd/consolidated.db') {
$data->{$fileinfo->{start_offset}} = $fileinfo;
}
};
return $data;
}
sub process_mbdx_file {
my ($mbdx) = @_;
my $fh = $mbdx->openr;
$fh->binmode;
my $buffer;
$fh->read($buffer, 4);
die if $buffer ne 'mbdx';
$fh->read($buffer, 2);
my $offset = 6;
my $filecount = getint($fh, 4, \$offset);
my $data = +{};
while ($offset < $mbdx->stat->size) {
$fh->read($buffer, 20);
$offset += 20;
my $file_id = unpack("H*", $buffer);
my $mbdb_offset = getint($fh, 4, \$offset);
my $mode = getint($fh, 2, \$offset);
$data->{$mbdb_offset + 6} = $file_id;
}
return $data;
}
sub getint {
my ($fh, $size, $offset) = @_;
$fh->read(my $buffer, $size);
$$offset += $size;
return oct('0x' . unpack("H*", $buffer));
}
sub getstring {
my ($fh, $offset) = @_;
my $buffer;
$fh->read($buffer, 2);
$$offset += 2;
my $unpacked = unpack('H*', $buffer);
return '' if $unpacked eq 'ffff';
my $length = oct("0x${unpacked}");
$fh->read($buffer, $length);
$$offset += $length;
return $buffer;
}
#!/usr/bin/env perl
use strict;
use warnings;
use Config::Pit;
use Data::Section::Simple;
use Furl;
use List::Util 'shuffle';
use Text::Xslate;
my $dbfile = shift or die;
my @data = ();
for my $table (qw/WifiLocation CellLocation/) {
my $result = qx{ sqlite3 '$dbfile' 'SELECT Timestamp, Latitude, Longitude FROM $table;' };
for my $row (split /\n/, $result) {
my @col = split /\|/, $row;
$col[0] += 31 * 365.25 * 24 * 60 * 60;
push @data, \@col;
}
}
@data = shuffle(@data);
my $token = get_token();
my $tableid = create_table($token);
for (1 .. 20) {
warn $_;
my @queries = ();
for (1 .. 500) {
my $record = shift @data;
push @queries, qq{INSERT INTO $tableid (timestamp, location) VALUES ($record->[0], '$record->[1],$record->[2]')};
}
my $query = join(';', @queries);
insert_rows($token, $tableid, $query);
warn 'insert ok';
sleep 1;
}
my $tx = Text::Xslate->new(
path => [
Data::Section::Simple->new()->get_data_section(),
],
);
print $tx->render('tracker.tx', { tableid => $tableid });
sub get_token {
my $conf = pit_get('google.com', require => {
username => 'google user name',
password => 'password',
});
my $furl = Furl->new;
my $url = 'https://www.google.com/accounts/ClientLogin';
my $res = $furl->post($url, [], [
Email => $conf->{username},
Passwd => $conf->{password},
accountType => 'GOOGLE',
service => 'fusiontables',
]);
die unless $res->is_success;
my ($token) = (split /\n/, $res->content)[2] =~ /^Auth=(.*)$/;
return $token;
}
sub create_table {
my ($token) = @_;
my $furl = Furl->new;
my $url = 'https://www.google.com/fusiontables/api/query';
my $res = $furl->post($url, [
Authorization => "GoogleLogin auth=$token",
'Content-Type' => 'application/x-www-form-urlencoded',
], [
sql => q{CREATE TABLE tracker (location: Location, timestamp: DATETIME)},
]);
die unless $res->is_success;
my $tableid = (split /\n/, $res->content)[1];
return $tableid;
}
sub insert_rows {
my ($token, $tableid, $query) = @_;
my $furl = Furl->new;
my $url = 'https://www.google.com/fusiontables/api/query';
my $res = $furl->post($url, [
Authorization => "GoogleLogin auth=$token",
'Content-Type' => 'application/x-www-form-urlencoded',
], [
sql => $query,
]);
die unless $res->is_success;
}
__DATA__
@@ tracker.tx
<html>
<head>
<title>iPhone Tracker</title>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.5.2");</script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function initialize() {
var latlng = new google.maps.LatLng(36, 140);
var options = {
zoom: 6,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(
document.getElementById("map_canvas"), options
);
var layer = new google.maps.FusionTablesLayer(<: $tableid :>, {
map: map
});
google.maps.event.addListener(layer, 'click', function(e) {
var date = new Date(e.row.timestamp.value * 1000);
var div = $(e.infoWindowHtml);
div.append('<br>').append(date.toLocaleString());
e.infoWindowHtml = div.html();
});
}
</script>
</head>
<body onload="initialize();">
<div id="map_canvas" style="width:100%; height:100%"></div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment