#!/usr/bin/perl -w
#
# Skrypt pobierajcy skompresowane paczki newsw z serwisu:
# http://www.media-com.com.pl/~radecki/
#
# cigacz wersja 0.1.4
# Zgierz, 7.05.2000r
# Copyright by Andrzej Radecki <radecki@wpk.p.lodz.pl>
#
# You are permitted to modify and distribute this program in the
# terms of the GNU Public License (GPL).
#
# Moesz kopiowa i redystrybuowa ten program na zasadach zgodnych
# z licencj GPL (GNU Public License)
#
# Opcje:
#	-q - (quiet) wycza wywietlanie paska postpu i nazw pobieranych paczek
#	-d - (debug) wcza wywielanie komunikatw pomocnych przy poprawianiu programu
#
#
# Przykad pliku konfiguracyjnego ($MAINDIR/groups):
#
# # to jest komentarz 
#
#  nazwa.grupy1   -10	#pobierze 10 ostatnich paczek
# nazwa.grupy2 255 	#pobierze paczki zaczynajc od 256-ej
#    nazwa.grupy3       #pobierze 4 ostatnie paczki (warto domylna)



use strict;
use Getopt::Std;
use IO::Socket;
use IO::Handle;
use Term::Cap;
use Symbol;

my $REMOTE_ADDR	= 'http://www.karnet.pl/newsy/'; # na koncu _musi_ by '/'

my $MAINDIR	= '/var/spool/feeder'; # a tu bez '/'!
my $RECEIVED	= "$MAINDIR/received";
my $GRUPY	= "$MAINDIR/groups";
my $maxclients	= 5;

my %args;

getopts('dq', \%args); #debug, quiet

#my $terminal;

if (! $args{q}) {
	use Term::Cap;
	require POSIX;
	my $termios = new POSIX::Termios;
	$termios->getattr;
	my $ospeed = $termios->getospeed;
#	$terminal = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed };
#	$terminal->Trequire(qw/ce ku kd/);
#	$terminal->Tputs('r1',1,  *STDOUT);
	print `tput clear`;
}

my $start_time = time();


$REMOTE_ADDR =~ /(http:\/\/)?(.+?)(\/.*)/;
my $remote_server;
my $remote_dir;
if (defined($2)) { $remote_server = $2 }
if (defined($3)) { $remote_dir = $3 } else { $remote_dir = '' };
$args{d} && print "$1, $remote_server -> $remote_dir\n";

#scigamy list dostpnych paczek
my @lista = (); #( 94, 95, 96, 97, 98, 99, 100);
my ($errcode, $plik) = sciagnij("list");
$args{d} && print "$errcode:, $plik\n";
if (!defined($errcode)) { die "Nie mog poczy si z serwerem \"$remote_server\"\n" }
elsif ($errcode !~ /^2/) { die "Bd ($errcode) przy pobieraniu pliku \"http://$remote_server$remote_dir/list\"\n" }
@lista = split (/\n/, $plik);

