#!/usr/bin/perl -w

#  (c) JFCh. 2009
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.

use strict;
use WWW::Mechanize;
use Getopt::Long;
use POSIX;
use Digest::SHA1 qw(sha1 sha1_hex sha1_base64);
use File::Path;
use File::Copy;

my ($down, @download);
my ($entry, @entries, $err);
my ($flag);
my ($local_file, $local_name, $local_sha1, $local_version, $localdir);
my ($m, $mask, $my_version);
my (@old_version);
my (@new_version);
my ($old_sha1, $old_version, $opt_clone, $opt_erase, $opt_eraseall, $opt_help, $opt_list, $opt_version);
my ($new_file, $new_sha1);
my ($remote_file, @remote_files, $remote_name, $remote_sha1, $remote_version);
my ($site, $sitelist, @sitelist_body, $system_version, $systemdir);
my ($url);
my ($version);

$| = 1;
$sitelist = '/etc/openssh-blacklist.sites';
$systemdir = '/var/cache/openssh-blacklist';
$localdir = $systemdir;
$mask = '^fingerprint_';
$my_version = "1.0";

if (geteuid ()) {
	$localdir = getenv ("HOME") . "/.openssh-blacklist";
	$err = GetOptions ("clone" => \$opt_clone, "erase" => \$opt_erase, "mask=s" => \$mask, "list" => \$opt_list, "help" => \$opt_help, "version" => \$opt_version);
} else {
	$err = GetOptions ("eraseall" => \$opt_eraseall, "erase" => \$opt_erase, "mask=s" => \$mask, "list" => \$opt_list, "help" => \$opt_help, "version" => \$opt_version);
}

if (!$err) {
	print $0 . ": use --help for help\n";
	exit;
}

if ($opt_version) {
	print $0 . " version " . $my_version . "\n";
	exit;
}

if ($opt_help) {
	print "Use: ". $0 ."[--clone] [--erase] [--eraseall] [--mask mask] [--list] [--help] [--version]\n";
	print "\t--clone\t\tClone system content to local user\n";
	print "\t--erase\t\tErase local data\n";
	print "\t--eraseall\tErase all data [NOT WORKING YET]\n";
	print "\t--mask RE\tSet file mask (regular expression) [default ^fingerprint_]\n";
	print "\t--list\t\tList files awailable to download\n";
	print "\t--help\t\tPrint this help\n";
	print "\t--version\tPrint program version\n";
	exit;
}

if ($opt_erase) {
	system ("rm -rf ". $localdir);
	print "Done.\n";
	exit;
}

if ($opt_eraseall) {
	print "hahah\n";
	exit;
}

&File::Path::mkpath ($localdir);
chdir ($localdir);

if ($opt_clone) {
	$system_version = $systemdir ."/VERSION";
	if (-r $system_version) {
		copy ($system_version, "VERSION");
		opendir (DIR, $systemdir);
		@entries = readdir (DIR);
		closedir (DIR);
		foreach $entry (@entries) {
			if (($entry ne 'VERSION') and !($entry =~ /^\./) and !(-f $entry)) {
				symlink ($systemdir ."/". $entry, $entry);
			}
		}
	}	
	print "Cloned.\n";
	exit;
}

$m = WWW::Mechanize->new(); 

open (SL, $sitelist) and @sitelist_body = <SL>;
close (SL);

@old_version = ();
open (VER, "VERSION") and @old_version = <VER>;
close (VER);

if ($opt_list) {
	foreach $site (@sitelist_body) {
		chop ($site);
		print "----- " . $site . " -----\n";

		@new_version = ();

		$url = $site . 'VERSION';
		$m->get ($url);
		$version = $m->content();
		@remote_files = split ("\n", $version);
		foreach $remote_file (@remote_files) {
			($remote_version, $remote_sha1, $remote_name) = split (":", $remote_file);
			chomp ($remote_name);
			$flag = 0;
			foreach $local_file (@old_version) {
				if ($local_file ne "\n") {
					($local_version, $local_sha1, $local_name) = split (":", $local_file);
					chomp ($local_name);
					if ($remote_name eq $local_name) {
						if (($local_sha1 eq $remote_sha1) or ($local_version > $remote_version)) {
							push (@new_version, $local_version .":". $local_sha1 .":". $local_name);
							$flag = 1;
						}
						last;
					}
				}
			}

			if (!$flag) {
				print ">> ". $remote_file ."\n";
				push (@new_version, $remote_version .":". $remote_sha1 .":". $remote_name);
			}
		}

		@old_version = @new_version;
	}
	exit
}

foreach $site (@sitelist_body) {
	chop ($site);
	print "----- " . $site . " -----\n";

	@download = ();
	@new_version = ();

	$url = $site . 'VERSION';
	$m->get ($url);
	$version = $m->content();
	@remote_files = split ("\n", $version);

	foreach $remote_file (@remote_files) {
		$old_version = '';
		$old_sha1 = '';
		($remote_version, $remote_sha1, $remote_name) = split (":", $remote_file);
		chomp ($remote_name);
		$flag = 0;
		foreach $local_file (@old_version) {
			if ($local_file ne "\n") {
				($local_version, $local_sha1, $local_name) = split (":", $local_file);
				chomp ($local_name);
				if ($remote_name eq $local_name) {
					if (($local_sha1 eq $remote_sha1) or ($local_version > $remote_version)) {
						push (@new_version, $local_version .":". $local_sha1 .":". $local_name);
						$flag = 1;
					} else {
						$old_version = $local_version;
						$old_sha1 = $local_sha1;
					}
					last;
				}
			}
		}

		if (!$flag) {
			push (@download, $remote_version .":". $remote_sha1 .":". $remote_name .":". $old_version .":". $old_sha1);
		}
	}

	foreach $down (@download) {
		($remote_version, $remote_sha1, $remote_name, $old_version, $old_sha1) = split (":", $down);
		if (($remote_name =~ /^[A-Z]/) or ($remote_name =~ $mask)) {

			print ">> ". $remote_name;
			$url = $site . $remote_name;
			$m->get ($url);
			$new_file = $m->content ();
			$new_sha1 = sha1_hex ($new_file);
			if ($remote_sha1 ne $new_sha1) {
				if ($old_version ne '') {
					push (@new_version, $old_version .":". $old_sha1 .":". $remote_name);
				}
				print " bad checksum\n"
			} else {
				push (@new_version, $remote_version .":". $remote_sha1 .":". $remote_name);
				if ($remote_name =~ /\.tar\.bz2$/) {
					$remote_name =~ s/\tar.\.bz2$//;
					system ("rm -rf ". $remote_name);
					open (OF, "| bunzip2 | tar xf -");
				} elsif ($remote_name =~ /\.bz2$/) {
					$remote_name =~ s/\.bz2$//;
					unlink ($remote_name);
					open (OF, "| bunzip2 > ". $remote_name);		
				} else {
					unlink ($remote_name);
					open (OF, '>'. $remote_name);
				}
				print OF $new_file;
				close (OF);
				print "\n";
			}
		}
	}

	open (VER, '>'. "VERSION");
	print VER join ("\n", @new_version);
	print VER"\n";
	close VER;

	@old_version = @new_version;
}


