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+)&amp;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