# parsujemy plik "groups"
my $first;
my %grupy = ();
my %grupy_stan = ();
my @lines = ();
my $liczba = 0;
my $licznik = 0;
my $global_size = 0;
my @clients=();
open (FH, "$GRUPY") || die "Nie mog otworzy pliku $GRUPY: $!\n";
	while (<FH>) {
		push @lines, $_;
		chomp;
		next if (/^#/ || /^\s*$/) ;  #komentarz bd pusta linia
		/^\s*(\S+)\s+(\-?\d+)/ || /^\s*(\S+)/; #grupa [+ licznik]
		unless (defined($2))	{ $first = $lista[$#lista] + 1 - 4 } #wart. domyslna num. paczki
		elsif ($2 < 0)		{ $first = $lista[$#lista] + 1 + $2 } #z minusem
		else			{ $first = $2 + 1 } #zwykly nr paczki
		if ( $first < $lista[0] ) {
			warn "Na serwerze nie ma ju paczek grupy \"$1\" o numerach $first..$lista[0]\n";
			$first = $lista[0];
		}
		$args{d} && print "$$: $first\n";
		for my $i ($first..$lista[$#lista]) { push @{$grupy{$1}}, $i; $liczba++; } #lista paczek do sciagniecia
		push @{$grupy_stan{$1}}, $first - 1; #paczka na ktorej skonczylismy ostatnim razem
	}
close(FH) || warn "Nie mog zamkn pliku $GRUPY: $!\n";


# tworzymy rurki
pipe (PARENT_READ, CHILD_WRITE); #dzieciak->rodzic
CHILD_WRITE->autoflush(1);

my @pipes_parent = ();
my @pipes_child = ();

for my $i (0..$maxclients-1) {
	$pipes_parent[$i] = gensym;
	$pipes_child[$i] = gensym;
	pipe($pipes_child[$i], $pipes_parent[$i]); #rodzic->dzieciak
	bless $pipes_child[$i], "IO::Handle"; #zebym to ja wiedzia po co?
	bless $pipes_parent[$i], "IO::Handle";
	$pipes_parent[$i]->autoflush(1);
}

#ok, mamy juz rurki, teraz procesy
for my $pn (0..$maxclients-1) {
	unless (my $pid = fork) {
		die "cannot fork: $!" unless defined $pid; #oops dziecko nie wyszlo
		&child($pn);
		exit;
	}
}
#$SIG{CHLD} = \&sig_chld_handler;

#jestem tatusiem

$SIG{CHLD} = 'IGNORE';
close CHILD_WRITE;
for my $rurka (@pipes_child) { close $rurka; }

my $line;
OUTER:
foreach my $grupa (keys %grupy) {
	&rysuj;
	while (1) {
		my $paczka = shift @{$grupy{$grupa}};
		last unless (defined($paczka));
		my $plik = "spool/$grupa.$paczka.bz2";
		my $line = <PARENT_READ>;  # czekamy na zgloszenie gotowosci potomka
		$args{d} && print "Parent ($$) <- $line";
		chomp $line;
		my $fh;
		if ($line =~ /^(\d+)/) { $fh = $pipes_parent[$1] } #czytamy numer dzieciaka
		my $nr = $1;
		if ($line =~ /OK/) {
			print $fh "$plik\n"; #jesli OK to dalej
			@{$clients[$nr]} = ($grupa, $paczka);
			&rysuj(1);
		}
		else { print $fh ".\n";	last OUTER; } # jesli nie to przerwij
		if ($line =~ /spool\/(.+)\.(\d+)\.bz2 (\d+)/) {
			push @{$grupy_stan{$1}}, $2;
			$licznik++;
			$global_size += $3;
			&rysuj(0);
		}
	}
}

# czekamy az dzieciaki dokoncza prace i zamykamy je
while (my $line = <PARENT_READ>) {
	$args{d} && print "Parent ($$) <- $line";
	chomp $line;
		my $fh;
		if ($line =~ /^(\d+)/) { $fh = $pipes_parent[$1] } #czytamy numer dzieciaka
		my $nr = $1;
		print $fh ".\n";
		#zapisujemy nr paczki
		if ($line =~ /OK spool\/(.+)\.(\d+)\.bz2 (\d+)/) {
			push @{$grupy_stan{$1}}, $2;
			@{$clients[$nr]} = ();
			$licznik++;
			$global_size += $3;
			&rysuj(0);
		}
}
close PARENT_READ;

#sprawdzamy ile udao si cign (kolejno paczek moe by przypadkowa)
foreach my $grupa (keys %grupy) {
		$args{d} && print "$grupa: ", join(", ", @{$grupy_stan{$grupa}}), "\n";
	my @sorted = sort {$a<=>$b} @{$grupy_stan{$grupa}}; #mamy posortowane numery sciagnietych paczek
	my $max=$sorted[0];
	$args{d} && print "$grupa: ", join(", ", @sorted), "\n";
	for my $stan (@sorted) { #szukamy dziury w calym
		if ($stan <= $max + 1) {$max = $stan}
		else {last}
	}
	@{$grupy_stan{$grupa}} = ();
	push @{$grupy_stan{$grupa}}, $max;
	$args{d} && print "$grupa: ", join(", ", @{$grupy_stan{$grupa}}), "\n";
}

# zapis pliku "groups"
open (FH, ">$GRUPY") || die "Nie mog otworzy pliku $GRUPY: $!\n";
	while (defined($_ = shift(@lines))) {
		if (/^#/ || /^\s*$/) {print FH} #komentarz bd pusta linia
		elsif (/^(\s*)(\S+)(\s+)(\-?\d+)(.*)/) # || /^\s*(\S+)/; #grupa [+ licznik]
			{ print FH "$1$2$3", @{$grupy_stan{$2}}[0], "$5\n" }
		elsif (/^(\s*)(\S+)(.*)/) #grupa [+ licznik]
			{ print FH "$1$2 ", @{$grupy_stan{$2}}[0], "$3\n" }
	}
close(FH) || warn "Nie mog zamkn pliku $GRUPY: $!\n";

print STDERR "\n\nFinished at ", scalar(gmtime()), "\n";
print STDERR "Elapsed time: ", time() - $start_time, " sec,\tAverage speed: ",
	(time() - $start_time == 0) ? 0:int($global_size/(time() - $start_time+1)),
	" Bps,\tTotal size: ", int($global_size/1024)," kB\n";


sub child {
	my $fh = $pipes_child[$_[0]];
	close PARENT_READ;
	for my $rurka (@pipes_parent) { close $rurka }
	for my $rurka (@pipes_child) { close $rurka unless ( $rurka == $fh ) }
	print(CHILD_WRITE "$_[0] OK\n");	#juz jestem gotowy
	my $line;
	while ($line = <$fh>) {
#		sleep(rand(4)+2);
#		sleep(1);
		chomp $line;
		$line =~ /spool\/(.+)/;
		my $paczka = $1;
		my $err=0;
		my $size=0;
		$args{d} && print("Child ($$) <- $line\n");
		last if ($line eq '.');
		(my $errcode, my $plik) = sciagnij ($line);
		if (defined($errcode) && (($errcode =~ /^2/) || ($errcode == 404))) {
			if (defined($plik)) {
				$size = length($plik);
				open (ART, ">$RECEIVED/$paczka") || do { warn "Nie mog otworzy pliku $RECEIVED/$paczka: $!\n"; $err=1;};
				print (ART $plik);
				close(ART) || do { warn "Nie mog zamkn pliku $RECEIVED/$paczka: $!\n"; $err=1;};
			}
		} else { $err=1; }
		if ($err)	{ print (CHILD_WRITE "$_[0] FAILED $line\n");}
		else 		{ print (CHILD_WRITE "$_[0] OK $line $size\n");}
	}
		
	close $fh;
	close CHILD_WRITE;
}

sub rysuj {
	my $frac = $licznik/$liczba;
	my $i = int($frac * 50);
	my $curr_time = time() - $start_time;

	if (! $args{q}) {
#		$terminal->Tgoto('cm', 0, 0, *STDOUT);
		print `tput cup 0 0`;
		for my $l (0..$maxclients-1) {
#			$terminal->Tputs('ce', 1, *STDOUT);
		print `tput cup 1`;	
		print `tput cup $l 0`;
		print `tput ce`;
			if (defined(@{$clients[$l]}[0])) {
				print $l+1, "+ ", @{$clients[$l]}[0],
				".", @{$clients[$l]}[1], "\n";
			} else {print "$l-\n"}
			print "\n";
		}
	print "[", '#' x $i, ' ' x (50 - $i) , "]\n";
	print `tput el`;
	#$terminal->Tputs('ce', 1, *STDOUT);
	print "Elapsed time: ", $curr_time, " sec,\tAverage speed: ", ($curr_time == 0) ? 0:int($global_size/($curr_time+1)),
		" Bps,\tTotal size: ", int($global_size/1024)," kB\n";
	}	
}


sub sciagnij {
	my @wynik = ();
	for my $i (0..4) { # retrying
		if ($i) {warn "Bd w czasie pobierania pliku, ponawiam prb\n"}
		@wynik = eval {
			local $/="\r\n";
			print "$_[0]";
			my $sokecik=IO::Socket::INET->new(PeerAddr => "$remote_server", PeerPort => 80,
				Timeout => 15, Proto => 'tcp');
#			$sokecik->print("GET $remote_dir$_[0] HTTP/1.0\n\n");
			$sokecik->print("GET $remote_dir$_[0] HTTP/1.1\n");
			$sokecik->print("Host: $remote_server\n\n");
#			print "$remote_server, $remote_dir $_[0]";
			my $line = <$sokecik>;
			my $status;
			$line =~ /^\S+\s+(\d+)/;
			$status = $1;
			if ($1 !~ /^2/) { return ($status, undef) };
			while(<$sokecik>) { last if /^\r\n/ }
			undef $/;
			$line = <$sokecik>;
			close ($sokecik);
			return ($status, $line);
		};
		last if (defined($wynik[0]));
	}
	return @wynik;
}

#EOF
