#!/usr/bin/env perl
use strict;
use warnings;

# Take your LJ username and password as arguments;
# print to stdout an HTML page containing the userpics
# of all your friends.

use RPC::XML;
use RPC::XML::Client;
use LWP::Simple;
use HTML::TokeParser;

my $client = RPC::XML::Client->new(
	'http://www.livejournal.com/interface/xmlrpc'
);

my $username = shift @ARGV or die("Need username");
my $password = shift @ARGV or die("Need password");

my $logindata = {
	username => $username,
	password => $password,
	ver      => 1,
};

# Set @friends to contain LJ usernames of all your friends.
my @friends = get_friends_list($logindata);

local $|=1;
print STDERR "Fetching friends userpics...\n";

# Read their userpics into a hash.
my %info = %{get_friendspics(@friends)};

# Print a summary page to stdout.

print <<HTML;
<!DOCTYPE html
PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" >
<title></title>
</head>
<body bgcolor="white" text="black" link="blue" vlink="purple" alink="red">
HTML



for my $friend (keys %info) {

print <<HTML;
<br clear="all">
<h3><a href="http://www.livejournal.com/users/$friend">$friend</a></h3>
<br clear="all">
HTML

	for my $url (@{$info{$friend}}) {
		print "<img src=\"$url\">\n"; 
	}
}

print <<HTML;
</body>
</html>
HTML

# Done.
exit 0;

# Take a hashref of login data, return an array of friends usernames.

sub get_friends_list {
	my $logindata = shift;
	my $response = $client->send_request(
		RPC::XML::request->new('LJ.XMLRPC.getfriends',$logindata));
	die_if_rpc_fault($response);

	my @friends = map { $_->{'username'} } 
			@{$response->value->{'friends'}};
	return @friends;

}

# Take an RPC::XML response, and check if it contains a fault.
# If so, print some diagnostics and die.

sub die_if_rpc_fault {
	my $response = shift;
	if ($response->is_fault) { 
		die("Unable to login:\n".
		"faultString=\"". $response->value->{'faultString'} ."\"\n".
		"faultCode=\"". $response->value->{'faultCode'} ."\"\n");
	}
}

# Take an LJ username; fetch their userpics page,
# screenscrape it for their userpics.
# Return an array containing userpic URLs.

sub get_userpics {

	my $username = shift;

	my $picsurl = "http://www.livejournal.com/allpics.bml?user=$username";
	my $picspage = get($picsurl) or die("Unable to fetch $picsurl");
	my $stream = HTML::TokeParser->new(\$picspage);
	my $class='';
	my @urls = ();

	# Find <span class="heading">.

	while (	($class ne 'heading') &&
		(my $nexttag = $stream->get_tag("span"))) {
		my ($tag,$attr,$attrseq,$text) = @$nexttag;
		if (defined($attr)) {
			$class = $attr->{'class'} || '';
		}
	}

	# Now find all img tags.

	while ( my $nexttag = $stream->get_tag("img")) {
		my ($tag,$attr,$attrseq,$text) = @$nexttag;
		if (defined($attr)) {
			if (defined(my $url = $attr->{'src'})) {
				if ($url =~ /userpic.livejournal.com/) {
					push @urls,$url;
				}
			}
		}
	}

	return @urls;
}

# Take a list of Lj usernames.
# Return a reference to a hash, each key of which is an LJ username.
# Each value is a reference to an array containing the URLs
# of their userpics.

sub get_friendspics {
	my %info = ();

	map {
		print STDERR "Fetching $_ ...\n";
		$info{$_} = [ get_userpics($_) ] } @_;
	return \%info;
}


