Created
April 21, 2011 15:50
-
-
Save sugyan/934815 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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