#! /usr/bin/perl
use LWP::UserAgent;
use HTTP::Request;
use IPC::Open2;
use XML::DOM;
use POSIX;

use strict;
use warnings;
no warnings qw(once);

my $ua = LWP::UserAgent->new;
push(@LWP::Protocol::http::EXTRA_SOCK_OPTS, SendTE => 0);
$ua->agent('Dalvik/1.2.0 (Linux; U; Android Froyo Build/MASTER)');

my $requestid = recognize($ua, $ARGV[0]);
lookup($ua, $requestid);

sub recognize {
	my ($ua, $filename) = @_;
	my $amr;
	{
		local $/ = undef;
		$amr = `cat $filename`;
	}

	my @parts = (
		language => make_part('en_US'),
		service => make_part('cn=CH,cn=V3,cn=SmartClub,cn=ShazamiD,cn=services'),
		applicationIdentifier => make_encrypted_string('ShazamId_SmartPhone_Gamma__1.2'),
		deviceId => make_encrypted_string('1234567812345678'),
		deviceModel => make_encrypted_string('Nexus_One'),
		userAgent => make_encrypted_string('Mozilla/5.0_(Linux;_U;_Android_1.0;_en-us;_generic)_AppleWebKit/525.10+_(KHTML,_like_Gecko)_Version/3.0.4_Mobile_Safari/523.12.2'),
		tagDate => make_encrypted_string(POSIX::strftime("%Y-%m-%dT%H:%M:%S", localtime)),
		cryptToken => make_encrypted_string('A220B36F'),
		sample => make_encrypted_part($amr, 'janet.3gp.amr', 'application/octet-stream'),
	);

	my $res = $ua->post(
		'http://goog.shazamid.com/orbit/DoRecognition1',
		\@parts,
		'content-language' => 'en_US',
		'Content_Type' => 'multipart/form-data; boundary=A3r_ISAAC_eQeY2Bh');
	 
	if (!$res->is_success) {
		die $res->status_line;
	}

	my $parser = XML::DOM::Parser->new;
	my $doc = $parser->parse($res->content);
	my $ids = $doc->getElementsByTagName("requestId");
	if ($ids->getLength == 0) {
		print "Didn't recognize song\n";
		exit;
	}
	return $ids->item(0)->getAttribute('id');
}

sub lookup {
	my ($ua, $requestid) = @_;

	my @parts = (
		language => make_part('en_US'),
		service => make_part('cn=CH,cn=V3,cn=SmartClub,cn=ShazamiD,cn=services'),
		applicationIdentifier => make_encrypted_string('ShazamId_SmartPhone_Gamma__1.2'),
		deviceId => make_encrypted_string('1234567812345678'),
		deviceModel => make_encrypted_string('Nexus_One'),
		userAgent => make_encrypted_string('Mozilla/5.0_(Linux;_U;_Android_1.0;_en-us;_generic)_AppleWebKit/525.10+_(KHTML,_like_Gecko)_Version/3.0.4_Mobile_Safari/523.12.2'),
		requestId => make_encrypted_string($requestid),
		coverartSize => make_encrypted_string('304'),
		artistartX => make_encrypted_string('81'),
		artistartY => make_encrypted_string('0'),
		cryptToken => make_encrypted_string('A220B36F'),
	);

	my $res = $ua->post(
		'http://goog.shazamid.com/orbit/RequestResults1',
		\@parts,
		'content-language' => 'en_US',
		'Content_Type' => 'multipart/form-data; boundary=A3r_ISAAC_eQeY2Bh');
	 
	if (!$res->is_success) {
		die $res->status_line;
	}

	print $res->content;
}

sub make_part {
	my ($content, $filename, $mime_type) = @_;
	if (defined($mime_type)) {
		return [ undef, $filename, 'Content' => $content, 'Content-Type' => $mime_type ];
	} else {
		return [ undef, $filename, 'Content' => $content ];
	}
}

sub make_encrypted_string {
	my ($content, $filename, $mime_type) = @_;
	if (length($content) % 8 == 0) {
		$content .= "\0\0\0\0\0\0\0\0";
	}
	my $enc = '#0x' . join("", map { sprintf "%02X", $_ } (unpack("W*", encrypt($content))));
	return make_part($enc, $filename, $mime_type);
}

sub make_encrypted_part {
	my ($content, $filename, $mime_type) = @_;
	my $enc = encrypt($content);
	return make_part($enc, $filename, $mime_type);
}
	
sub encrypt {
	my $content = shift;
	my $blocks = (length($content) + 7) / 8;
	my $ret = "";
	local $/ = undef;

	my $pid = IPC::Open2::open2(\*CHLD_OUT, \*CHLD_IN, './encrypt-ice');
	for my $i (0..($blocks-1)) {
		my $block = substr($content, 8*$i, 8);
		print CHLD_IN $block;
	}

	close CHLD_IN;
	$ret = <CHLD_OUT>;
	close CHLD_OUT;
	return $ret;
}
