# by Stefan 'tommie' Tomanek
#
use strict;
use vars qw($VERSION %IRSSI);
$VERSION = "2003020801";
%IRSSI = (
authors => "Stefan 'tommie' Tomanek",
contact => "stefan\@pico.ruhr.de",
name => "Poison",
description => "equips Irssi with an interface to giFT",
license => "GPLv2",
changed => "$VERSION",
modules => "IO::Socket::INET Data::Dumper",
commands => "poison"
);
use vars qw($forked %ids);
use IO::Socket::INET;
use Data::Dumper;
use Irssi;
use POSIX;
sub show_help() {
my $help = $IRSSI{name}." $VERSION
/poison
List current downloads
/poison search
Search for files on the network
";
my $text = '';
foreach (split(/\n/, $help)) {
$_ =~ s/^\/(.*)$/%9\/$1%9/;
$text .= $_."\n";
}
print CLIENTCRAP &draw_box($IRSSI{name}, $text, "help", 1);
}
sub giftconnect {
my $host = Irssi::settings_get_str('poison_host');
my $port = Irssi::settings_get_int('poison_port');
my $sock = IO::Socket::INET->new(PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp');
return $sock;
}
sub draw_box ($$$$) { my ($title, $text, $footer, $colour) = @_;
my $box = '';
$box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
foreach (split(/\n/, $text)) {
$box .= '%R|%n '.$_."\n";
}
$box .= '%R`--<%n'.$footer.'%R>->%n';
unless ($colour) {
$box =~ s/%(.)/$1 eq '%'?$1:''/eg;
}
return $box;
}
sub round ($$) {
return $_[0] unless Irssi::settings_get_bool('poison_round_filesize');
if ($_[1] > 100000) {
return sprintf "%.2fMB", $_[0]/1024/1024;
} else {
return sprintf "%.2fKB", $_[0]/1024;
}
}
sub array2table {
my (@array) = @_;
my @width;
foreach my $line (@array) {
for (0..scalar(@$line)-1) {
my $l = $line->[$_];
$l =~ s/%[^%]//g;
$l =~ s/%%/%/g;
$width[$_] = length($l) if $width[$_][$_];
$text .= $line->[$_];
$l =~ s/%[^%]//g;
$l =~ s/%%/%/g;
$text .= " "x($width[$_]-length($l)+1) unless ($_ == scalar(@$line)-1);
}
$text .= "\n";
}
return $text;
}
sub bg_do ($$) {
my ($id, $sub) = @_;
my ($rh, $wh);
pipe($rh, $wh);
return if $forked;
$forked = 1;
my $pid = fork();
if ($pid > 0) {
close $wh;
Irssi::pidwait_add($pid);
my $pipetag;
my @args = ($rh, \$pipetag); $pipetag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, \@args);
} else {
eval {
my $result;
$result->{$id} = &$sub();
my $dumper = Data::Dumper->new([$result]);
$dumper->Purity(1)->Deepcopy(1);
my $data = $dumper->Dump;
print($wh $data);
close($wh);
};
POSIX::_exit(1);
}
}
sub pipe_input ($) {
my ($rh, $pipetag) = @{$_[0]};
my $text;
$text .= $_ foreach (<$rh>);
close($rh);
Irssi::input_remove($$pipetag);
$forked = 0;
return unless($text);
no strict;
my $result = eval "$text";
return unless ref $result;
print_results($result->{search}) if defined $result->{search};
print CLIENTCRAP '%R>>%n Added '.$result->{sources}.' source(s) for download' if defined $result->{sources};
}
sub search_file ($) {
my ($query) = @_;
my $sock = giftconnect();
return unless $sock;
$sock->print("SEARCH query(".$query.");\n");
my %results;
my %item;
my $meta = 0;
while ($_ = $sock->getline()) {
if ((not $meta) && / *(.*?)\((.*?)\)[^;]/) {
my ($key, $value) = ($1, $2);
$value =~ s/\\(.)/$1/g;
$item{$key} = $value;
} elsif (/META/) {
$meta = 1;
} elsif (/ITEM;/) {
$sock->close();
last;
} elsif (/;/) {
$meta = 0;
my %foo = %item;
%item = ();
$results{$foo{hash}} = \%foo;
}
}
return \%results;
}
sub get_file ($) {
my ($id) = @_;
return unless $ids{$id};
my $data = $ids{$id};
add_source($data);
bg_do('sources', sub { retrieve_sources($data->{hash}) } );
}
sub retrieve_sources ($) {
my ($hash) = @_;
my %sources;
foreach (@{ find_sources($hash) }) {
add_source($_);
$sources{$_->{user}} = 1;
}
return scalar keys %sources;
}
sub add_source (\%) {
my ($data) = @_;
my $sock = giftconnect();
return unless $sock;
my @bar = split('/', $data->{url});
my $file = $bar[-1];
my $line = "ADDSOURCE ";
$line .= "user(".$data->{user}.") ";
$line .= "hash(".$data->{hash}.") ";
$line .= "size(".$data->{size}.") ";
$line .= "url(".$data->{url}.") ";
$line .= "save(".$file.");";
$sock->print($line."\n");
$sock->close();
}
sub find_sources ($) {
my ($hash) = @_;
my $sock = giftconnect();
return unless $sock;
$sock->print("LOCATE query(".$hash.");\n");
my %item;
my @sources;
my $meta = 0;
while ($_ = $sock->getline()) {
if ((not $meta) && (/ *(.*?)\((.*?)\)[^;]/)) {
my ($key, $value) = ($1, $2);
#print $key." => ".$value;
$value =~ s/\\(.)/$1/g;
$item{$key} = $value;
} elsif (/META/) {
$meta = 1;
} elsif (/ITEM;/) {
$sock->close();
last;
} elsif (/;/) {
$meta = 0;
my %foo = %item;
%item = ();
push @sources, \%foo;
}
}
return \@sources;
}
sub get_downloads {
my %downloads;
my $sock = giftconnect();
return unless $sock;
$sock->print("ATTACH client(".$IRSSI{name}.") version(".$VERSION."); DETACH;");
my %downloads;
my ($add, $source) = (0,0);
my %item;
while ($_ = $sock->getline()) {
if (/^DOWNLOAD_ADD\((\d+)\)/) {
$add = 1;
$item{sessionid} = $1;
} elsif (/SOURCE/) {
$source = 1;
} elsif (/};/) {
$source = 0;
$add = 0;
my %foo = %item;
$downloads{$foo{file}} = \%foo;
} else {
if (($add && not $source) && /^ (.*?)\((.*?)\)$/) {
my ($key, $value) = ($1, $2);
$value =~ s/\\(.)/$1/g;
$item{$key} = $value;
}
}
}
return \%downloads;
}
sub print_results ($) {
my ($results) = @_;
my @array;
%ids = ();
my $i = 1;
foreach (sort {uc($a) cmp uc($b)} keys %$results) {
my @bar = split('/', $results->{$_}{url});
my $file = $bar[-1];
$file =~ s/%20/ /g;
$file =~ s/%/%%/g;
my @line;
push @line, "%9".$i."%9";
push @line, "%9".$file."%9";
push @line, $results->{$_}{size};
push @line, $results->{$_}{availability};
push @array, \@line;
$ids{$i} = $results->{$_};
$i++;
}
my $text = array2table(@array);
print CLIENTCRAP draw_box("Poison", $text, "Results", 1) if $text;
}
sub print_downloads ($) {
my ($downloads) = @_;
my $text;
foreach (sort {uc($a) cmp uc($b)} keys %$downloads) {
if ($downloads->{$_}{state} eq 'Active') {
$text .= '%bo%n';
} elsif ($downloads->{$_}{state} eq 'Paused') {
$text .= '%yo%n';
}
my $percent = $downloads->{$_}{size} > 0 ? ($downloads->{$_}{transmit} / $downloads->{$_}{size}) * 100 : 0;
my $file = $_;
$file =~ s/%20/ /g;
$file =~ s/%/%%/g;
$text .= " %9".$file."%9";
$text .= "\n";
$text .= ' ';
$text .= round($downloads->{$_}{transmit}, $downloads->{$_}{size}).'/';
$text .= round($downloads->{$_}{size}, $downloads->{$_}{size});
$percent =~ s/(\..).*/$1/g;
$text .= " (".$percent."%%)";
$text .= "\n"
}
print CLIENTCRAP draw_box("Poison", $text, "Downloads", 1);
}
sub cmd_poison ($$$) {
my ($args, $server, $witem) = @_;
my @args = split(/ /, $args);
if (@args == 0) {
print_downloads(get_downloads());
} elsif ($args[0] eq 'search') {
shift @args;
if ($forked) {
print CLIENTCRAP '%R>>%n Already searching...';
} else {
print CLIENTCRAP '%R>>%n Search in progress...';
}
bg_do 'search', sub { search_file(join(' ', @args)) };
#print_results search_file(join(' ', @args));
} elsif ($args[0] eq 'get' && $args[1]) {
get_file($args[1]);
} elsif ($args[0] eq 'help') {
show_help();
}
}
Irssi::settings_add_str('poison', 'poison_host', 'localhost');
Irssi::settings_add_int('poison', 'poison_port', 1213);
Irssi::settings_add_bool('poison', 'poison_round_filesize', 1);
Irssi::command_bind('poison', \&cmd_poison);
foreach my $cmd ('help', 'search', 'get') {
Irssi::command_bind('poison '.$cmd => sub {
cmd_poison("$cmd ".$_[0], $_[1], $_[2]); });
}
print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded, /poison help';