#!/usr/bin/perl
# lj-edit-friends - a simple script for add or delete
# LiveJournal friends using by LJ XML-RPC interface.
# dimio (dimio.org), see more on: http://dimio.org/?p=1434
# See more about LJ XML-RPC on:
# livejournal.com/doc/server/ljp.csp.xml-rpc.protocol.html
require 5.008_008;
use warnings;
use strict;
use utf8;
use Digest::MD5 qw (md5_hex);
use LWP::UserAgent;

our $VERSION = '0.04';
# set 'print' like a 'say'
$\ = $/;

usage( $ARGV[0] ) if !@ARGV || $ARGV[0] =~ m{^-{0,2}(h|help)$}i;

my $options = {
	frlist_file	=> shift,
	friend_mode	=> shift || 'add', # 'add' or 'delete'

	login		=> shift || 'test',
	password	=> shift || 'test',
	
	rpc_host	=> 'http://www.livejournal.com/interface/xmlrpc',
};

my $friends_list = read_friends_list( $options->{frlist_file} );

my $ua = LWP::UserAgent->new (
	agent	=> "LJ XML-RPC Client v. $VERSION",
	timeout	=> 360,
);

my $challenge = get_challenge( $options, $ua );

print lj_editfriends( $options, $ua, $challenge, $friends_list );

####################################################################
sub usage {
	my $help = shift;

	print "$0 ver. $VERSION, dimio (dimio.org)$/" if $help;
	print < [mode] [lj_login] [lj_pass]
file		- path to file with a list of friends (required)
mode		- 'add' or 'delete' friends (optional, by default is 'add')
lj_login	- login to LJ account, may be specified on $0 (optional, by default is 'test')
lj_pass		- password to LJ account, may be specified on $0 (optional, by default is 'test')

h|help		- show help and exit
EOF
	exit;
}

sub lj_editfriends {
	my $options = shift;
	my $ua = shift;
	my $challenge = shift;
	my $friends_list = shift;

	my $resp_content = '
		
		LJ.XMLRPC.editfriends
		
		
		

		username
		'. $options->{login} .'
		

		auth_method
		challenge
		

		auth_challenge
		'. $challenge .'
		

		auth_response
		'. md5_hex( $challenge . md5_hex( $options->{password} )) .'
		

		ver
		1
		

		'. $options->{friend_mode} .'
		
		';

		if ( $options->{friend_mode} eq 'add' ){
			$resp_content .= lj_addfriends( $friends_list );
		}
		elsif ( $options->{friend_mode} eq 'delete' ){
			$resp_content .= lj_deletefriends( $friends_list );
		}
		else {
			print "Illegal mode option \"$options->{friend_mode}\", see usage: $/";
			usage();
		}

		$resp_content .=
		'
		
		

		
		
		
		';

	my $response = $ua->post( 
		$options->{rpc_host},
		Content => $resp_content,
	);
	die( $response->status_line )
		unless $response->is_success;

	return $response->decoded_content;
}

sub lj_deletefriends {
	my $friends_list = shift;

	my $resp_content;
	foreach( @{ $friends_list } ){
		$resp_content .=
		''. $_ .'';
	}

	return $resp_content;
}

sub lj_addfriends {
	my $friends_list = shift;

	my $resp_content;
	foreach( @{ $friends_list } ){
		$resp_content .=
		'
		username
		'. $_ .'
		
		';
	}

	return $resp_content;
}

sub get_challenge {
	my $options = shift;
	my $ua = shift;

	my $response = $ua->post( 
		$options->{rpc_host},
		Content => 
		'
		
		LJ.XMLRPC.getchallenge
		
		
		
		
		
		
		',
	);
	die( $response->status_line )
		unless $response->is_success;

	my $challenge = $1
		if $response->decoded_content =~ m#([\w:]{3,})<\/string>#;

	return $challenge;
}

sub read_friends_list {
	my $file = shift;

	my $fh;
	open( $fh, '<', $file )
		or die "Can't open < $file: $!";

	# my @friends = <$fh>;
	my @friends;
	while ( <$fh> ){
		chomp;
		s/^\s+//;
		s/\s+$//;
		next if m{^(#.*|$)};
		s/^friend\s+//;
		s/(add|remove)\s+//;

		push @friends, $_;
	}

	close( $fh )
		or warn "Can't close $file: $!";

	return \@friends;
}