1# iDonkey for mldonkey 2# 3## by Stefan Tomanek 4# 5 6use strict; 7 8use vars qw($VERSION %IRSSI); 9$VERSION = "2004051601"; 10%IRSSI = ( 11 authors => "Stefan 'tommie' Tomanek", 12 contact => "stefan\@pico.ruhr.de", 13 name => "iDonkey", 14 description => "equips Irssi with an interface to mldonkey", 15 license => "GPLv2", 16 changed => "$VERSION", 17 modules => "IO::Socket::INET Data::Dumper LWP::UserAgent HTML::Entities", 18 sbitems => "idonkey", 19 commands => "idonkey" 20); 21 22 23use Irssi 20020324; 24use Irssi::TextUI; 25use IO::Socket::INET; 26use LWP::UserAgent; 27use HTML::Entities; 28use Data::Dumper; 29use POSIX; 30use vars qw($forked $timer $timer2 $index %downloads $nresults $seen $credits $noul %edlinks $expected); 31 32sub show_help() { 33 my $help = $IRSSI{name}." $VERSION 34/idonkey (downloads) 35 List your current downloads 36/idonkey launch 37 Start a new mldonkey process 38/idonkey quit 39 Quit the mldonkey 40/idonkey servers (connected) 41 List connected servers 42/idonkey servers all 43 List all servers 44/idonkey servers connect <num> 45 Connect to server with id <num> 46 or connect more servers 47/idonkey servers disconnect <num> 48 Disconnnect from server with id <num> 49/idonkey overnet (stats) 50 Print OverNet statistics 51/idonkey dllink (force) <link> 52 Download an ed2k-link 53/idonkey search <query> 54 Query the donkey network for a file 55/idonkey results 56 Display the results of the last query 57/idonkey get (force) <num1> <num2> 58 Download the named files 59/idonkey pause <filename> 60 Pause a download 61/idonkey resume <filename> 62 Resume a download 63/idonkey cancel <filename> 64 Cancel a download 65/idonkey commit 66 Move downloaded files to the incoming directory 67/idonkey settings show 68 Display the current mldonkey settings 69/idonkey settings change <key> <value> 70 Change settings of mldonkey 71/idonkey shares reshare 72 Check all shared files 73/idonkey shares close 74 Close all open file descriptors 75/idonkey sharereactor (latest) 76 Display the latest releases 77/idonkey sharereactor search <query> 78 Search www.sharereactor.com 79/idonkey sharereactor download <release> 80 Download all files of a release 81/idonkey bittorrent search <quer> 82 Search torrents 83/idonkey noupload <min> 84 Disable uploading for <min> minutes 85/idonkey client-stats 86 Display detailed client statistics 87/idonkey forget 88 Clear all searches 89"; 90 my $text = ''; 91 foreach (split(/\n/, $help)) { 92 $_ =~ s/^\/(.*)$/%9\/$1%9/; 93 $text .= $_."\n"; 94 } 95 print CLIENTCRAP &draw_box($IRSSI{name}, $text, "help", 1); 96} 97 98 99sub draw_box ($$$$) { 100 my ($title, $text, $footer, $colour) = @_; 101 my $box = ''; 102 $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n"; 103 foreach (split(/\n/, $text)) { 104 $box .= '%R|%n '.$_."\n"; 105 } 106 $box .= '%R`--<%n'.$footer.'%R>->%n'; 107 unless ($colour) { 108 $box =~ s/%(.)/$1 eq '%'?$1:''/eg; 109 } 110 return $box; 111} 112 113sub array2table { 114 my (@array) = @_; 115 my @width; 116 foreach my $line (@array) { 117 for (0..scalar(@$line)-1) { 118 my $l = $line->[$_]; 119 $l =~ s/%[^%]//g; 120 $l =~ s/%%/%/g; 121 $width[$_] = length($l) if $width[$_]<length($l); 122 } 123 } 124 my $text; 125 foreach my $line (@array) { 126 for (0..scalar(@$line)-1) { 127 my $l = $line->[$_]; 128 $text .= $line->[$_]; 129 $l =~ s/%[^%]//g; 130 $l =~ s/%%/%/g; 131 $text .= " "x($width[$_]-length($l)+1) unless ($_ == scalar(@$line)-1); 132 } 133 $text .= "\n"; 134 } 135 return $text; 136} 137 138sub donkey_connect { 139 my $host = Irssi::settings_get_str('idonkey_host'); 140 my $port = Irssi::settings_get_int('idonkey_port'); 141 my $password = Irssi::settings_get_str('idonkey_password'); 142 my $sock = IO::Socket::INET->new(PeerAddr => $host, 143 PeerPort => $port, 144 Proto => 'tcp'); 145 return 0 unless $sock; 146 my $password = Irssi::settings_get_str('idonkey_password'); 147 while ($_ = $sock->getline()) { 148 s/\e.*?m//g; 149 if (/Use \? for help/) { 150 $sock->print("auth ".$password."\n"); 151 } elsif (/Full access enabled/) { 152 $sock->print("ansi false\n"); 153 foreach (1..3) { 154 $sock->getline(); 155 } 156 return $sock; 157 } elsif (/Bad login\/password/) { 158 $sock->close(); 159 return 0; 160 } 161 } 162} 163 164sub bg_do ($) { 165 my ($cmd) = @_; 166 my ($rh, $wh); 167 pipe($rh, $wh); 168 return if $forked > 1; 169 $forked++; 170 my $pid = fork(); 171 if ($pid > 0) { 172 close $wh; 173 Irssi::pidwait_add($pid); 174 my $pipetag; 175 my @args = ($rh, \$pipetag); $pipetag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, \@args); 176 } else { 177 eval { 178 my $result; 179 if ($cmd eq 'downloads') { 180 $result->{downloads} = get_downloads(); 181 } elsif ($cmd =~ /^(pause|cancel|resume) (.*)/) { 182 transfer_command($1, $2); 183 } elsif ($cmd =~ /^results *(.*)/) { 184 $result->{results} = get_results($1); 185 } elsif ($cmd eq 'servers') { 186 $result->{servers} = get_servers(0); 187 } elsif ($cmd eq 'allservers') { 188 $result->{servers} = get_servers(1); 189 } elsif ($cmd eq 'status') { 190 if (Irssi::settings_get_bool('idonkey_update_results')) { 191 #$result->{nresults} = scalar @{ get_results('/.*/')->{results} }; 192 } 193 $result->{status} = get_status_info(); 194 } elsif ($cmd eq 'ovstats') { 195 $result->{ovstats} = get_ovstats(); 196 } elsif ($cmd =~ /^settings (.*?)$/) { 197 my $regexp = $1; 198 $regexp = '.*' unless $regexp; 199 $result->{settings} = get_settings($regexp); 200 } elsif ($cmd =~ /^set (.*?) (.*)$/) { 201 my ($key, $val) = ($1, $2); 202 $result->{change} = change_setting($key, $val); 203 } elsif ($cmd eq 'reshare') { 204 $result->{reshare} = reshare(); 205 } elsif ($cmd eq 'close_fds') { 206 $result->{close_fds} = close_fds(); 207 } elsif ($cmd =~ /^sr-search (.*)$/) { 208 $result->{sr_search} = sharereactor_search($1); 209 } elsif ($cmd eq "sr-latest") { 210 $result->{sr_latest} = sharereactor_latest(); 211 } elsif ($cmd =~ /^bt-search (.*)$/) { 212 $result->{bt_search} = bittorrent_search($1); 213 } elsif ($cmd =~ /^noupload (.*)$/) { 214 $result->{noupload} = no_upload($1); 215 } elsif ($cmd eq 'client-stats') { 216 $result->{client_stats} = get_client_stats(); 217 } elsif ($cmd eq 'forget') { 218 $result->{forget} = forget_searches(); 219 } elsif ($cmd =~ /^fake (.*)/) { 220 $result->{fake} = check_fake($1); 221 } 222 my $dumper = Data::Dumper->new([$result]); 223 $dumper->Purity(1)->Deepcopy(1); 224 my $text = $dumper->Dump; 225 print($wh $text); 226 #store_fd $result, $wh; 227 close($wh); 228 }; 229 POSIX::_exit(1); 230 } 231 232} 233 234sub pipe_input { 235 my ($rh, $pipetag) = @{$_[0]}; 236 $forked--; 237 Irssi::input_remove($$pipetag); 238 my $text; 239 $text .= $_ foreach <$rh>; 240 #print "RETURN"; 241 #print $text; 242 no strict 'vars'; 243 my $result = eval "$text"; 244 return unless ref $result; 245 %downloads = %{ $result->{downloads} } if ref $result->{downloads}; 246 $expected = $result->{results}->{waiting} if defined $result->{results} && $result->{results}->{waiting}; 247 show_client_stats($result->{client_stats}) if ref $result->{client_stats}; 248 list_downloads($result->{downloads}) if ref $result->{downloads}; 249 list_results($result->{results}) if ref $result->{results}; 250 list_servers($result->{servers}) if ref $result->{servers}; 251 show_ovstats($result->{ovstats},0) if ref $result->{ovstats}; 252 show_settings($result->{settings}) if ref $result->{settings}; 253 show_change($result->{change}) if ref $result->{change}; 254 update_status($result->{status}) if ref $result->{status}; 255 show_sr_search($result->{sr_search}) if ref $result->{sr_search}; 256 show_sr_latest($result->{sr_latest}) if ref $result->{sr_latest}; 257 show_bt_search($result->{bt_search}) if ref $result->{bt_search}; 258 store_links($result->{sr_search}) if ref $result->{sr_search}; 259 store_links_bt($result->{bt_search}) if ref $result->{bt_search}; 260 $nresults = $result->{nresults} if $result->{nresults}; 261 262 show_fake($result->{fake}) if $result->{fake}; 263 print CLIENTCRAP "%B>>%n Forgot ".$result->{forget}." searche(s)" if exists $result->{forget}; 264 print CLIENTCRAP "%B>>%n Upload disabled for ".$result->{noupload}->[0]." minutes" if exists $result->{noupload}; 265 print CLIENTCRAP "%B>>%n Shares have been checked" if $result->{reshare}; 266 print CLIENTCRAP "%B>>%n Files have been closed" if $result->{close_fds}; 267} 268 269sub show_fake ($) { 270 my ($data) = @_; 271 my $name = $data->{filename}; 272 my $hash = $data->{hash}; 273 my $text = "%B>>%n '".$name."' [".$hash."] is "; 274 $text .= 'not ' unless $data->{fake}; 275 $text .= 'a fake'; 276 print CLIENTCRAP $text; 277} 278 279sub store_links ($) { 280 my ($results) = @_; 281 %edlinks = (); 282 foreach (@$results) { 283 $edlinks{$_->{name}} = $_->{files}; 284 } 285} 286 287sub store_links_bt ($) { 288 my ($results) = @_; 289 %edlinks = (); 290 foreach (@$results) { 291 $edlinks{$_->{name}} = [ $_->{torrent} ]; 292 } 293} 294 295 296sub check_fake ($) { 297 my ($num) = @_; 298 my $results = get_results('/.*/'); 299 my $res; 300 foreach (@{$results->{results}}) { 301 #print $num." ".$_->{id}; 302 $res = $_ if $_->{id} eq $num; 303 } 304 return undef unless $res; 305 $res->{fake} = is_fake($res->{hash}); 306 return $res; 307} 308 309sub forget_searches { 310 my $sock = donkey_connect(); 311 return undef unless $sock; 312 my $i = 0; 313 foreach (keys %{ get_queries() }) { 314 $sock->print('forget '.$_."\n"); 315 $i++; 316 } 317 $sock->print("q\n"); 318 $sock->close(); 319 return $i; 320} 321 322sub show_client_stats ($) { 323 my ($data) = @_; 324 my @table; 325 foreach (sort keys %$data) { 326 my $first = 1; 327 foreach my $item (sort keys %{ $data->{$_} }) { 328 next if $item eq 'banned'; 329 next if $item eq 'seen'; 330 next if $data->{$_}{$item}{num} == 0; 331 my @line; 332 push @line, $first ? "%9".$_."%9" : ''; 333 $first = 0; 334 push @line, uc substr($item, 0, 1); 335 push @line, $data->{$_}{$item}{percent}."%"; 336 push @line, '#'x(80/100 * $data->{$_}{$item}{percent}); 337 push @table, \@line; 338 } 339 } 340 my $text = array2table(@table); 341 print CLIENTCRAP &draw_box('iDonkey', $text, 'Client Stats', 1); 342} 343 344sub show_sr_latest ($) { 345 my ($results) = @_; 346 my @table; 347 foreach (@$results) { 348 push @table, [$_->{date}, $_->{category}, "%9".$_->{title}."%9"]; 349 #$text .= $_->{date}." %9".$_->{title}."%9 [".$_->{category}."]\n"; 350 } 351 my $text = array2table(@table); 352 print CLIENTCRAP &draw_box('iDonkey', $text, 'Sharereactor latest releases', 1); 353} 354 355sub show_sr_search ($) { 356 my ($results) = @_; 357 my $text; 358 foreach (@$results) { 359 $text .= "%9".$_->{name}."%9 [".$_->{category}."]\n"; 360 foreach (@{ $_->{files} }) { 361 $text .= '-> '.$_."\n"; 362 } 363 } 364 print CLIENTCRAP &draw_box('iDonkey', $text, 'ShareReactor', 1); 365} 366 367sub show_bt_search ($) { 368 my ($results) = @_; 369 my $text; 370 foreach (@$results) { 371 $text .= "%9".$_->{name}."%9\n"; 372 my $url = $_->{torrent}; 373 $url =~ s/%/%%/g; 374 $text .= "-> $url\n"; 375 } 376 print CLIENTCRAP &draw_box('iDonkey', $text, 'BitTorrent results', 1); 377} 378 379sub show_change (\%) { 380 my ($change) = @_; 381 print CLIENTCRAP "%B>>%n [iDonkey] Setting %9".$change->{key}."%9 changed from '".$change->{old}."' to '".$change->{new}."'"; 382} 383 384sub show_settings (\%) { 385 my ($settings) = @_; 386 my @table; 387 foreach (sort keys %$settings) { 388 push @table, ["%9".$_."%9", $settings->{$_}]; 389 } 390 my $text = array2table(@table); 391 print CLIENTCRAP &draw_box('iDonkey', $text, 'settings', 1); 392} 393 394sub show_ovstats (\%$) { 395 my ($stats, $nodes) = @_; 396 my $text; 397 398 if ($nodes) { 399 $text .= "%9Connected nodes:%9\n"; 400 $text .= ' '.$_."\n" foreach @{ $stats->{nodes} }; 401 $text .= "\n"; 402 } 403 #$text .= @{ $stats->{nodes} }." connected nodes\n\n"; 404 $text .= "%9Search hits:%9 ".$stats->{search_hits}."\n"; 405 $text .= "%9Source hits:%9 ".$stats->{source_hits}; 406 print CLIENTCRAP &draw_box('iDonkey', $text, 'OverNet stats', 1); 407} 408 409sub update_status (\%) { 410 my ($data) = @_; 411 return unless ref $data; 412 $expected = $data->{waiting}; 413 $noul = $data->{noupload}; 414 $credits = $data->{credit}; 415 %downloads = %{ $data->{downloads} }; 416 Irssi::statusbar_items_redraw('idonkey'); 417} 418 419sub no_upload ($) { 420 my ($min) = @_; 421 my $sock = donkey_connect(); 422 return undef unless $sock; 423 my $noup; 424 my $credit; 425 $sock->print('nu '.$min."\n"); 426 $sock->flush(); 427 $sock->print("vu\n"); 428 while ($_ = $sock->getline()) { 429 if (/^Upload credits : (\d+) minutes$/) { 430 $credit = $1; 431 } elsif (/^Upload disabled for (\d+)/) { 432 $noup = $1; 433 $sock->close(); 434 } 435 } 436 return [$noup, $credit]; 437} 438 439sub get_client_stats { 440 my %stats; 441 my $sock = donkey_connect(); 442 return \%stats unless $sock; 443 $sock->print("client_stats\n"); 444 my $op; 445 while ($_ = $sock->getline()) { 446 if (/Total seens:/) { 447 $op = "seen"; 448 } elsif (/Total filerequests received:/) { 449 $op = "requests"; 450 } elsif (/Total downloads:/) { 451 $op = "downloads"; 452 } elsif (/Total uploads:/) { 453 $op = "uploads"; 454 } elsif (/Total banneds:/) { 455 $op = "banned"; 456 } elsif (/^ *(.*?): *(\d+) \((\d+\.\d+) %\)/) { 457 $stats{$1}{$op}{num} = $2; 458 $stats{$1}{$op}{percent} = $3; 459 } elsif (/^$/) { 460 $sock->close(); 461 last; 462 } 463 } 464 return \%stats; 465} 466 467sub get_downloads { 468 my %downloads; 469 my $sock = donkey_connect(); 470 return \%downloads unless $sock; 471 $sock->print("vd\n"); 472 my $ready; 473 my $nfiles; 474 my @files; 475 my $sent; 476 while ($_ = $sock->getline()) { 477 my $line = $_; 478 #print $line foreach (1..100); 479 if (/^Downloaded (\d+)\/(\d+) files/) { 480 $nfiles = $1+$2; 481 #} elsif (/^\[(.*?) *(\d+) *?\] +(?:.*?) +([-0-9.]+) +(?:-?\d+) +(?:\d+) +[\d-]+:([\d-]+) +([0-9.-]+|Paused|Queued)/) { 482 } elsif (/^\[(.*?) *(\d+) *?\] +(?:.*?) +([-0-9.]+) +(?:-?\d+) +(?:\d+) +(?:\d+) +[\d-]+:([\d-]+) +\d+\/\d+ +([0-9.-]+|Paused|Queued)/) { 483 #print $_; 484 my $id = $2; 485 $downloads{$id}{percent} = $3; 486 $downloads{$id}{available} = ($4 == 0) ? 1 : 0; 487 $downloads{$id}{rate} = $5; 488 push @files, $id; 489 } elsif (/^ *\[(.*?) *(\d+) *?\] +(.*?) +(\d+) +[0-9A-Z]{32}/) { 490 my $id = $2; 491 $downloads{$id}{net} = $1; 492 $downloads{$id}{percent} = 100; 493 $downloads{$id}{rate} = "Completed"; 494 $downloads{$id}{size} = $4; 495 $downloads{$id}{downloaded} = $4; 496 push @{ $downloads{$id}{names} }, $3; 497 #$sock->print("vd ".$id."\n"); 498 push @files, $id; 499 } elsif (/\[(.*?) *(\d+) *?\] +(.*?) +(\d+) +(\d+)$/) { 500 $downloads{$sent}{net} = $1; 501 $downloads{$sent}{size} = $4; 502 $downloads{$sent}{downloaded} = $5; 503 push @{ $downloads{$sent}{names} }, $3; 504 } elsif (/^ \((.*?)\)$/) { 505 push @{ $downloads{$sent}{names} }, $1; 506 } elsif (/^(\d+) sources:/) { 507 $downloads{$sent}{sources} = $1; 508 $sent = undef; 509 #$downloads{$processing}{onlist} = 0; 510 } elsif (/^Chunks: \[(\d+)\]/ && not $downloads{$sent}{net} eq 'BitTorrent') { 511 foreach (split(//, $1)) { 512 push @{ $downloads{$sent}{chunks} }, $_; 513 #print $processing if $processing eq '3'; 514 } 515 } 516 $ready = 1 if (@files == $nfiles); 517 #} elsif (/^ *(?:.*?) \(last_ok <(?:.*?)> lasttry <(?:.*?)> nexttry <(?:.*?)> onlist (true|false)\)$/) { 518 # $downloads{$processing}{onlist}++ if $1 eq 'true'; 519 #} 520 if (($nfiles == 0) || defined @files && @files == 0 && not defined $sent) { 521 $sock->close(); 522 return \%downloads; 523 } else { 524 if ($ready && not defined $sent) { 525 $sent = pop @files; 526 if (1) { 527 $sock->close(); 528 $sock = donkey_connect(); 529 #$sock->print("id\n"); 530 $sock->print('vd '.$sent."\n"); 531 # What a hack :) FIXME in mldonkey 532 } else { 533 $sent = undef; 534 } 535 } 536 } 537 } 538} 539 540sub transfer_command ($$) { 541 my ($cmd, $transfer) = @_; 542 my $sock = donkey_connect(); 543 return undef unless $sock; 544 my $downloads = get_downloads(); 545 if ($downloads->{$transfer}) { 546 $sock->print($cmd." ".$transfer."\n"); 547 } else { 548 foreach (keys %$downloads) { 549 foreach my $name (@{$downloads->{$_}{names}}) { 550 next unless $name eq $transfer; 551 $sock->print($cmd." ".$_."\n"); 552 } 553 } 554 } 555 $sock->close(); 556} 557 558sub sharereactor_latest { 559 my $ua = LWP::UserAgent->new(env_proxy => 1, 560 keep_alive => 1, 561 timeout => 30); 562 my $response = $ua->get('http://www.sharereactor.com/'); 563 my @releases; 564 foreach (split /\n/, $response->content() ) { 565 if (/^<a href="release\.php\?id=(\d+)">(\d+\.\d+\.\d+) - (.*?)<\/a> <a href="category\.php\?id=\d+">\((.*?)\)<\/a><br>/) { 566 #print "FOO"; 567 my $new = { date => $2, id => $1, title => $3, category => $4 }; 568 push @releases, $new; 569 } 570 } 571 return \@releases; 572} 573 574sub sharereactor_search ($) { 575 my ($query) = @_; 576 my $enc_query = HTML::Entities::encode($query); 577 my $ua = LWP::UserAgent->new(env_proxy => 1, 578 keep_alive => 1, 579 timeout => 30); 580 my $response = $ua->get('http://www.sharereactor.com/search.php?search='.$enc_query.'&category=0'); 581 return unless $response->is_success(); 582 my @results; 583 foreach (split /\n/, $response->content()) { 584 if (/<a href="release\.php\?id=(\d+)">(.*?)<\/a>/) { 585 push @results, { name => $2, id => $1 }; 586 587 my $ua2 = LWP::UserAgent->new(env_proxy => 1, 588 keep_alive => 1, 589 timeout => 30); 590 my $response2 = $ua2->get('http://www.sharereactor.com/downloadrelease.php?id='.$1); 591 foreach (split /\n/, $response2->content()) { 592 if (/"(ed2k:\/\/\|file\|.*?\|\d+\|.*?\|)";/) { 593 push @{ $results[-1]->{files} }, $1; 594 } 595 } 596 } elsif (/<a href="category\.php\?id=\d+">(.*?)<\/a>/) { 597 $results[-1]->{category} = $1; 598 } 599 } 600 #print $_->{name}." ".$_->{id}."\n" foreach @results; 601 return \@results; 602} 603 604sub bittorrent_search ($) { 605 my ($query) = @_; 606 my $enc_query = HTML::Entities::encode($query); 607 my $ua = LWP::UserAgent->new(env_proxy => 1, 608 keep_alive => 1, 609 timeout => 30); 610 my $response = $ua->get('http://www.bytemonsoon.com/?search='.$enc_query.'&cat=0&incldead=0'); 611 return unless $response->is_success(); 612 my @results; 613 foreach (split /\n/, $response->content()) { 614 if (/^<td><a href="details\.php\?id=(\d+)&hit=1"><b>(.*?)<\/b><\/a><\/td>$/) { 615 push @results, { name => $2, id => $1 }; 616 } elsif (/^<td align="center"><a href="(.*?)">torrent<\/a><\/td>$/) { 617 $results[-1]->{torrent} = "http://www.bytemonsoon.com/$1"; 618 } 619 } 620 return \@results 621} 622 623sub get_ovstats { 624 my $sock = donkey_connect(); 625 return unless $sock; 626 $sock->print("ovstats\n"); 627 my $result; 628 $result->{nodes} = []; 629 while ($_ = $sock->getline()) { 630 if (/^ +(\d+\.\d+\.\d+.\d+:\d+)$/) { 631 push @{ $result->{nodes} }, $1; 632 } elsif (/^ Search hits: (\d+)$/) { 633 $result->{search_hits} = $1; 634 } elsif (/^ Source hits: (\d+)$/) { 635 $result->{source_hits} = $1; 636 } elsif (/^$/) { 637 last; 638 } 639 } 640 return $result; 641} 642 643sub is_fake ($) { 644 my ($hash) = @_; 645 my $ua = LWP::UserAgent->new(env_proxy => 1, 646 keep_alive => 1, 647 timeout => 30); 648 my $url = 'http://edonkeyfakes.ath.cx/fakecheck/update/fakecheck.php'; 649 my %form = ( hash => $hash ); 650 my $response = $ua->post($url, \%form); 651 return unless $response->is_success(); 652 return not ($response->content() =~ /Your query didn't match anything in our fakedatabase\!/); 653} 654 655sub get_settings ($) { 656 my ($regexp) = @_; 657 my $sock = donkey_connect(); 658 return unless $sock; 659 $sock->print("voo\n"); 660 my $result = {}; 661 while ($_ = $sock->getline()) { 662 if (/^(.*?) = (.*?)$/) { 663 my ($key, $val) = ($1, $2); 664 #print "<".$regexp.">"; 665 next unless ($key =~ /$regexp/i); 666 $result->{$key} = $val; 667 } else { 668 $sock->close(); 669 return $result; 670 } 671 } 672} 673 674sub reshare { 675 my $sock = donkey_connect(); 676 return 0 unless $sock; 677 $sock->print("reshare\n"); 678 $sock->close(); 679 return 1; 680} 681 682sub close_fds { 683 my $sock = donkey_connect(); 684 return 0 unless $sock; 685 $sock->print("close_fds\n"); 686 $sock->close(); 687 return 1; 688} 689 690sub change_setting ($$) { 691 my ($key, $val) = @_; 692 my $result; 693 $result->{key} = $key; 694 $result->{old} = get_settings($key)->{$key}; 695 my $sock = donkey_connect(); 696 return unless $sock; 697 $sock->print("set ".$key." ".$val."\n"); 698 $sock->close(); 699 #$result->{new} = $val; 700 $result->{new} = get_settings($key)->{$key}; 701 return $result; 702} 703 704sub get_servers ($) { 705 my ($all) = @_; 706 my $sock = donkey_connect(); 707 return unless $sock; 708 if ($all) { 709 $sock->print("vma\n"); 710 } else { 711 $sock->print("vm\n"); 712 } 713 my $result; 714 while ($_ = $sock->getline()) { 715 #if (/^\[(.*?) (\d+) *\] ([0-9.]+):(\d+) + (.*?) + (\d+) +(\d+) (Connected)?$/) { 716 if (/^\[(.*?) (\d+) *\] (.+):(\d+) + (.*?) + (\d+) +(\d+) (Connected)?$/) { 717 my $server = { net => $1, 718 id => $2, 719 ip => $3, 720 port => $4, 721 comment => $5, 722 users => $6, 723 files => $7 724 }; 725 $result->{$2} = $server; 726 } elsif (/^ *$/) { 727 $sock->close(); 728 return $result; 729 } 730 } 731} 732 733sub search_file ($) { 734 my ($query) = @_; 735 my $sock = donkey_connect(); 736 return unless $sock; 737 $sock->print("s ".$query."\n"); 738 while ($_ = $sock->getline()) { 739 if (/Query \d+ Sent to \d+/) { 740 $sock->close; 741 return 1; 742 } elsif (/exception/) { 743 $sock->close; 744 return 0; 745 } 746 } 747} 748 749sub list_downloads ($) { 750 my ($data) = @_; 751 my $text = downloads2text($data, '/.*/'); #downloads_list($data, '/.*/'); 752 print CLIENTCRAP &draw_box('iDonkey', $text, 'Downloads', 1); 753} 754 755sub get_best_name ($) { 756 my ($names) = @_; 757 my $result; 758 foreach (@$names) { 759 # It's a hash 760 $result = $_; 761 last unless /[A-Z0-9]{32}/; 762 } 763 return $result; 764} 765 766sub downloads2text ($$) { 767 my ($downloads, $regexp) = @_; 768 my $length = Irssi::settings_get_int('idonkey_max_filename_length'); 769 my $text; 770 my @table; 771 my @chunks; 772 my @names; 773 my ($speed, $downloaded, $size) = (0,0,0); 774 foreach (sort {get_best_name($downloads->{$a}{names}) cmp get_best_name($downloads->{$b}{names})} keys %$downloads) { 775 my @line; 776 my $filename = get_best_name($downloads->{$_}{names}); 777 my $name = shorten_filename($filename, $length); 778 $name =~ s/%/%%/g; 779 my $download; 780 # Color codes: 781 # Yellow Paused 782 # Bold green Completed 783 # Green Downloading & 100% available 784 # Blue Downloading, but not completly on network 785 if ($downloads->{$_}{rate} =~ /Paused|Queued/) { 786 $download .= '%yo%n'; 787 } elsif ($downloads->{$_}{rate} eq 'Completed') { 788 $download .= '%Go%n'; 789 } else { 790 if ($downloads->{$_}{available}) { 791 $download .= '%go%n'; 792 } else { 793 $download .= '%bo%n'; 794 } 795 $speed += $downloads->{$_}{rate}; 796 } 797 $size += $downloads->{$_}{size}; 798 $downloaded += $downloads->{$_}{downloaded}; 799 $download .= ' %9'.$name.'%9 ('.$_.')'; 800 push @names, $download; 801 #$text .= "\n" if 1; 802 push @line, round($downloads->{$_}{downloaded}, $downloads->{$_}{size}); 803 #$text .= ' '.round($downloads->{$_}{downloaded}, $downloads->{$_}{size})."/"; 804 push @line, round($downloads->{$_}{size},$downloads->{$_}{size}); 805 push @line, '('.$downloads->{$_}{percent}.'%%)'; 806 #$text .= round($downloads->{$_}{size},$downloads->{$_}{size})." (".$downloads->{$_}{percent}."%%)"; 807 if ($downloads->{$_}{rate} =~ /^[0-9.]+$/) { 808 push @line, $downloads->{$_}{rate}." kb/s"; 809 } elsif ($downloads->{$_}{rate} eq '-') { 810 push @line, "0 kb/s"; 811 } else { 812 push @line, $downloads->{$_}{rate}; 813 } 814 #push @line, ' ['.$downloads->{$_}{sources}.'/'.$downloads->{$_}{onlist}.' @'.$downloads->{$_}{net}.']' if (defined $downloads->{$_}{sources}); 815 my $netload = '['; 816 $netload .= $downloads->{$_}{sources}."@"; 817 $netload .= $downloads->{$_}{net}.']'; 818 push @line, $netload; 819 push @line, .$downloads->{$_}{tag}; 820 #$text .= "\n"; 821 if (1 || $downloads->{$_}{chunks}) { 822 if (ref $downloads->{$_}{chunks} && @{$downloads->{$_}{chunks}} > 1) { 823 my $chunk; 824 $chunk .= '['; 825 foreach (@{$downloads->{$_}{chunks}}) { 826 if ($_ > 1) { 827 $chunk .= '%g|%n'; 828 } elsif ($_ == 1) { 829 $chunk .= '%b:%n'; 830 } else { 831 $chunk .= '%r.%n'; 832 } 833 } 834 $chunk .= "]"; 835 push @chunks, $chunk; 836 } else { 837 push @chunks, ""; 838 } 839 } 840 push @table, \@line; 841 } 842 foreach (split /\n/, array2table(@table)) { 843 $text .= (shift @names)."\n"; 844 $text .= " ".$_."\n"; 845 if (Irssi::settings_get_bool('idonkey_show_chunks')) { 846 my $chunk = shift @chunks; 847 $text .= " ".$chunk."\n" if $chunk; 848 } 849 } 850 my $percent = $size > 0 ? ($downloaded / $size)*100 : 0; 851 $percent = $1 if ($percent =~ /(\d+\.\d{1}).*?/); 852 if (keys %$downloads > 1) { 853 $text .= "".'%9Total:%9 '; 854 $text .= round($downloaded, $size).'/'; 855 $text .= round($size, $size); 856 $text .= ' ('.$percent.'%%), '.$speed.' kb/s'; 857 } 858 return $text; 859} 860 861sub round ($$) { 862 return $_[0] unless Irssi::settings_get_bool('idonkey_round_filesize'); 863 if ($_[1] > 100000) { 864 return sprintf "%.2fMB", $_[0]/1024/1024; 865 } else { 866 return sprintf "%.2fKB", $_[0]/1024; 867 } 868} 869 870sub get_queries { 871 my $sock = donkey_connect(); 872 # FIXME A real parser here? 873 return undef unless $sock; 874 $sock->print("vs\n"); 875 my %result; 876 my $num; 877 while ($_ = readline($sock)) { 878 chop; 879 if (/^Searching (\d+) queries$/) { 880 $num = $1; 881 } elsif (/^\[(\d+) *\](.*) .*?$/) { 882 my $id = $1; 883 my $regexp = $2; 884 my @token = $regexp =~ /CONTAINS\[(.*?)\]/g; 885 $result{$id} = \@token; 886 $num--; 887 } 888 last if (defined $num && $num == 0); 889 } 890 return \%result; 891} 892 893sub get_results ($) { 894 my ($filter) = @_; 895 my $sock = donkey_connect(); 896 my $net = '.*'; 897 if ($filter =~ /-net (.*?)(?: |$)/) { 898 $net = $1; 899 } 900 #my $regexp = '.*'; 901 my @filters; 902 while ($filter =~ /(\!?)\/(.*?)\//g) { 903 my %entry = ( "reverse" => $1 ? 1 : 0, 904 "regexp" => $2 905 ); 906 push @filters, \%entry; 907 } 908 my $result; 909 my @results; 910 my $waiting = 0; 911 my $filtered = 0; 912 return undef unless $sock; 913 my $num = 0; 914 $sock->print("vr\n"); 915 my @token; 916 while ($_ = readline($sock)) { 917 chop; 918 if (/^Result of search (\d+)$/) { 919 my $searches = get_queries(); 920 @token = @{ $searches->{$1} } if ref $searches->{$1}; 921 } elsif (/^(\d+) results \((?:done|(-?\d+) waiting)\)$/) { 922 $num = $1; 923 $waiting = $2 if $2; 924 unless ($num) { 925 $sock->close(); 926 last(); 927 } 928 } elsif (/^\[ *(\d+)\] (.*?(?: Napster)?) (.*)/) { 929 # FIXME Find a better Solution for open Napster 930 my %data = ( id=> $1, filename => $3, visible => 1, net => $2); 931 932 $data{visible} = 1 unless @filters; 933 foreach my $entry (@filters) { 934 next unless $data{visible}; 935 my $regexp = $entry->{regexp}; 936 my $reverse = $entry->{reverse}; 937 if (not $reverse) { 938 $data{visible} = 0 if not ($data{filename} =~ /$regexp/i); 939 } else { 940 $data{visible} = 0 if ($data{filename} =~ /$regexp/i); 941 } 942 } 943 if (Irssi::settings_get_bool('idonkey_filter_search_results') && @token) { 944 foreach (@token) { 945 $data{visible} = 0 unless $data{filename} =~ /$_/i; 946 last unless $data{visible}; 947 } 948 $data{visible} = 0 unless $data{net} =~ /$net/i; 949 } 950 if (Irssi::settings_get_bool('idonkey_filter_nameless_results')) { 951 $data{visible} = 0 unless $data{filename}; 952 } 953 push @results, \%data; 954 } elsif (/^ ALREADY DOWNLOADED$/) { 955 $results[-1]->{downloaded} = 1; 956 } elsif (/^ +(-?\d+) ([0-9A-Z]{32}) (\d+)?/) { 957 $results[-1]->{size} = $1; 958 $results[-1]->{hash} = $2; 959 $results[-1]->{sources} = $3; 960 unless ($results[-1]->{visible}) { 961 pop @results; 962 $filtered++; 963 } else { 964 #$results[-1]->{fake} = is_fake($results[-1]->{hash}); 965 } 966 } elsif (/No search to print/ || /^$/ || /^exception/) { 967 $sock->close(); 968 last(); 969 } 970 } 971 my $sortby = Irssi::settings_get_str('idonkey_sort_results_by'); 972 @results = sort {uc($a->{$sortby}) <=> uc($b->{$sortby})} @results; 973 $result->{results} = \@results; 974 $result->{waiting} = $waiting; 975 $result->{filtered} = $filtered; 976 return $result; 977} 978 979sub list_servers ($) { 980 my ($data) = @_; 981 my @text; 982 foreach (sort { $data->{$a}{id} <=> $data->{$b}{id} } keys %$data) { 983 push @text, ["%9".$data->{$_}{id}."%9", $data->{$_}{net}, $data->{$_}{ip}.':'.$data->{$_}{port}, $data->{$_}{users}, $data->{$_}{files}, $data->{$_}{comment}]; 984 } 985 unshift @text, ["%9ID%9", "%9net%9", "%9address%9", "%9users%9", "%9files%9", "%9comment%9"] if @text; 986 print CLIENTCRAP &draw_box('iDonkey', array2table(@text), 'servers', 1); 987} 988 989sub list_results ($) { 990 my ($data) = @_; 991 my $results = $data->{results}; 992 my @text; 993 $seen = $nresults; 994 my $length = Irssi::settings_get_int('idonkey_max_filename_length'); 995 foreach (@$results) { 996 my @line; 997 next unless $_->{visible}; 998 my $file = shorten_filename($_->{filename}, $length); 999 $file =~ s/%/%%/g; 1000 push @line, '%9'.$_->{id}.'%9'; 1001 push @line, '%9'.$file.'%9'; 1002 push @line, $_->{fake} ? '%RF%n' : ''; 1003 push @line, $_->{downloaded} ? '%GD%n' : ''; 1004 push @line, $_->{net} if defined $_->{net}; 1005 push @line, '['.$_->{sources}.']'; 1006 push @line, round($_->{size}, $_->{size}); 1007 1008 push @text, \@line; 1009 } 1010 my $footer = 'Results'; 1011 $footer .= ' ('.$data->{filtered}.' filtered)' if $data->{filtered} > 0; 1012 $footer .= ' ('.$data->{waiting}.' waiting)' if $data->{waiting} > 0; 1013 print CLIENTCRAP &draw_box('iDonkey', array2table(@text), $footer, 1); 1014} 1015 1016sub get_file ($$) { 1017 my ($file, $force) = @_; 1018 my $sock = donkey_connect(); 1019 return unless $sock; 1020 $sock->print("d ".$file."\n"); 1021 while ($_ = $sock->getline()) { 1022 if (/download started/) { 1023 $sock->close(); 1024 return 1; 1025 } elsif (/(File already downloaded|could not start download)/) { 1026 if ($force) { 1027 $sock->print("force_download\n"); 1028 $sock->close(); 1029 return 1 1030 } else { 1031 $sock->close(); 1032 return 0; 1033 } 1034 } 1035 } 1036} 1037 1038sub download_link ($$) { 1039 my ($url, $force) = @_; 1040 my $sock = donkey_connect(); 1041 return unless $sock; 1042 $sock->print("dllink ".$url."\n"); 1043 $sock->print("force_download\n") if $force; 1044 while ($_ = $sock->getline()) { 1045 if (/download (started|forced)|Done/) { 1046 $sock->close(); 1047 return 1; 1048 } elsif (/Unable|bad syntax|exception/ && not $force) { 1049 $sock->close(); 1050 return 0; 1051 } 1052 } 1053} 1054 1055sub connect_servers ($$) { 1056 my ($ids, $disconnect) = @_; 1057 my $sock = donkey_connect(); 1058 return unless $sock; 1059 $sock->print("c\n") unless (@$ids); 1060 foreach (@$ids) { 1061 if ($disconnect) { 1062 $sock->print("x ".$_."\n"); 1063 } else { 1064 $sock->print("c ".$_."\n"); 1065 } 1066 } 1067 $sock->close(); 1068} 1069 1070sub quit_donkey { 1071 my $sock = donkey_connect(); 1072 return unless $sock; 1073 $sock->print("kill\n"); 1074 $sock->close(); 1075 return 1; 1076} 1077 1078sub commit_downloads { 1079 my $sock = donkey_connect(); 1080 return unless $sock; 1081 $sock->print("commit\n"); 1082 $sock->close(); 1083 until ($_ = $sock->getline()) { 1084 #if (/commited/) { 1085 return 1; 1086 #} else { 1087 # return 0; 1088 #} 1089 } 1090} 1091 1092sub get_status_info { 1093 my $result; 1094 $result->{downloads} = get_downloads(); 1095 $result->{waiting} = get_results('/.*/')->{waiting}; 1096 my $upload = no_upload(0); 1097 $result->{credit} = $upload->[1]; 1098 $result->{noupload} = $upload->[0]; 1099 return $result; 1100} 1101 1102sub cmd_idonkey ($$$) { 1103 my ($args, $server, $witem) = @_; 1104 my @arg = split(/ /, $args); 1105 if (@arg == 0 || $arg[0] eq 'downloads') { 1106 #list_downloads(); 1107 bg_do('downloads'); 1108 } elsif ($arg[0] eq 'help') { 1109 show_help(); 1110 } elsif ($arg[0] =~ /pause|resume|cancel/ && defined $arg[1]) { 1111 bg_do(join(' ', @arg)); 1112 } elsif ($arg[0] =~ /results/) { 1113 bg_do(join(' ', @arg)); 1114 } elsif ($arg[0] eq 'search') { 1115 shift @arg; 1116 if (search_file(join(' ', @arg))) { 1117 $seen = 0; 1118 $nresults = 0; 1119 print CLIENTCRAP "%R>>%n Query '".join(' ', @arg)."' sent to network..."; 1120 } 1121 } elsif ($arg[0] eq 'get' && defined $arg[1]) { 1122 shift @arg; 1123 my $force = 0; 1124 foreach my $id (@arg) { 1125 if ($id eq 'force') { 1126 $force = 1; 1127 } elsif (get_file($id, $force)) { 1128 print CLIENTCRAP "%R>>%n Download of file ".$id." started"; 1129 } else { 1130 print CLIENTCRAP "%R>>%n Download of file ".$id." failed"; 1131 } 1132 } 1133 } elsif ($arg[0] eq 'dllink' && defined $arg[1]) { 1134 shift @arg; 1135 join(' ', @arg) =~ /(force )?(.*)/; 1136 my $force = defined $1 ? 1 : 0; 1137 if (download_link($2, $1)) { 1138 print CLIENTCRAP "%R>>%n Download of ".$2." started"; 1139 } else { 1140 print CLIENTCRAP "%R>>%n Download of ".$2." failed"; 1141 } 1142 } elsif ($arg[0] eq 'commit') { 1143 if (commit_downloads()) { 1144 print CLIENTCRAP "%R>>%n Completed downloads saved"; 1145 } else { 1146 print CLIENTCRAP "%R>>%n Saving completed downloads failed"; 1147 } 1148 } elsif ($arg[0] eq 'launch') { 1149 my $cmd = Irssi::settings_get_str('idonkey_mldonkey_cmd'); 1150 system($cmd); 1151 print CLIENTCRAP "%R>>%n MLDonkey launched"; 1152 } elsif ($arg[0] eq 'quit') { 1153 if ( quit_donkey() ) { 1154 print CLIENTCRAP "%R>>%n MLDonkey killed"; 1155 } else { 1156 print CLIENTCRAP "%R>>%n Unable to kill MLDonkey"; 1157 } 1158 } elsif ($arg[0] eq 'servers') { 1159 shift @arg; 1160 if ( (not @arg) || ($arg[0] eq 'connected') ) { 1161 bg_do('servers'); 1162 } elsif ($arg[0] eq 'disconnect') { 1163 shift @arg; 1164 connect_servers(\@arg, 1); 1165 } elsif ($arg[0] eq 'connect') { 1166 shift @arg; 1167 connect_servers(\@arg, 0); 1168 } elsif ($arg[0] eq 'all') { 1169 bg_do('allservers'); 1170 } 1171 } elsif ($arg[0] eq 'overnet') { 1172 shift @arg; 1173 if ( (not @arg) || ($arg[0] eq 'stats') ) { 1174 bg_do('ovstats'); 1175 } elsif ($arg[0] eq 'nodes') { 1176 bg_do('ovnodes'); 1177 } 1178 } elsif ($arg[0] eq 'settings') { 1179 shift @arg; 1180 if ( (not @arg) ) { 1181 # Do something 1182 } elsif ($arg[0] eq 'show') { 1183 shift @arg; 1184 bg_do('settings '.join(' ',@arg)); 1185 } elsif ($arg[0] eq 'change') { 1186 shift @arg; 1187 return unless (defined $arg[0] && defined $arg[1]); 1188 my $key = shift @arg; 1189 my $val = join(' ', @arg); 1190 bg_do('set '.$key.' '.$val) 1191 } 1192 } elsif ($arg[0] eq 'shares') { 1193 shift @arg; 1194 if ( (not @arg) ) { 1195 ## list shares? 1196 } elsif ($arg[0] eq 'reshare') { 1197 bg_do('reshare'); 1198 } elsif ($arg[0] eq 'close') { 1199 bg_do('close_fds'); 1200 } 1201 } elsif ($arg[0] eq 'sharereactor') { 1202 shift @arg; 1203 if ( (not @arg) || $arg[0] eq 'latest') { 1204 print CLIENTCRAP "%B>>%n Retrieving latest releases..."; 1205 bg_do('sr-latest'); 1206 } elsif ($arg[0] eq 'search') { 1207 shift @arg; 1208 bg_do('sr-search '.join(" ", @arg)); 1209 print CLIENTCRAP "%B>>%n Searching ShareReactor for '".join(" ", @arg)."'"; 1210 } elsif ($arg[0] eq 'download' && defined $arg[1]) { 1211 shift @arg; 1212 download_sr(join(" ", @arg)); 1213 } 1214 } elsif ($arg[0] eq 'bittorrent') { 1215 shift @arg; 1216 if ($arg[0] eq 'search') { 1217 shift @arg; 1218 bg_do('bt-search '.join(" ", @arg)); 1219 print CLIENTCRAP "%B>>%n Searching BitTorrent for '".join(" ", @arg)."'"; 1220 } 1221 } elsif ($arg[0] eq 'noupload') { 1222 shift @arg; 1223 if (@arg && $arg[0] =~ /-?\d+/) { 1224 bg_do('noupload '.$arg[0]); 1225 } 1226 } elsif ($arg[0] eq 'client-stats') { 1227 bg_do('client-stats'); 1228 } elsif ($arg[0] eq 'forget') { 1229 bg_do('forget'); 1230 } elsif ($arg[0] eq 'fake' && defined $arg[1]) { 1231 shift @arg; 1232 foreach (@arg) { 1233 next unless /\d+/; 1234 bg_do('fake '.$_); 1235 } 1236 } 1237} 1238 1239sub download_sr ($) { 1240 my ($download) = @_; 1241 if (defined $edlinks{$download}) { 1242 foreach my $link (@{ $edlinks{$download} }) { 1243 if (download_link($link,0)) { 1244 print CLIENTCRAP "%R>>%n Download of ".$link." started"; 1245 } else { 1246 print CLIENTCRAP "%R>>%n Download of ".$link." failed"; 1247 } 1248 } 1249 } else { 1250 print CLIENTCRAP "%B>>%n Unknown release, try searching for it."; 1251 } 1252} 1253 1254 1255sub shorten_filename ($$) { 1256 my ($file, $length) = @_; 1257 unless ($length == 0) { 1258 my $post = 4; 1259 my $pre = $length-5-$post; 1260 $file =~ s/^(.{$pre}).*(.{$post})/$1\[\.\.\.\]$2/; 1261 } 1262 return $file; 1263} 1264 1265sub filename_percent ($$) { 1266 my ($name, $percent) = @_; 1267 my $length = length($name); 1268 my $done = $length * ($percent/100); 1269 my $string = '%g%U'.substr($name, 0, $done).'%U%n%y'.substr($name, $done, $length).'%n'; 1270 return $string; 1271} 1272 1273sub sb_idonkey ($$) { 1274 my ($item, $get_size_only) = @_; 1275 my $line; 1276 $line .= $nresults."|" if ($seen != $nresults && defined $seen); 1277 $line .= '%F'.$expected."%F|" if $expected > 0; 1278 $line .= $noul."min|" if $noul > 0; 1279 #my $length = Irssi::settings_get_int('idonkey_max_filename_length'); 1280 my $length = Irssi::settings_get_int('idonkey_statusbar_max_filename_length'); 1281 my $i = 0; 1282 foreach (sort keys %downloads) { 1283 $index = 0 if $index > (scalar keys %downloads)-1; 1284 unless ($i == $index) { 1285 $i++; 1286 next; 1287 } 1288 unless (Irssi::settings_get_bool('idonkey_statusbar_show_paused')) { 1289 if ($downloads{$_}{rate} eq 'Paused') { 1290 $index++; 1291 next; 1292 } 1293 } 1294 my $filename = get_best_name($downloads{$_}{names}); 1295 my $file = shorten_filename($filename, $length); 1296 $line .= filename_percent($file, $downloads{$_}{percent}); 1297 $line .= ' '.$downloads{$_}{percent}.'%% '; 1298 unless ($downloads{$_}{rate} eq '-') { 1299 $line .= $downloads{$_}{rate}; 1300 $line .= ' kb/s' if $downloads{$_}{rate} =~ /^[0-9.]+$/; 1301 } 1302 $line .= ' '; 1303 $i++; 1304 } 1305 $line =~ s/ $//; 1306 my $format = "{sb ".$line."}"; 1307 $item->{min_size} = $item->{max_size} = length($line); 1308 $item->default_handler($get_size_only, $format, 0, 1); 1309} 1310 1311sub call_for_status { 1312 bg_do('status'); 1313} 1314 1315sub sig_complete_word ($$$$$) { 1316 my ($list, $window, $word, $linestart, $want_space) = @_; 1317 if ($linestart =~ /^.idonkey (pause|resume|cancel)/) { 1318 foreach (sort {get_best_name($downloads{$a}{names}) cmp get_best_name($downloads{$b}{names})} keys %downloads) { 1319 my $name = get_best_name($downloads{$_}{names}); 1320 if ( ($1 eq 'resume' && $downloads{$_}{rate} eq 'Paused') || 1321 ($1 eq 'pause' && not $downloads{$_}{rate} eq 'Paused') || 1322 ($1 eq 'cancel') ) { 1323 push @$list, $name if $name =~ /^(\Q$word\E.*)?$/i; 1324 } 1325 } 1326 Irssi::signal_stop(); 1327 } elsif ($linestart =~ /^.idonkey search/) { 1328 my @opts = ('minsize', 'maxsize', 'media', 'Video', 'Audio', 'format', 'title', 'album', 'artist', 'field', 'not', 'and', 'or'); 1329 foreach (@opts) { 1330 $_ = '-'.$_; 1331 push @$list, $_ if /^(\Q$word\E.*)?$/i; 1332 } 1333 Irssi::signal_stop(); 1334 } elsif ($linestart =~ /^.idonkey sharereactor download/) { 1335 foreach (sort keys %edlinks) { 1336 push @$list, $_ if /^(\Q$word\E.*)?$/i; 1337 } 1338 Irssi::signal_stop(); 1339 } elsif ($linestart =~ /^.idonkey dllink/) { 1340 foreach (sort keys %edlinks) { 1341 foreach my $link (@{ $edlinks{$_} }) { 1342 push @$list, $link if $link =~ /^(\Q$word\E.*)?$/i; 1343 } 1344 } 1345 Irssi::signal_stop(); 1346 } elsif ($linestart =~ /^.idonkey results/) { 1347 my @opts = ('net'); 1348 foreach (@opts) { 1349 $_ = '-'.$_; 1350 push @$list, $_ if /^(\Q$word\E.*)?$/i; 1351 } 1352 } 1353} 1354 1355sub next_status { 1356 $index++; 1357 $index = 0 if $index > (scalar keys %downloads)-1; 1358 Irssi::statusbar_items_redraw('idonkey'); 1359} 1360 1361sub install_timer { 1362 return if defined $timer; 1363 my $timeout = Irssi::settings_get_int('idonkey_statusbar_interval'); 1364 my $timeout2 = Irssi::settings_get_int('idonkey_update_interval'); 1365 return unless $timeout && $timeout2; 1366 $timer = Irssi::timeout_add($timeout*1000, \&next_status, undef); 1367 $timer2 = Irssi::timeout_add($timeout2*1000, \&call_for_status, undef); 1368} 1369 1370sub uninstall_timer { 1371 return unless defined $timer; 1372 Irssi::timeout_remove($timer); 1373 Irssi::timeout_remove($timer2); 1374 $timer = undef; 1375} 1376 1377Irssi::command_bind('idonkey', \&cmd_idonkey); 1378foreach my $cmd ('downloads', 'pause', 'resume', 'results', 'search', 'get', 'get force', 'cancel', 'help', 'commit', 'dllink', 'dllink force', 'launch', 'quit', 'servers', 'servers disconnect', 'servers connected', 'servers all', 'servers connect', 'overnet', 'overnet stats', 'overnet nodes', 'settings', 'settings show', 'settings change', 'shares', 'shares reshare', 'shares close', 'sharereactor', 'sharereactor search', 'sharereactor download', 'sharereactor latest', 'noupload', 'client-stats', 'forget', 'fake', 'bittorrent', 'bittorrent search') { 1379 Irssi::command_bind('idonkey '.$cmd => sub { 1380 cmd_idonkey("$cmd ".$_[0], $_[1], $_[2]); }); 1381} 1382Irssi::signal_add_first('complete word', \&sig_complete_word); 1383 1384Irssi::settings_add_str($IRSSI{name}, 'idonkey_password', ''); 1385Irssi::settings_add_str($IRSSI{name}, 'idonkey_host', 'localhost'); 1386Irssi::settings_add_int($IRSSI{name}, 'idonkey_port', 4000); 1387Irssi::settings_add_int($IRSSI{name}, 'idonkey_max_filename_length', 65); 1388# sources, filename, id, size 1389Irssi::settings_add_str($IRSSI{name}, 'idonkey_sort_results_by', "id"); 1390Irssi::settings_add_bool($IRSSI{name}, 'idonkey_round_filesize', 1); 1391 1392 1393Irssi::settings_add_bool($IRSSI{name}, 'idonkey_filter_nameless_results', 1); 1394Irssi::settings_add_bool($IRSSI{name}, 'idonkey_filter_search_results', 0); 1395Irssi::settings_add_bool($IRSSI{name}, 'idonkey_show_chunks', 1); 1396 1397Irssi::settings_add_str($IRSSI{name}, 'idonkey_mldonkey_cmd', 'screen mldonkey'); 1398Irssi::settings_add_int($IRSSI{name}, 'idonkey_statusbar_interval', 0); 1399Irssi::settings_add_bool($IRSSI{name}, 'idonkey_statusbar_show_paused', 1); 1400Irssi::settings_add_int($IRSSI{name}, 'idonkey_update_interval', 0); 1401Irssi::settings_add_bool($IRSSI{name}, 'idonkey_update_results', 1); 1402Irssi::settings_add_int($IRSSI{name}, 'idonkey_statusbar_max_filename_length', 25); 1403 1404Irssi::statusbar_item_register('idonkey', 0, "sb_idonkey"); 1405 1406install_timer(); 1407 1408print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded, /idonkey help'; 1409